kafka9重複消費問題解決

龍騰四海365發表於2017-06-07

kafka9重複消費問題解決


背景:之前用的kafka客戶端版本是0.8,近期升級了kafka客戶端的版本,寫了新的消費者和生產者的程式碼,在本地測試沒有問題,可以正常消費與生產。但最近的專案中使用了新版的程式碼,當資料量較大時會出現重複消費的問題。現將問題的排除與解決過程記錄下來,避免再次踩坑。


問題發現:由於ConsumerRecord物件可以獲取到當前訊息的分割槽與偏移量,故在log日誌中將當前訊息的分割槽與偏移量也記錄下來了。在監控日誌的過程中,發現某一個分割槽的偏移量會在多個執行緒中出現,而且偏移量的值還不一樣,因此覺得可能是重複消費,當查詢程式消費的總記錄數和kafka中的訊息記錄數相差甚多。


解決過程:上網搜尋如何解決kafka重複消費的問題,都是在說kafka在session時間內未提交offset,故參考網上思路,將consumer的poll的時間改成100ms,即100ms從kafka上poll一次資料,並且設定props.put("auto.commit.interval.ms", "1000"); props.put("session.timeout.ms", "30000");kafka自動提交的時間間隔和session時間。

改完後測試,發現當kafka的資料量較大時,還是會有重複消費問題,然後將poll的資料 條數列印出來發現資料條數較多,而且一段時間後(一般30s)會報一個錯誤:

org.apache.kafka.clients.consumer.CommitFailedException:
 Commit cannot be completed since the group has already rebalanced and assigned the partitions to another member. 
This means that the time between subsequent calls to poll() was longer than the configured session.timeout.ms, which typically implies that the poll loop is spending too much time message processing. 
You can address this either by increasing the session timeout or by reducing the maximum size of batches returned in poll() with max.poll.records. [com.bonc.framework.server.kafka.consumer.ConsumerLoop]

大概意思是:由於組已經重新平衡並將分割槽分配給另一個成員,因此無法完成提交。 這意味著在呼叫poll()的後續呼叫之間的時間比配置的session.timeout.ms長,這通常意味著poll迴圈花費太多時間訊息處理。 您可以通過增加會話超時或通過使用max.poll.records減少poll()中返回的批次的最大大小來解決此問題。

於是設定如下引數:

//一次從kafka中poll出來的資料條數
        //max.poll.records條資料需要在在session.timeout.ms這個時間內處理完
        props.put("max.poll.records","100");

這個數值的大小需要和session.timeout.ms的時長做評估,即100條資料在session.timeout.ms時間內是否能處理完?

注:

props.put("session.timeout.ms", "30000");
        
//訊息傳送的最長等待時間.需大於session.timeout.ms這個時間
 props.put("request.timeout.ms", "40000");

還需要 注意的是,fetch.min.bytes這個引數配置,從kafka拉取的資料的大小,這個引數最好設定一下,不然的話可能出問題。建議設定為:

//server傳送到消費端的最小資料,若是不滿足這個數值則會等待直到滿足指定大小。預設為1表示立即接收。
props.put("fetch.min.bytes", "1");

總結:

一般情況下,kafka重複消費都是由於未正常提交offset,故修改配置,正常提交offset即可解決。上文中提到的主要配置如下所示:

/* 自動確認offset的時間間隔  */
props.put("auto.commit.interval.ms", "1000");

props.put("session.timeout.ms", "30000");

//訊息傳送的最長等待時間.需大於session.timeout.ms這個時間
props.put("request.timeout.ms", "40000");

//一次從kafka中poll出來的資料條數
//max.poll.records條資料需要在在session.timeout.ms這個時間內處理完
props.put("max.poll.records","100");

//server傳送到消費端的最小資料,若是不滿足這個數值則會等待直到滿足指定大小。預設為1表示立即接收。
props.put("fetch.min.bytes", "1");
//若是不滿足fetch.min.bytes時,等待消費端請求的最長等待時間
props.put("fetch.wait.max.ms", "1000");





相關文章