深刻理解Apache Kafka

文章首發於公衆號:松花皮蛋的黑板報
做者就任於京東,在穩定性保障、敏捷開發、高級JAVA、微服務架構有深刻的理解linux

clipboard.png

1、介紹
Kafka在世界頗負盛名,大部分互聯網公司都在使用它,那麼它究竟是什麼呢?算法

Kafka由LinkedIn公司於2011年推出,自那時起功能逐步迭代,目前演變成一個完整的平臺級產品,它容許您冗餘地存儲巨大的數據量,擁有一個具備巨大吞吐量(數百萬/秒)的消息總線,而且支持實時流任務處理。總的來講,Kafka是一個分佈式,可水平擴展,容錯的日誌提交系統數據庫

這些描述都很是抽象,讓咱們一個接一個地理解它們的意思,隨後深刻探討其工做原理apache

2、分佈式
分佈式系統意味着不一樣機器上的服務實例一塊兒工做成一個總體爲用戶提供完整的服務,Kafka的分佈式體如今存儲、接收信息、發送信息在不一樣的節點上,它帶來的好處是可擴展性和容錯性數組

3、水平可擴展
咱們先給垂直可擴展下一個定義,好比說,你的傳統數據庫服務開始變得超負載,能夠經過簡單地擴充該服務器資源(CPURAMSSD)緩存這個問題,這就叫垂直擴展-單點增長資源,不過有兩大體命的缺點:底層硬件資源有限、須要停機操做。反之,水平擴展經過增長更多的機器部署服務解決相似問題緩存

4、容錯
分佈式系統被設計成可允許必定程序的錯誤,不像單點部署發生異常時總體服務都將不可用,有五個節點的Kafka實例,即便有2個節點宕機了仍能繼續工做服務器

5、commit日誌
一個commit日記相似預寫式日記(WAL)和事務日記,它是可追加的有序的持久化數據,沒法進行修改或者刪除網絡

clipboard.png

這種結構是Kafka的核心,它具有排序功能,而排序則能夠保證肯定性的處理,這二者都是分佈式系統中的重要問題session

Kafka一般會將消息持久化到磁盤上,它充分利用磁盤的有序讀取特性,讀寫的時間複雜度都爲O(1),這是至關了不得的,另外讀取和寫入操做不會相互影響,寫入不會加鎖阻塞讀取操做架構

6、如何工做的
生產者發到消息至Kafka Node節點,存儲在主題Topic中,消費者訂閱主題以接收消息,這是一個生產訂閱模式。爲了使一個節點Topic的數據量不至過大,Kafka引入分區的概念,從而具有更好的性能和伸縮性。Kafka保證分區內的全部消息都按照到達順序排序,區分消息的方式是經過其偏移量offset,你能夠將其理解爲普通數組的下標索引

clipboard.png

Kafka中Broker服務節點是愚蠢的,消費者是聰明的,Kafka不會記錄消費者讀取的操做和刪除消息,相反,數據被存儲一段時間或者達到必定的大小閾值,消費者能夠自由調整偏移量offset以重複獲取他們想要的消息或者捨棄

值得注意的是爲了不進程兩次讀取相同的消息,Kafka引入了消費者組的概念,其中包含一個或者多個消息者實例,約定每一個組只能同時有一個實例消費分區的消息。不過這引來了一個麻煩,連社區也無力解決,也就是Kafka中的重平衡Rebalance問題,它本質是一種協議,規定一個消費者組下的全部消費者實例如何達成一致,來分配訂閱主題的每一個分區,當組成員數發生變動、訂閱主題數發生變動、訂閱主題的分區數發生變動時都會觸發Rebalance,從而達到最公平的分配策略,不過他和GC的STW相似,在Rebalance期間,全部的消費者實例都會中止消費,而後從新分配鏈接。咱們應該儘可能避免這種狀況的發生,儘可能讓消費實例數等於分區數

clipboard.png

7、持久化至磁盤
正如前面說起的,Kafk將消息存儲至磁盤而不是內存RAM,你或許會驚訝它是如何作出這種選擇的,背後應該有許多優化使其可行,沒錯,事實上優化點包括:

一、Kafka的通訊協議支持消息合併,減小網絡流量傳輸,Broker節點一次持續存儲大量數據,消費者能夠一次獲取大量的消息
二、操做系統經過提早讀入(read-ahead)和write-behind緩存技術,使得磁盤上的線性讀寫速度快,現代磁盤速度慢的結論是基於須要磁盤搜索的場景
三、現代操做系統引入頁面緩存(Page cache)技術,頁緩衝由多個磁盤塊構造,在linux讀寫文件時,它用於緩存文件的邏輯內容,從而加塊對磁盤映射和數據的訪問
四、Kafka存儲消息使用的是不可變的標準二進制格式,能夠充分利用零拷貝技術(zero-copy),將數據從頁緩存直接複製到socket通道中

8、數據分佈式和複製
咱們來談談Kafka如何實現容錯以及如何在節點間分配數據

Kafka將分區數據拷貝複製到多個Brokers節點上,避免某個Broker死亡致使數據不可達。每時每刻,一個Broker節點」擁有」一個分區,而且是應用程序從該分區讀取寫入的節點,這稱爲分區leader,它將收到的數據複製到其餘N個Broker節點上,它們稱爲follower,並準備好在leader節點死亡時被選舉爲leader。這種模式使得消息不易丟失,你能夠根據消息的重要程序合理調整replication factor參數,下圖是4個Broker節點,擁有3個複製副本的示例

clipboard.png

你或許會有疑問,生產者或者消費者是如何正確得知分區的leader是哪一個節點的?事實上,Kafka將這些信息保存到Zookeeper服務中

9、Zookeeper服務
Zookeeper是一個分佈式KV對目錄存儲系統,特色是可靠性高、讀取性能高,可是寫入性能差,常被用於存儲元數據和保存集羣狀態,包括心跳、配置等等

Kafka將如下消息保存至Zookeeper中:

一、消費者組的每一個分區的偏移量,不事後來Kafka將其保存至內部主題__consumer_offsets中
二、訪問權限列表
三、生產者和消費者速率限定額度
四、分區leader信息和它們的健康狀態

clipboard.png

10、Controller控制器
一個分佈式系統確定是可協調的,當事件發生時,節點必須以某種方式作出反應,控制器負責決定集羣如何作出反應並指示節點作某事,它是功能不能過於複雜的Broker節點,最主要的職責是負責節點下線和從新加入時重平衡和分配新的分區leader

控制器從ZooKeeper Watch事件中能夠得知某個Broker節點實例下線(或者節點過時,通常發生於Broker長時間繁忙致使心跳異常)的狀況,而後作出反應,決定哪些節點應成爲受影響分區的新leader,而後通知每一個相關的follower經過leaderAndlsr請求開始重新的leader複製數據

clipboard.png

從上面能夠得知,本來做爲分區leader的Broker節點實例重啓後,它將再也不擔任任何分區的leader,消費者也不會從這個節點上讀取消息,這致使了資源的浪費,幸運的是,Kafka有一個被稱爲優先副本(preferred leader replica)的概念-你能夠理解成原先爲該分區leader節點(經過broker id區分)的副本,若是該副本可用,Kafka會將集羣恢復成以前狀態,經過設置auto.leader.rebalance.enabled=true可使得這個過程自動觸發,默認值爲true

Broker節點下線一般都是短暫的,這意味着一段時間後會恢復,這就是爲何當一個節點離開集羣時,與之關聯的元數據不會被刪除,若是它是一個分區的跟隨者,系統也不會爲此分區從新分配新的跟隨者

可是須要注意的是,恢復加入的節點不能當即拿回其上次的leader地位,它尚未資格

11、ISR
副本同步隊列ISR(in-sync replicas),它是由leader維護的,follower從leader同步數據是有延遲的,任意一個超過閾值都會被剔除出ISR列表, 存入OSR(Outof-Sync Replicas)列表中,新加入的follower也會先存放在OSR中

一個follower想被選舉成leader,它必須在ISR隊列中才有資格,不過,在沒有同步副本存在而且已有leader都下線的邊緣狀況下,能夠選擇可用性而不是一致性

ISR列表維護標準以下:

一、它在過去的X秒內有完整同步leader消息,經過replica.lag.time.max.ms配置約定
二、它在過去的X秒內向Zookeeper發送了一個心跳,經過zookeeper.session.timeout.ms配置約定

12、生產者acks設置
明顯,存在一系列意外事件會致使leader下線,假如leader節點接收到生產者的消息,在存儲而且響應ack後節點崩潰了,此時Kafka會從ISR列表中選舉一個新的leader,可是因爲生產者ack配置默認爲1,意思是隻考慮leader接收狀況不考慮follower同步狀況,最終致使部分消息丟失了,因此咱們應該在生產者端設置acks=all,要求每條數據必須是寫入全部副本以後,才能認爲是寫成功,另一層意思是起碼有一個leader和一個follower。不過這種設置影響集羣性能,下降了吞吐量,使得生產者須要在發送下一批消息以前等待更多時間

clipboard.png

十3、水位
經過ack=all約定了leader節點在消息沒有同步到全部的ISR列表前不會有任何返回,另外,節點會跟蹤全部同步副本具備的最大偏移量,也就是高水位偏移量HW(high watermark offset),consumer沒法消費分區下leader副本中偏移量大於分區HW的任何消息。當某個副本成爲leader副本時、broker出現崩潰致使副本被踢出ISR時、producer向leader寫入消息後、leader處理follower fetch請求時,都會嘗試更新分區HW,從而保證了數據一致性和正常消費時不會出現讀取到舊值

clipboard.png

十4、腦裂
想象一下,當正常存活的controller控制器因爲長時間GC-STW致使不可用,而後Zookeeper會認爲/controller節點(節點3)已通過期隨即刪除併發送通知到其餘broker節點,其餘每一個broker節點都嘗試升級爲控制器節點,假設節點2從競爭中勝出成功新的控制器節點並在ZK中建立/controller節點

而後其餘節點接收到通知,瞭解到節點2成爲了新的控制器節點,除了還在GC暫停的節點3,或者通知壓根沒到達的節點3,也就是說節點3不知道leadership已經發生了變化,它還覺得本身是控制器節點。此時,同時存在兩個控制器,並行發出可能存在衝突的命令,致使嚴重的後果

幸運的是,Kafka提供了epoch number的方式能夠輕鬆區分出真實的控制器,它是自增加的序列號,信息存儲在ZooKeeper中,顯然序列號最大的那個節點纔是真實的

clipboard.png

十5、何時應該使用Kafka
從上面幾點可知,Kafka能夠成爲事件驅動架構的中心部分,使你能夠真正將應用程序彼此分離

clipboard.png

你或許對Kafka中的時間輪算法、Kafka中的剛好一次交付等主題也感興趣的話,歡迎前往www.liangsonghua.me閱讀

文章翻譯整理自

一、https://hackernoon.com/thorou...
二、https://hackernoon.com/apache...

文章來源:http://www.liangsonghua.me
做者介紹:京東資深工程師-梁鬆華,長期關注穩定性保障、敏捷開發、JAVA高級、微服務架構

clipboard.png

相關文章
相關標籤/搜索