kafka系列之(3)——Coordinator與offset管理和Consumer Rebalance
1、Consumer與Consumer Group
consumer group是kafka提供的可擴充套件且具有容錯性的消費者機制。組內可以有多個消費者或消費者例項(consumer instance),它們共享一個公共的ID,即group ID。組內的所有消費者協調在一起來消費訂閱主題(subscribed topics)的所有分割槽(partition)。
consumer group下可以有一個或多個consumer instance,consumer instance可以是一個程式,也可以是一個執行緒
group.id是一個字串,唯一標識一個consumer group
consumer group下訂閱的topic下的每個分割槽只能分配給某個group下的一個consumer(當然該分割槽還可以被分配給其他group)
2、Coordinator介紹
Coordinator一般指的是執行在broker上的group Coordinator,用於管理Consumer Group中各個成員,每個KafkaServer都有一個GroupCoordinator例項,管理多個消費者組,主要用於offset位移管理和Consumer Rebalance。
Coordinator儲存的資訊
對於每個Consumer Group,Coordinator會儲存以下資訊:
- 對每個存在的topic,可以有多個消費組group訂閱同一個topic(對應訊息系統中的廣播)
- 對每個Consumer Group,後設資料如下:
訂閱的topics列表
Consumer Group配置資訊,包括session timeout等
組中每個Consumer的後設資料。包括主機名,consumer id
每個正在消費的topic partition的當前offsets
Partition的ownership後設資料,包括consumer消費的partitions對映關係
如何確定consumer group的coordinator
consumer group如何確定自己的coordinator是誰呢? 簡單來說分為兩步:
- 確定consumer group位移資訊寫入__consumers_offsets這個topic的哪個分割槽。具體計算公式:
__consumers_offsets partition# = Math.abs(groupId.hashCode() % groupMetadataTopicPartitionCount) 注意:groupMetadataTopicPartitionCount由offsets.topic.num.partitions指定,預設是50個分割槽。 - 該分割槽leader所在的broker就是被選定的coordinator
Coordinator工作過程
3、offset位移管理
消費者在消費的過程中需要記錄自己消費了多少資料,即消費位置資訊。在Kafka中這個位置資訊有個專門的術語:位移(offset)。
(1)、很多訊息引擎都把這部分資訊儲存在伺服器端(broker端)。這樣做的好處當然是實現簡單,但會有三個主要的問題:
1. broker從此變成有狀態的,會影響伸縮性;
2. 需要引入應答機制(acknowledgement)來確認消費成功。
3. 由於要儲存很多consumer的offset資訊,必然引入複雜的資料結構,造成資源浪費。
而Kafka選擇了不同的方式:每個consumer group管理自己的位移資訊,那麼只需要簡單的一個整數表示位置就夠了;同時可以引入checkpoint機制定期持久化,簡化了應答機制的實現。
(2)、Kafka預設是定期幫你自動提交位移的(enable.auto.commit = true),你當然可以選擇手動提交位移實現自己控制。
(3)、另外kafka會定期把group消費情況儲存起來,做成一個offset map,如下圖所示:
上圖中表明瞭test-group這個組當前的消費情況。
offset管理
老版本的位移是提交到zookeeper中的,目錄結構是:/consumers/<group.id>/offsets/<topic>/<partitionId>,但是zookeeper其實並不適合進行大批量的讀寫操作,尤其是寫操作。
因此kafka提供了另一種解決方案:增加__consumeroffsets topic,將offset資訊寫入這個topic,擺脫對zookeeper的依賴(指儲存offset這件事情)。__consumer_offsets中的訊息儲存了每個consumer group某一時刻提交的offset資訊。依然以上圖中的consumer group為例,格式大概如下:
__consumers_offsets topic配置了compact策略,使得它總是能夠儲存最新的位移資訊,既控制了該topic總體的日誌容量,也能實現儲存最新offset的目的。compact的具體原理請參見:Log Compaction
至於每個group儲存到__consumers_offsets的哪個分割槽,如何檢視的問題請參見這篇文章:Kafka 如何讀取offset topic內容 (__consumer_offsets)
位移管理的過程
offset提交訊息會根據消費組的key(消費組名稱)進行分割槽. 對於一個給定的消費組,它的所有訊息都會傳送到唯一的broker(即Coordinator)
Coordinator上負責管理offset的元件是Offset manager。負責儲存,抓取,和維護消費者的offsets. 每個broker都有一個offset manager例項. 有兩種具體的實現:
ZookeeperOffsetManager: 呼叫zookeeper來儲存和接收offset(老版本的位移管理)。
DefaultOffsetManager: 提供消費者offsets內建的offset管理。
通過在config/server.properties中的offset.storage引數選擇。
DefaultOffsetManager
除了將offset作為logs儲存到磁碟上,DefaultOffsetManager維護了一張能快速服務於offset抓取請求的consumer offsets表。這個表作為快取,包含的含僅僅是”offsets topic”的partitions中屬於leader partition對應的條目(儲存的是offset)。
對於DefaultOffsetManager還有兩個其他屬性: “offsets.topic.replication.factor和”offsets.topic.num.partitions”,預設值都是1。這兩個屬性會用來自動地建立”offsets topic”。
offset manager介面的概要:
Offset Commit提交過程:
消費端
一條offset提交訊息會作為生產請求.當消費者啟動時,會為”offsets topic”建立一個消費者. 下面是內建的生產者的一些屬性:
可以使用非同步.但是使用同步可以避免延遲的生產請求(因為是批量訊息),並且我們需要立即知道offset訊息是否被broker成功接收
|request.required.acks|-1|確保所有的replicas和leader是同步的,並且能看到所有的offset訊息
|key.serializer.class|StringEncoder|key和payload都是strings
注意我們沒有對提交的offset訊息進行壓縮,因為每條訊息本身大小是很小的,如果壓縮了反而適得其反.
目前key和offset的值通過純文字方式傳遞. 我們可以轉換為更加緊湊的二進位制協議,而不是把
Long型別的offset和Int型別的partition作為字串. 當然在不斷演進時還要考慮版本和格式協議.
broker端
broker把接收到的offset提交資訊當做一個正常的生產請求,對offset請求的處理和正常的生產者請求處理方式是一樣的.
一旦將資料追加到leader的本地日誌中,並且所有的replicas都趕上leader.leader檢查生產請求是”offsets topic”,
(因為broker端的處理邏輯針對offset請求和普通生產請求是一樣的,如果是offset請求,還需要有不同的處理分支)
它就會要求offset manager新增這個offset(對於延遲的生產請求,更新操作會在延遲的生產請求被完成的時候).
因為設定了acks=-1,只有當這些offsets成功地複製到ISR中的所有brokers,才會被提交給offset manager.
Offset Fetch獲取過程:
消費端
消費者啟動時,會首先建立到任意一個存活的brokers的通道.因此消費者會傳送它所有”OffsetFetchRequest”
到這個隨機選中的broker. 如果出現錯誤,這個通道就會被關閉,並重新建立一個隨機的通道.
broker端
一個Offset抓取請求包含了多個topic-partitions. 接收請求的broker可能有也可能沒有請求的partitions的offset資訊.
因此接收請求的brokers也會和其他broker通訊. 一個通道連線池會用來轉發請求給partition的leader broker.
下面是一個broker在接收到一個offset抓取請求後的步驟:
接收請求的broker首先決定”offset topic”的哪個partition負責這個請求
從broker的leader cache中找出對應partition的leader(會在controller的每次metadata更新請求中更新快取)
如果接收請求的broker就是leader,它會從自己的offset manager中讀取出offset,並新增到響應中
如果offset不存在,返回UnknownTopicOrPartitionCode
如果broker正在載入offsets table,返回OffsetLoadingCode.消費者受到這個狀態碼會在之後重試
如果接收請求的broker不是指定topic-partition的leader,它會將OffsetFetchRequest轉發給這個partition的當前leader
如果”offsets topic”這個時候不存在,它會嘗試自動建立,在建立成果後,會返回offset=-1
3、Consumer Reblance
什麼是rebalance?
rebalance本質上是一種協議,規定了一個consumer group下的所有consumer如何達成一致來分配訂閱topic的每個分割槽。比如某個group下有20個consumer,它訂閱了一個具有100個分割槽的topic。正常情況下,Kafka平均會為每個consumer分配5個分割槽。這個分配的過程就叫rebalance。Kafka新版本consumer預設提供了兩種分配策略:range和round-robin。
rebalance的觸發條件有三種:
組成員發生變更(新consumer加入組、已有consumer主動離開組或已有consumer崩潰了——這兩者的區別後面會談到)
訂閱主題數發生變更——這當然是可能的,如果你使用了正規表示式的方式進行訂閱,那麼新建匹配正規表示式的topic就會觸發rebalance
訂閱主題的分割槽數發生變更
Rebalance Generation
JVM GC的分代收集就是這個詞(嚴格來說是generational),我這裡把它翻譯成“屆”好了,它表示了rebalance之後的一屆成員,主要是用於保護consumer group,隔離無效offset提交的。比如上一屆的consumer成員是無法提交位移到新一屆的consumer group中。我們有時候可以看到ILLEGAL_GENERATION的錯誤,就是kafka在抱怨這件事情。每次group進行rebalance之後,generation號都會加1,表示group進入到了一個新的版本,如下圖所示: Generation 1時group有3個成員,隨後成員2退出組,coordinator觸發rebalance,consumer group進入Generation 2,之後成員4加入,再次觸發rebalance,group進入Generation 3.
協議(protocol)
rebalance本質上是一組協議。group與coordinator共同使用它來完成group的rebalance。目前kafka提供了5個協議來處理與consumer group coordination相關的問題:
Heartbeat請求:consumer需要定期給coordinator傳送心跳來表明自己還活著
LeaveGroup請求:主動告訴coordinator我要離開consumer group
SyncGroup請求:group leader把分配方案告訴組內所有成員
JoinGroup請求:成員請求加入組
DescribeGroup請求:顯示組的所有資訊,包括成員資訊,協議名稱,分配方案,訂閱資訊等。通常該請求是給管理員使用
Coordinator在rebalance的時候主要用到了前面4種請求。
liveness
consumer如何向coordinator證明自己還活著? 通過定時向coordinator傳送Heartbeat請求。如果超過了設定的超時時間,那麼coordinator就認為這個consumer已經掛了。一旦coordinator認為某個consumer掛了,那麼它就會開啟新一輪rebalance,並且在當前其他consumer的心跳response中新增“REBALANCE_IN_PROGRESS”,告訴其他consumer:不好意思各位,你們重新申請加入組吧!
Rebalance過程
rebalance的前提是coordinator已經確定了。
總體而言,rebalance分為2步:Join和Sync
1 Join, 顧名思義就是加入組。這一步中,所有成員都向coordinator傳送JoinGroup請求,請求入組。一旦所有成員都傳送了JoinGroup請求,coordinator會從中選擇一個consumer擔任leader的角色,並把組成員資訊以及訂閱資訊發給leader——注意leader和coordinator不是一個概念。leader負責消費分配方案的制定。
2 Sync,這一步leader開始分配消費方案,即哪個consumer負責消費哪些topic的哪些partition。一旦完成分配,leader會將這個方案封裝進SyncGroup請求中發給coordinator,非leader也會發SyncGroup請求,只是內容為空。coordinator接收到分配方案之後會把方案塞進SyncGroup的response中發給各個consumer。這樣組內的所有成員就都知道自己應該消費哪些分割槽了。
注意!! consumer group的分割槽分配方案是在客戶端執行的!Kafka將這個權利下放給客戶端主要是因為這樣做可以有更好的靈活性。比如這種機制下我可以實現類似於Hadoop那樣的機架感知(rack-aware)分配方案,即為consumer挑選同一個機架下的分割槽資料,減少網路傳輸的開銷。Kafka預設為你提供了兩種分配策略:range和round-robin。由於這不是本文的重點,這裡就不再詳細展開了,你只需要記住你可以覆蓋consumer的引數:partition.assignment.strategy來實現自己分配策略就好了。
consumer group狀態機
和很多kafka元件一樣,group也做了個狀態機來表明組狀態的流轉。coordinator根據這個狀態機會對consumer group做不同的處理,如下圖所示
簡單說明下圖中的各個狀態:
Dead:組內已經沒有任何成員的最終狀態,組的後設資料也已經被coordinator移除了。這種狀態響應各種請求都是一個response: UNKNOWN_MEMBER_ID
Empty:組內無成員,但是位移資訊還沒有過期。這種狀態只能響應JoinGroup請求
PreparingRebalance:組準備開啟新的rebalance,等待成員加入
AwaitingSync:正在等待leader consumer將分配方案傳給各個成員
Stable:rebalance完成!可以開始消費了
rebalance場景剖析
1 新成員加入組(member join)
2 組成員崩潰(member failure)
組成員崩潰和組成員主動離開是兩個不同的場景。因為在崩潰時成員並不會主動地告知coordinator此事,coordinator有可能需要一個完整的session.timeout週期(心跳週期)才能檢測到這種崩潰,這必然會造成consumer的滯後。可以說離開組是主動地發起rebalance;而崩潰則是被動地發起rebalance。如圖:
3 組成員主動離組(member leave group)
4 提交位移(member commit offset)
refer
http://www.cnblogs.com/huxi2b/p/6223228.html
http://zqhxuyuan.github.io/2016/02/18/Kafka-Consumer-Offset-Manager/
http://www.cnblogs.com/byrhuangqiang/p/6384986.html
相關文章
- Kafka Consumer 的 Rebalance 機制Kafka
- Kafka的coordinatorKafka
- kafka rebalance 機制與Consumer多種消費模式案例應用實戰-kafka 商業環境實戰Kafka模式
- 理解Kafka offsetKafka
- Kafka Offset StorageKafka
- Kafka ConsumerKafka
- Kafka 消費者組 RebalanceKafka
- Kafka Producer ConsumerKafka
- kafka rebalance解決方案 -incremental cooperative協議和static membership功能KafkaREMOOP協議
- alpakka-kafka(2)-consumerKafka
- kafka 0.11檢視offset命令Kafka
- Spark createDirectStream 維護 Kafka offset(Scala)SparkKafka
- spark direct kafka 將offset推到zookeeperSparkKafka
- kafka offset 過期處理策略Kafka
- 聊聊 Kafka Consumer 那點事Kafka
- Spring整合Kafka實現批量消費和手動提交offsetSpringKafka
- Kafka訊息積壓,你監控Rebalance了嗎?Kafka
- 大資料教程系列之Kafka和activemq對比大資料KafkaMQ
- Rocketmq offset進度管理MQ
- 常見的page,client,offset系列client
- Kafka的Consumer負載均衡演算法Kafka負載演算法
- Kafka詳解五、Kafka Consumer的底層API- SimpleConsumerKafkaAPI
- offset與style區別
- Storm系列(六)storm和kafka整合ORMKafka
- 如何確定Kafka的分割槽數、key和consumer執行緒數Kafka執行緒
- Kafka之消費與心跳Kafka
- 詳細解析kafka之kafka分割槽和副本Kafka
- Python3 系列之 環境包管理神器 pipenvPython
- Java Api Consumer 連線啟用Kerberos認證的KafkaJavaAPIROSKafka
- 最佳實踐|從Producer 到 Consumer,如何有效監控 KafkaKafka
- Spark Streaming 之 Kafka 偏移量管理SparkKafka
- terraform-provider-kafka:用於管理Kafka主題和ACLORMIDEKafka
- rebalance的原理
- BOM之三大系列(offset-scroll-client)client
- Predicate和Consumer應用
- 操作符offset 和 jmp指令
- jQuery offset()和position()用法詳解jQuery
- Js offset、scroll、client三大系列的區別JSclient