Kafka Java API 之Producer原始碼解析
原帖地址:http://blog.csdn.net/xeseo/article/details/18315451
從我的OneNote copy過來,格式似乎有點問題,懶得整了。將就著看吧,各位。
Kafka提供了Producer類作為java producer的api,該類有sync和async兩種傳送方式。
預設是sync方式,即producer的呼叫類在訊息真正傳送到佇列中去以後才返回,其工作原理如下:
new Producer
當我們new了一個kafka java api提供的Producer類時,其底層,實際會產生兩個核心類的例項:Producer(Scala的,不是java api提供的那個)、DefaultEventHandler。在建立的同時,會預設new一個ProducerPool,即我們每new一個java的Producer類,就會有一個scala的Producer、EventHandler和ProducerPool!
producer.send()
當我們呼叫java的producer.send方法時,底層呼叫scala的Producer的send方法,其內部其實調的是eventhandler.handle(message)方法。
- eventHandler會首先序列化該訊息,
eventHandler.serialize(events)
- 然後會根據傳入的broker資訊、topic資訊,去取最新的該topic的metadata資訊
BrokerPartitionInfo.updateInfo
| -> ClientUtils.fetchTopicMetadata //建立一個topicMetadataRequest,並隨機的選取傳入的broker資訊中任何一個去取metadata,直到取到為止
| -> val producer: SyncProducer = ProducerPool.createSyncProducer(producerConfig, shuffledBrokers(i)) //對隨機選到的broker會建立一個SyncProducer
| -> SyncProducer.send //傳送topicMetadataRequest到該broker去取metadata,獲得該topic所對應的所有的broker資訊
看到這裡也就明白了為什麼kafka的api裡面並不要求你提供完整的整個kafka叢集的broker資訊,而是任選一個或幾個。因為在這裡它會去你提供的broker取該topic的最新的所有的broker資訊。
這裡要注意的是,用於傳送topicMetadataRequest的SyncProducer雖然是用ProducerPool.createSyncProducer方法建出來的,但用完並不還回pool,而是直接Close,所以會發現有INFO log打出來
[INFO] <main> Connected toxxx.xxx.xxx.xxx:9092 for producing
[INFO] <main> Disconnecting fromxxx.xxx.xxx.xxx:9092
注意:
這個重新整理metadata並不僅在第一次初始化時做。為了能適應kafka broker執行中因為各種原因掛掉、paritition改變等變化,eventHandler會定期的再去重新整理一次該metadata,重新整理的間隔用引數topic.metadata.refresh.interval.ms定義,預設值是10分鐘。
這裡有三點需要強調:
- 不呼叫send, 不會建立socket,不會去定期重新整理metadata
- 在每次取metadata時,kafka會單獨開一個socket去取metadata,開完再關掉。
- 根據取得的最新的完整的metadata,重新整理Pool中到broker的連線(第一次建立時,pool裡面是空的)
- 每10分鐘的重新整理會直接重新把到每個broker的socket連線重建,意味著在這之後的第一個請求會有幾百毫秒的延遲。如果不想要該延遲,把topic.metadata.refresh.interval.ms值改為-1,這樣只有在傳送失敗時,才會重新重新整理。Kafka的叢集中如果某個partition所在的broker掛了,可以檢查錯誤後重啟重新加入叢集,手動做rebalance,producer的連線會再次斷掉,直到rebalance完成,那麼重新整理後取到的連線著中就會有這個新加入的broker。
在ClientUtils.fetchTopicMetadata呼叫完成後,回到BrokerPartitionInfo.updateInfo繼續執行,在其末尾,pool會根據上面取得的最新的metadata建立所有的SyncProducer,即Socket通道producerPool.updateProducer(topicsMetadata)
注意:
在ProducerPool中,SyncProducer的數目是由該topic的partition數目控制的,即每一個SyncProducer對應一個broker,內部封了一個到該broker的socket連線。
每次重新整理時,會把已存在SyncProducer給close掉,即關閉socket連線,然後新建SyncProducer,即新建socket連線,去覆蓋老的。
如果不存在,則直接建立新的。
- 然後,才是真正傳送資料
dispatchSerializedData(outstandingProduceRequests)
- 如果傳送失敗,會進行重試。重試時,又會重新整理metadata,而kafka的leader選舉需要一定的時間,所以這次重新整理可能需要等待,最大等待時間由引數retry.backoff.ms(預設為100)定義。
重試最大次數由引數message.send.max.retries定義預設為3
async方式通過引數producer.type控制,例子:
Properties p = new Properties();
props.put("producer.type", "async");
ProducerConfig config = new ProducerConfig(props);
producer = new Producer<String, byte[]>(config);
async方式與sync方式的不同在於,在初始化scala的producer時,會建立一個ProducerSendThread物件。然後,在呼叫send時,它並不是直接呼叫eventHandler.handle方法,而是把訊息放入一個長度由queue.buffering.max.messages引數定義的佇列(預設10000),當佇列滿足以下兩種條件時,會由ProducerSendThread觸發eventHandler.handle方法,把佇列中的訊息作為一個batch傳送
- 時間超過queue.buffering.max.ms定義的值,預設5000ms
- 佇列中當前訊息個數超過batch.num.messages定義的值,預設200
結論:
1. Kafka提供的java api中的Producer,底層只是維護該topic到每個broker的連線,並不是一個傳統意義上的連線池。在使用sync方式時,我們應該自己實現一個連線池,裡面包含若干Producer物件,以實現最大化寫入效率。我自己寫了一個簡單的:https://github.com/EdisonXu/simple-kafka-producer-pool
2. 在寫入的資料頻率不高或要求獲得寫入結果時,應使用sync方式,否則會因async的等待時間引入額外的延遲
3. 在寫入的資料頻率很高時,應使用async方式,以batch的形式寫入,獲得最大效率
相關文章
- Kafka之Producer原始碼Kafka原始碼
- 原始碼分析Kafka之Producer原始碼Kafka
- apache kafka原始碼分析-Producer分析ApacheKafka原始碼
- Kafka 之 async producer (2) kafka.producer.async.DefaultEventHandlerKafka
- Kafka 之 async producer (1)Kafka
- Kafka學習(四)-------- Kafka核心之ProducerKafka
- 我花了一週讀了Kafka Producer的原始碼Kafka原始碼
- 詳解Kafka ProducerKafka
- Java集合之Hashtable原始碼解析Java原始碼
- Java集合之ArrayList原始碼解析Java原始碼
- Java集合之HashMap原始碼解析JavaHashMap原始碼
- java之原始碼路徑及apiJava原始碼API
- Java集合框架之 Java HashMap 原始碼解析Java框架HashMap原始碼
- alpakka-kafka(1)-producerKafka
- Kafka Producer ConsumerKafka
- Java集合(6)之 HashMap 原始碼解析JavaHashMap原始碼
- Java集合之LinkedList原始碼解析Java原始碼
- Flink kafka source & sink 原始碼解析Kafka原始碼
- JDK原始碼解析之Java SPI機制JDK原始碼Java
- Java併發之Semaphore原始碼解析(一)Java原始碼
- Java併發之Semaphore原始碼解析(二)Java原始碼
- Kafka java api-生產者程式碼KafkaJavaAPI
- java基礎型別原始碼解析之HashMapJava型別原始碼HashMap
- Java併發之ReentrantLock原始碼解析(一)JavaReentrantLock原始碼
- Java併發之ReentrantLock原始碼解析(二)JavaReentrantLock原始碼
- Java併發之ReentrantLock原始碼解析(三)JavaReentrantLock原始碼
- Java併發之ReentrantLock原始碼解析(四)JavaReentrantLock原始碼
- Java併發之ThreadPoolExecutor原始碼解析(二)Javathread原始碼
- Java併發之ThreadPoolExecutor原始碼解析(三)Javathread原始碼
- Java——HashMap原始碼解析JavaHashMap原始碼
- Java——ArrayList原始碼解析Java原始碼
- Java WeakHashMap 原始碼解析JavaHashMap原始碼
- Java TreeMap 原始碼解析Java原始碼
- kafka原始碼剖析(二)之kafka-server的啟動Kafka原始碼Server
- RocketMQ中Producer的啟動原始碼分析MQ原始碼
- Java Timer原始碼解析(定時器原始碼解析)Java原始碼定時器
- Kafka程式碼解析Kafka
- 插曲:Kafka原始碼預熱篇--- Java NIOKafka原始碼Java