面試|圖解kafka的高可用機制

對於一個複雜的分佈式系統,如果沒有豐富的經驗和牛逼的架構能力,很難把系統做得簡單易維護,我們都知道,一個軟件的生命週期中,後期維護佔了70%,所以系統的可維護性是極其重要的, kafka 能成爲大數據領域的事實標準,很大原因是因爲運維起來很方便簡單,今天我們來看下 kafka 是怎麼來簡化運維操作的。

kafka 使用多副本來保證消息不丟失,多副本就涉及到kafka的複製機制,在一個超大規模的集羣中,時不時地這個點磁盤壞了,那個點cpu負載高了,出現各種各樣的問題,多個副本之間的複製,如果想完全自動化容錯,就要做一些考量和取捨了。我們舉個例子說明下運維中面對的複雜性,我們都知道 kafka 有個 ISR集合,我先說明下這個概念:

kafka不是完全同步,也不是完全異步,是一種ISR機制: 

1. leader會維護一個與其基本保持同步的Replica列表,該列表稱爲ISR(in-sync Replica),每個Partition都會有一個ISR,而且是由leader動態維護 

2. 如果一個follower比一個leader落後太多,或者超過一定時間未發起數據複製請求,則leader將其重ISR中移除 

3. 當ISR中所有Replica都向Leader發送ACK時,leader才commit,這時候producer才能認爲一個請求中的消息都commit了。

如果你對技術提升很感興趣,可以加入Java高級技術來交流學習:856443934,裏面都是同行,有資源分享和技術進階思維導圖,其中:(分佈式架構、高可擴展、高性能、高並 發、Jvm性能調優、Spring,MyBatis,Nginx源碼分析,Redis,ActiveMQ、、Mycat、Netty、Kafka、Mysql 、Zookeeper、Tomcat、Docker、Dubbo、Nginx)。歡迎一到五年的工程師加入,合理利用自己每一分每一秒的時間來學習提升自己,不要再用"沒有時間“來掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來的自己一個交代!


在這種機制下,如果一個  producer 一個請求發送的消息條數太多,導致flower瞬間落後leader太多怎麼辦?如果 follower不停的移入移出 ISR 會不會影響性能?如果對這種情況加了報警,就有可能造成告警轟炸,如果我們不加報警,如果是broker 掛掉或者 broker 因爲IO性能或者GC問題夯住的情況導致落後leader太多,這種真正需要報警情況怎麼辦呢?今天我們來看下 kafka 是怎麼在設計上讓我們完全避免這種運維中頭疼的問題的。

kafka的複製機制

kafka 每個分區都是由順序追加的不可變的消息序列組成,每條消息都一個唯一的offset 來標記位置。



kafka中的副本機制是以分區粒度進行復制的,我們在kafka中創建 topic的時候,都可以設置一個複製因子,這個複製因子決定着分區副本的個數,如果leader 掛掉了,kafka 會把分區主節點failover到其他副本節點,這樣就能保證這個分區的消息是可用的。leader節點負責接收producer 打過來的消息,其他副本節點(follower)從主節點上拷貝消息。


 

kakfa 日誌複製算法提供的保證是當一條消息在 producer 端認爲已經 committed的之後,如果leader 節點掛掉了,其他節點被選舉成爲了 leader 節點後,這條消息同樣是可以被消費到的。

這樣的話,leader 選舉的時候,只能從 ISR集合中選舉,集合中的每個點都必須是和leader消息同步的,也就是沒有延遲,分區的leader 維護ISR 集合列表,如果某個點落後太多,就從 ISR集合中踢出去。 producer 發送一條消息到leader節點後, 只有當ISR中所有Replica都向Leader發送ACK確認這條消息時,leader才commit,這時候producer才能認爲這條消息commit了,正是因爲如此,kafka客戶端的寫性能取決於ISR集合中的最慢的一個broker的接收消息的性能,如果一個點性能太差,就必須儘快的識別出來,然後從ISR集合中踢出去,以免造成性能問題。kafka 複製機制詳情參考  https://kafka.apache.org/documentation.html#replication

一個副本怎麼纔算是跟得上leader的副本

一個副本不能 “caught up” leader 節點,就有可能被從 ISR集合中踢出去,我們舉個例子來說明,什麼纔是真正的 “caught up” —— 跟leader節點消息同步。

kafka 中的一個單分區的 topic — foo,複製因子爲 3 ,分區分佈和 leader 和 follower 如下圖,現在broker 2和3 是 follower 而且都在 ISR 集合中。我們設置 replica.lag.max.messages 爲4,只要 follower 只要不落後leader 大於3條消息,就然後是跟得上leader的節點,就不會被踢出去, 設置  replica.lag.time.max.ms 爲 500ms, 意味着只要 follower 在每 500ms內發送fetch請求,就不會被認爲已經dead ,不會從ISR集合中踢出去。



