kafka系列之(3)——Coordinator與offset管理和Consumer Rebalance

weixin_34236497發表於2017-05-11

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)


3951014-c51e9ba63d2caeb5.png
圖片.png

2、Coordinator介紹

Coordinator一般指的是執行在broker上的group Coordinator,用於管理Consumer Group中各個成員,每個KafkaServer都有一個GroupCoordinator例項,管理多個消費者組,主要用於offset位移管理和Consumer Rebalance。

Coordinator儲存的資訊

對於每個Consumer Group,Coordinator會儲存以下資訊:

  1. 對每個存在的topic,可以有多個消費組group訂閱同一個topic(對應訊息系統中的廣播)
  2. 對每個Consumer Group,後設資料如下:
    訂閱的topics列表
    Consumer Group配置資訊,包括session timeout等
    組中每個Consumer的後設資料。包括主機名,consumer id
    每個正在消費的topic partition的當前offsets
    Partition的ownership後設資料,包括consumer消費的partitions對映關係

如何確定consumer group的coordinator

consumer group如何確定自己的coordinator是誰呢? 簡單來說分為兩步:

  1. 確定consumer group位移資訊寫入__consumers_offsets這個topic的哪個分割槽。具體計算公式:
    __consumers_offsets partition# = Math.abs(groupId.hashCode() % groupMetadataTopicPartitionCount) 注意:groupMetadataTopicPartitionCount由offsets.topic.num.partitions指定,預設是50個分割槽。
  2. 該分割槽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,如下圖所示:


3951014-df9031e70a8200a3.png
圖片.png

上圖中表明瞭test-group這個組當前的消費情況。

offset管理

老版本的位移是提交到zookeeper中的,目錄結構是:/consumers/<group.id>/offsets/<topic>/<partitionId>,但是zookeeper其實並不適合進行大批量的讀寫操作,尤其是寫操作。

3951014-961010cefbb97a48.png
圖片.png

因此kafka提供了另一種解決方案:增加__consumeroffsets topic,將offset資訊寫入這個topic,擺脫對zookeeper的依賴(指儲存offset這件事情)。__consumer_offsets中的訊息儲存了每個consumer group某一時刻提交的offset資訊。依然以上圖中的consumer group為例,格式大概如下:
3951014-1802863d21ac1d72.png
圖片.png

3951014-1f61cce8b42b64c2.png
圖片.png
3951014-c5b3d34c4fa62864.png
圖片.png

__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介面的概要:

3951014-92bb57393c590726.png
圖片.png

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.
3951014-bb6025959783fa4e.png
圖片.png

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
3951014-76eda9e045535762.png
圖片.png

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
訂閱主題的分割槽數發生變更

3951014-2405398fd4da4ca5.png
圖片.png

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.
3951014-480e2a4f87716610.png
圖片.png

協議(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負責消費分配方案的制定。
3951014-30b27a129a67480a.png
Paste_Image.png

2 Sync,這一步leader開始分配消費方案,即哪個consumer負責消費哪些topic的哪些partition。一旦完成分配,leader會將這個方案封裝進SyncGroup請求中發給coordinator,非leader也會發SyncGroup請求,只是內容為空。coordinator接收到分配方案之後會把方案塞進SyncGroup的response中發給各個consumer。這樣組內的所有成員就都知道自己應該消費哪些分割槽了。
3951014-63f8fcfbf894e17e.png
Paste_Image.png

注意!! consumer group的分割槽分配方案是在客戶端執行的!Kafka將這個權利下放給客戶端主要是因為這樣做可以有更好的靈活性。比如這種機制下我可以實現類似於Hadoop那樣的機架感知(rack-aware)分配方案,即為consumer挑選同一個機架下的分割槽資料,減少網路傳輸的開銷。Kafka預設為你提供了兩種分配策略:range和round-robin。由於這不是本文的重點,這裡就不再詳細展開了,你只需要記住你可以覆蓋consumer的引數:partition.assignment.strategy來實現自己分配策略就好了。
consumer group狀態機
和很多kafka元件一樣,group也做了個狀態機來表明組狀態的流轉。coordinator根據這個狀態機會對consumer group做不同的處理,如下圖所示
3951014-58928a58ef478cb0.png
Paste_Image.png

簡單說明下圖中的各個狀態:
Dead:組內已經沒有任何成員的最終狀態,組的後設資料也已經被coordinator移除了。這種狀態響應各種請求都是一個response: UNKNOWN_MEMBER_ID
Empty:組內無成員,但是位移資訊還沒有過期。這種狀態只能響應JoinGroup請求
PreparingRebalance:組準備開啟新的rebalance,等待成員加入
AwaitingSync:正在等待leader consumer將分配方案傳給各個成員
Stable:rebalance完成!可以開始消費了
rebalance場景剖析
1 新成員加入組(member join)
3951014-521275e040718fe3.png
Paste_Image.png

2 組成員崩潰(member failure)
組成員崩潰和組成員主動離開是兩個不同的場景。因為在崩潰時成員並不會主動地告知coordinator此事,coordinator有可能需要一個完整的session.timeout週期(心跳週期)才能檢測到這種崩潰,這必然會造成consumer的滯後。可以說離開組是主動地發起rebalance;而崩潰則是被動地發起rebalance。如圖:
3951014-a3965eb26a2f2359.png
Paste_Image.png

3 組成員主動離組(member leave group)
3951014-82ead821d41224e6.png
Paste_Image.png

4 提交位移(member commit offset)

3951014-91ed25175a2b8d97.png
Paste_Image.png

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

相關文章