kafka之broker


不同broker之間的關系

  Kafka使用zookeeper來維護集群成員的信息。每個broker都有一個唯一標識符,這個標識符可以在配置文件中指定,也可以自動生成。在broker啟動時,它通過建立臨時節點把自己的ID注冊到zookeeper。kafka組件訂閱broker在zookeeper上的注冊路徑,當有broker進入或退出集群時,這些組件就可以獲得通知。

  在broker停機、出現網絡分區或長時間垃圾回收停頓時,broker會從zookeeper上斷開連接,此時broker在啟動時創建的臨時節點會自動從zookeeper上移除。監聽broker列表的kafka組件會被告知該broker已移除。

  在關閉broker時,它對應的節點也會消息,不過它的iD會繼續存在於其他數據結構中。例如,主題的副本列表里就可能包含這些iD。在完全關閉一個broker之后,如果使用相同的ID啟動另一個全新的broker,它會立即加入集群,並擁有與舊broker相同的分區和主題。

控制器

  控制器其實也是一個broker,只不過它除了具有一般broker的功能之外,還負責分區首領的選舉。集群里第一個啟動的broker通過在zookeeper里創建一個臨時節點讓自己成為控制器。其他的broker在控制器節點上創建zookeeper watch對象,這樣他們就可以收到這個節點的變更通知。

  如果控制器被關閉或者與zookeeper斷開連接,zookeeper上的臨時節點就會消失。集群里的其他broker通過watch對象得到控制器節點消失的通知,他們嘗試讓自己成為新的控制器。第一個在zookeeper里成功創建控制器節點的broker成為新的控制器,其他的broker會在新的控制器節點上創建新的watch對象。每個新選出的控制器通過zookeeper的條件遞增操作獲得一個全新的數值更大的controller epoch。同時,控制器使用epoch來避免“腦裂”。腦裂是指兩個節點同時認為自己是當前的控制器。

  當控制器發現一個broker已經離開集群(通過觀察相關的zookeeper路徑),它就知道,那些失去首領的分區需要一個新首領(這些分區的首領剛好是在這個broker上)。控制器遍歷這些分區,並確定誰應該成為新首領,然后向所有包含新首領或者現有跟隨者的broker發送請求。該請求中包含了誰是新首領以及誰是分區跟隨者的信息。

  當控制器發現一個broker加入集群時,它會使用brokerID來檢查新加入的broker是否包含現在分區的副本。如果有,控制器就把變更通知發送給新加入的broker和其他broker,新broker上的副本開始從首領那里復制消息。

復制

  kafka使用主題來組織數據,每個主題被分為如干個分區,每個分區有多個副本。那些副本被保存在broker上,每個broker可以保存成百上千個屬於不同主題和分區的副本。

  副本類型

  • 首領副本:每個分區都有一個首領副本。為了保證一致性,所有生產者和消費者請求都會經過這個副本。
  • 跟隨者副本:首領以外的副本都是跟隨者副本。跟隨者副本不處理來自客戶端的請求,它們唯一的任務就是從首領那里復制消息,保持與首領一致的狀態。如果首領發生崩潰,其中一個跟隨者會被提升為新首領。

  首領的另一個任務是搞清楚哪個跟隨者的狀態和自己是一致的。為了與首領保持一致,跟隨者向首領發送獲取數據的請求,這種請求和消費者為了讀取消息而發送的請求是一樣的。首領將響應消息發送給跟隨者。請求消息里包含了跟隨者想要獲取消息的偏移量,而且這些偏移量總是有序的。例如,一個跟隨者副本先請求消息1,在請求消息2,然后請求消息3,在收到這三個請求的響應前,它是不會發送第4個請求消息的。如果跟隨者發送了第4個消息,首領就知道它已經收到了前三個響應。通過查看每個跟隨者的請求的最新偏移量,首領就會知道每個跟隨者的復制進度。

  副本有可以分為同步副本和非同步副本。

