public KafkaSpout(SpoutConfig spoutConf) { _spoutConfig = spoutConf;
}
基於0.93版本的Storm
SpoutConfig繼承自KafkaConfig。由於SpoutConfig和KafkaConfig所有的instance field全是public, 因此在使用構造方法後,可以直接設定各個域的值。
public class SpoutConfig extends KafkaConfig implements Serializable { public List<String> zkServers = null; //記錄Spout讀取進度所用的zookeeper的host public Integer zkPort = null;//記錄進度用的zookeeper的埠 public String zkRoot = null;//進度資訊記錄於zookeeper的哪個路徑下 public String id = null;//進度記錄的id,想要一個新的Spout讀取之前的記錄,應把它的id設為跟之前的一樣。 public long stateUpdateIntervalMs = 2000;//多久往Zookeeper記錄一次進度。 public SpoutConfig(BrokerHosts hosts, String topic, String zkRoot, String id) { super(hosts, topic); this.zkRoot = zkRoot; this.id = id; } }
public class KafkaConfig implements Serializable { public final BrokerHosts hosts; //用以獲取Kafka broker和partition的資訊 public final String topic;//從哪個topic讀取訊息 public final String clientId; // SimpleConsumer所用的client id public int fetchSizeBytes = 1024 * 1024; //發給Kafka的每個FetchRequest中,用此指定想要的response中總的訊息的大小 public int socketTimeoutMs = 10000;//與Kafka broker的連線的socket超時時間 public int fetchMaxWait = 10000; //當伺服器沒有新訊息時,消費者會等待這些時間 public int bufferSizeBytes = 1024 * 1024;//SimpleConsumer所使用的SocketChannel的讀緩衝區大小 public MultiScheme scheme = new RawMultiScheme();//從Kafka中取出的byte[],該如何反序列化 public boolean forceFromStart = false;//是否強制從Kafka中offset最小的開始讀起 public long startOffsetTime = kafka.api.OffsetRequest.EarliestTime();//從何時的offset時間開始讀,預設為最舊的offset public long maxOffsetBehind = Long.MAX_VALUE;//KafkaSpout讀取的進度與目標進度相差多少,相差太多,Spout會丟棄中間的訊息
public boolean useStartOffsetTimeIfOffsetOutOfRange = true;//如果所請求的offset對應的訊息在Kafka中不存在,是否使用startOffsetTime
public int metricsTimeBucketSizeInSecs = 60;//多長時間統計一次metrics
public KafkaConfig(BrokerHosts hosts, String topic) {
this(hosts, topic, kafka.api.OffsetRequest.DefaultClientId());
}
public KafkaConfig(BrokerHosts hosts, String topic, String clientId) {
this.hosts = hosts;
this.topic = topic;
this.clientId = clientId;
}
}
對Zookeeper的使用
KafkaSpout的配置中有兩個地方可以用到Zookeeper
- 用Zookeeper來記錄KafkaSpout的處理進度,在topology重新提交或者task重啟後繼續之前的處理進度。在SpoutConfig中的zkServers, zkPort和zkRoot與此相關。如果zkServer和zkPort沒有設定,那麼KafkaSpout會使用Storm叢集所用的Zookeeper記錄這些資訊。
- 用Zookeeper來獲取Kafka中一個topic的所有partition,和每個partition的leader。這需要實現BrokerHosts的子類ZkHosts.但是,這個Zookeepr是可選的。如果使用BrokerHosts的另一個子類StaticHosts,把partition和leader的對應關係硬編碼,則不需要Zookeeper來提供此功能。KafkaSpout會從Kafka叢集使用的Zookeeper中提取partition和leader的對應關係。而且:
- 如果使用StatisHosts,那麼KafkaSpout會使用StaticCoordinator,這個coordinator不能響應partition leader的變化。
- 如果使用ZkHosts,那麼KafkaSpout會使用ZkCoordinator, 當其refresh()方法被呼叫後,這個cooridnator會檢查發生leader變更的partition,併為之生成新的PartitionManager.從而能夠在leader變更後,繼續讀取訊息。
影響初始讀取進度的配置項
在一個topology上線後,它從哪個offset開始讀取訊息呢?有一些配置項對此有影響:
- SpoutConfig中的id欄位。如果想要一個topology從另一個topology之前的處理進度繼續處理,它們需要有相同的id。
- KafkaConfig的forceFromStart欄位。如果此欄位設為true, 那麼它一個topology上線後,它會忽略之前相同id的topology的進度,並且從Kafka中最早的訊息開始處理。
- KafkaConfig的startOffsetTime欄位。預設為kafka.api.OffsetRequest.EarliestTime()開始讀,也就是從Kafka中最早的訊息開始處理。也可以設成kafka.api.OffsetRequest.LatestOffset,也就是最早的訊息開始讀。也可以自己指定具體的值。
- KafkaConfig的maxOffsetBehind欄位。這個欄位對於KafkaSpout的多個處理流程都有影響。當提交一個新topology時,如果沒有forceFromStart, 當KafkaSpout對某個partition的處理進度落後startOffsetTime對應的offset多於此值時,KafkaSpout會丟棄中間的訊息,從而強制趕上目標進度.比如,如果startOffsetTime設成了lastestTime,那麼如果進度落後超過maxOffsetBehind,KafkaSpout會直接從latestTime對應的offset開始處理。如果設成了froceFromStart,則在提交新任務時,始終會從EarliestTime開始讀。
- KafkaSpout的userStartOffsetTimeIfOffsetOutOfRange欄位。如果設成true,那麼當fetch訊息時出錯,且FetchResponse顯示的出錯原因是OFFSET_OUT_OF_RANGE,那麼就會嘗試從KafkaSpout指定的startOffsetTime對應的訊息開始讀。例如,如果有一批訊息因為超過了儲存期限被Kafka刪除,並且zk裡記錄的訊息在這批被刪除的訊息裡。如果KafkaSpout試圖從zk的記錄繼續讀,那麼就會出現OFFSET_OUT_OF_RANGE的錯誤,從而觸發這個配置。
實際上maxOffsetBehind有時候有點名不符實。當startOffsetTime為A, zk裡的進度為B, A - B > maxOffsetBehind時,應該從A - maxOffsetBehind除開始讀或許更好一些,而不是直接跳到startOffsetTime。此處的邏輯參見PartitionManager的實現。
附:其中KafkaConfig的maxWait的意義請參見這篇文章 《卡夫卡的煉獄》
實際上,KafkaSpout的一些行為可能會比較詭異,特別是與maxOffsetBehind有關的部分。這些行為由PartitionManager決定,參見對PartitionManager的分析這篇文章。