現在 producer 發送一條消息,offset 爲3, 這時候 broker 3 發生了 GC, 入下圖:


因爲 broker 3 現在在 ISR 集合中, 所以要麼 broker 3  拉取同步上這條 offset 爲3 的消息,要麼 3 被從 ISR集合中踢出去,不然這條消息就不會 committed, 因爲 replica.lag.max.messages=4 爲4, broker 3 只落後一條消息,不會從ISR集合中踢出去, broker 3 如果這時候 GC  100ms, GC 結束,然後拉取到 offset 爲3的消息,就再次跟 leader 保持完全同步,整個過程一直在 ISR集合中,如下圖:


什麼時候一個副本纔會從ISR集合中踢出去

一個副本被踢出 ISR集合的幾種原因:

  • 一個副本在一段時間內都沒有跟得上 leader 節點,也就是跟leader節點的差距大於 replica.lag.max.messages 通常情況是 IO性能跟不上,或者CPU 負載太高,導致 broker 在磁盤上追加消息的速度低於接收leader 消息的速度。

  • 一個 broker 在很長時間內(大於 replica.lag.time.max.ms )都沒有向 leader 發送fetch 請求,  可能是因爲 broker 發生了 full GC, 或者因爲別的原因掛掉了。

  • 一個新 的 broker 節點,比如同一個 broker id, 磁盤壞掉,新換了一臺機器,或者一個分區 reassign 到一個新的broker 節點上,都會從分區leader 上現存的最老的消息開始同步。


所以說 kafka 0.8 版本後設置了兩個參數  , replica.lag.max.messages 用來識別性能一直很慢的節點, replica.lag.time.max.ms 用來識別卡住的節點。

一個節點在什麼情況下真正處於落後狀態

從上面的情況來看,兩個參數看似已經足夠了,如果一個副本超過 replica.lag.time.max.ms還沒有發送fetch同步請求, 可以認爲這個副本節點卡住了,然後踢出去,但是還有一種比較特殊的情況沒有考慮到,我們上文中設置 replica.lag.max.messages 爲4,之所以設置爲 4, 是我們已經知道 producer 每次請求打過來的消息數都在 4 以下,如果我們的參數是作用於多個 topic 的情況,那麼這個 producer 最大打過來的消息數目就不好估計了,或者說在經常出現流量抖動的情況下,就會出現一個什麼情況呢,我們還是使用例子說明:


如果我們的 topic — foo  的 producer 因爲流量抖動打過來一個 包含 4條消息的請求,我們設置的 replica.lag.max.messages 還是爲4, 這個時候,所有的 follower 都會因爲超出落後條數被踢出 ISR集合:


然後,因爲 follower 是正常的,所以下一次 fetch 請求就會又追上 leader, 這時候就會再次加入 ISR 集合,如果經常性的抖動,就會不斷的移入移出ISR集合,會造成令人頭疼的 告警轟炸。

這裏的核心問題是,在海量的 topic 情況下,或者經常性的流量抖動情況下,我們不能對 topic 的producer 每次打過來的消息數目做任何假設,所以就不太好定出來一個 合適的 eplica.lag.max.messages 

一個配置全部搞定

如果你對技術提升很感興趣,可以加入Java高級技術來交流學習:856443934,裏面都是同行,有資源分享和技術進階思維導圖,其中:(分佈式架構、高可擴展、高性能、高並 發、Jvm性能調優、Spring,MyBatis,Nginx源碼分析,Redis,ActiveMQ、、Mycat、Netty、Kafka、Mysql 、Zookeeper、Tomcat、Docker、Dubbo、Nginx)。歡迎一到五年的工程師加入,合理利用自己每一分每一秒的時間來學習提升自己,不要再用"沒有時間“來掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來的自己一個交代!

其實只有兩種情況是異常的,一種就是卡住,另外一種是follower 性能慢,如果我們只根據 follower 落後 leader 多少來判斷是否應該把 follower 提出ISR集合,就必須要對流量進行預測估計,怎麼才能避免這種不靠譜的估計呢,kafka 給出的方案是這樣的,對 replica.lag.time.max.ms 這個配置的含義做了增強,和之前一樣,如果 follower 卡住超過這個時間不發送fetch請求, 會被踢出ISR集合,新的增強邏輯是,在 follower 落後 leader 超過 eplica.lag.max.messages 條消息的時候,不會立馬踢出ISR 集合,而是持續落後超過  replica.lag.time.max.ms 時間,纔會被踢出,這樣就能避免流量抖動造成的運維問題,因爲follower 在下一次fetch的時候就會跟上leader, 這樣就也不用對 topic 的寫入速度做任何的估計嘍。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章