同步副本需要滿足的條件

  • 與zookeeper之間有一個活躍的會話,也就是說,它在過去的6s(可配置)內向zookeeper發送過心跳。
  • 再過去10s內(可配置replica.lag.time.max.ms)從首領哪里獲取過消息。
  • 再過去的10s內從首領那里獲取過最新的消息。光從受涼那里獲取消息是不夠的,他還必須是幾乎零延遲的。

  如果跟隨者副本不能滿足以上任何一點,那么它就會被認為是不同步的。跟隨者的正常不活躍時間或者稱為不同步副本之前的時間是通過replica.lag.time.max.ms參數來設置的。這個時間間隔直接影響着首領選舉期間的客戶端行為和數據保留機制。一個不同步副本通過和zookeeper重新建立連接,並從首領那里獲取最新消息,可以重新變成同步的。

  一個滯后的同步副本會導致生產者和消費者變慢,因為在消息被認為已提交之前,客戶端會等待所有的同步副本接收到消息。當然是否需要所有同步副本接收到消息才算提交,是可以通過生產者acks配置的,當然這個參數的配置需要我們在性能和一致性之間做出選擇。

  我們知道一般在首領失效時,只有同步副本才有可能被選為最新副本。不同步副本是不能被選舉為首領的,畢竟它沒有包含全部消息。但是,如果我們將參數unclean.leader.election.enable設為true,就是允許不同步副本成為首領(也就是不完全的選舉)。一般我們不提倡開啟不完全選舉,但是對於一些可用性要求比較高,比如實時點擊流分析系統,一般會啟用不完全的首領選舉。

  根據kafka對可靠性數據保證的定義,消息只有在被寫入到所有的同步副本之后才被認為是已提交的。但如果這里的“所有同步副本”只包含一個,那么在這個副本變為不可用的時,數據就會丟失。這時我們最好確保數據被寫入不止一個副本,這就是需要把最小同步副本數量設置的大些。這樣能保證在首領失效時,有其他的同步副本能被選為首領,但是如果同步副本數量小於我們設置的最小同步副本,那么broker就會停止接受生產者的請求。最小同步副本是通過min.insync.replicas設置的,在主題級別和broker級別都有這個參數,根據需要去設置。

 

  除了當前首領外,每個分區都有一個首選首領(分區的副本清單中第一個副本一般就是首選首領)---創建主題時選定的首領就是分區的首選首領。之所以把它叫做首選首領,是因為在創建分區時,需要在broker之間均衡首領。因此,我們希望首選首領成為真正的首領。默認情況下,auto.leader.rebalance.enable為true,它會檢查首選首領是不是當前首領,如果不是,並且該副本是同步副本的,就會觸發首領選舉,讓首選首領成為當前首領。

  

broker處理請求

   kafka提供一個二進制協議,制定了請求消息的格式以及broker如何對請求作出響應。客戶端發起連接和發送請求,broker按照請求到達的順序來處理他們,這種順序保證了kafka具有消息隊列的特性,同時保證保存的消息也是有序的。

  那么broker是如何處理請求的呢?

  broker會在它所監聽的每個端口上運行一個Acceptor線程,這個線程會創建一個連接,並把它交給Processor線程去處理。Processor線程(網絡線程)的數量是可配置的。網絡線程負責從客戶端獲取請求消息,把它們放進請求隊列,請求消息放入請求隊列后,IO線程會處理它們,並將處理結果放入響應隊列,然后網絡線程從響應隊列中獲取響應消息,把他們發送給客戶端。

  生產請求和獲取請求都必須發送給分區的首領副本。如果broker收到一個針對特定分區的請求,而該分區的首領在另一個broker上,那么發送請求的客戶端會收到一個“非分區首領”的錯誤響應。同樣獲取請求時也會有同樣的要求。kafka客戶端要自己負責把生產請求和獲取請求發送給正確的broker上。

  那么客戶端怎么知道該往哪里發送請求呢?客戶端使用了另一種請求類型,也就是元數據請求。這些請求中包含了客戶端感興趣的主題列表,以及這些主題包含的分區,每個分區都有哪些副本,以及哪個副本是首領。元數據請求可以發送給任何一個broker,因為所有broker都緩存了這些消息。

  一般情況下,客戶端會把這些信息緩存起來,並直接往目標broker上發送生產請求和獲取請求。客戶端需要時不時地通過發送元數據請求來刷新這些信息(刷新頻率可以通過metadata.max.age.ms配置)。如果客戶端收到“非首領”錯誤,客戶端在嘗試重發請求之前先刷新元數據,因為這個錯誤說明了客戶端正在使用過期的元數據信息。

  客戶端從broker上獲取消息時,客戶端可以指定broker最多可以從一個分區里返回多少數據。這個限制非常重要的,因為客戶端需要為broker返回的數據分配足夠的內存。如果請求的偏移量存在,broker將按照客戶端指定的數據上限從分區里返回讀取消息,再把消息返回給客戶端。kafka使用零復制技術向客戶端發送消息---kafka直接把消息從文件里發送到網絡通道,而不需要經過任何的緩沖區。客戶端除了可以設置broker返回數據的上限,還可以設置下限。

分區分配

kafka的基本存儲單元就是分區。分區無法在多個broker間進行再細分,也無法在同一個broker的多個磁盤上進行細分。

對於分區分配的理解,我們可以用一個實際場景來解釋。例如,假設我們有6個broker,打算創建一個包含10個分區的主題,復制系數3,那么kafka就會有30個分區副本。在進行分區分配時,我們要達到如下的目標:

  • 在broker間均勻地分布分區副本。
  • 確保每個分區的每個副本分布在不同的broker上。
  • 如果為broker制定了機架信息,那么盡可能把每個分區的副本分配到不同的機架的broker上。

為了實現這個目標,我們先隨機選擇一個broker(假設是4),然后使用輪詢的方式給每個broker分配首領。於是,首領分區0會在broker4上,首領分區1在broker5上,首領分區2在broker0上,以此類推。然后從分區首領開始,依次分配其跟隨者副本。如果分區0的首領在broker4上,那么它的第一個跟隨者在broker5上,第二個跟隨者副本在broke0上。其他的分區也是同樣的道理。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM