kafka深入研究之路(1)-剖析各原理01

kafka深入研究之路(1)-剖析各原理01

引言:來到了新公司,需要對kafka組件有很深的研究,本人之前對老版的kafka有過一定的研究,但是談不上深入,新公司力推kafka,比較kafka作爲消息系統在目前的市場上的佔有率還是很高的,可以看本人之前kafka的博客中有關kafka的優點和爲什麼要用kafka。
在衆多優點中,我本人認爲最重要的2個優點如下:

1、削峯
數據庫的處理能力是有限的,在峯值期,過多的請求落到後臺,一旦超過系統的處理能力,可能會使系統掛掉。

kafka深入研究之路(1)-剖析各原理01

如上圖所示,系統的處理能力是 2k/s,MQ 處理能力是 8k/s,峯值請求 5k/s,MQ 的處理能力遠遠大於數據庫,在高峯期,請求可以先積壓在 MQ 中,系統可以根據自身的處理能力以 2k/s 的速度消費這些請求。
這樣等高峯期一過,請求可能只有 100/s,系統可以很快的消費掉積壓在 MQ 中的請求。
注意,上面的請求指的是寫請求,查詢請求一般通過緩存解決。

2、解耦
如下場景,S 系統與 A、B、C 系統緊密耦合。由於需求變動,A 系統修改了相關代碼,S 系統也需要調整 A 相關的代碼。
過幾天,C 系統需要刪除,S 緊跟着刪除 C 相關代碼;又過了幾天,需要新增 D 系統,S 系統又要添加與 D 相關的代碼;再過幾天,程序猿瘋了...

kafka深入研究之路(1)-剖析各原理01

這樣各個系統緊密耦合,不利於維護,也不利於擴展。現在引入 MQ,A 系統變動,A 自己修改自己的代碼即可;C 系統刪除,直接取消訂閱;D 系統新增,訂閱相關消息即可。
kafka深入研究之路(1)-剖析各原理01

這樣通過引入消息中間件,使各個系統都與 MQ 交互,從而避免它們之間的錯綜複雜的調用關係。


kafka架構原理:

最經典的圖也就是官方的圖了
kafka深入研究之路(1)-剖析各原理01

找了一些其他博主的圖:這裏自己就懶的畫了

kafka深入研究之路(1)-剖析各原理01

通俗點講:就是producer ----> kafka cluster(brokers) -----> consumer
生產者生產消息 經過 kafka隊列 被消費者消費

相關的組件概念見:


topic and logs

廢話不多說,先見圖
kafka深入研究之路(1)-剖析各原理01

文字解釋如下:
Message 是按照 Topic 來組織的,每個 Topic 可以分成多個 Partition(對server.properties/num.partitions)。 本人習慣性配置文件爲num.partitions=broker個數,人爲的分配到各個節點上。

Partition 中的每條記錄(Message)包含三個屬性:Offset,messageSize 和 Data。
其中 Offset 表示消息偏移量;messageSize 表示消息的大小;Data 表示消息的具體內容。

Partition 是以文件的形式存儲在文件系統中,位置由 server.properties/log.dirs 指定,其命名規則爲 <topic_name>-<partition_id>。
生產配置文件爲:log.dirs=/data/kafka/kafka-logs

[hadoop@kafka03-55-13 kafka-logs]$ pwd
/data/kafka/kafka-logs
[hadoop@kafka03-55-13 kafka-logs]$ ls |grep mjh
topic-by-mjh-0
topic-by-mjh-1
topic-by-mjh-10
topic-by-mjh-11
topic-by-mjh-12
...
...
...

Partition 可能位於不同的 Broker 上,Partition 是分段的,每個段是一個 Segment 文件。

Partition 目錄下包括了數據文件和索引文件

[hadoop@kafka03-55-13 kafka-logs]$ cd topic-by-mjh-0
[hadoop@kafka03-55-13 topic-by-mjh-0]$ ll
total 4
-rw-rw-r-- 1 hadoop hadoop 10485760 Aug 24 20:13 00000000000000000334.index
-rw-rw-r-- 1 hadoop hadoop        0 Aug 13 17:42 00000000000000000334.log
-rw-rw-r-- 1 hadoop hadoop 10485756 Aug 24 20:13 00000000000000000334.timeindex
-rw-rw-r-- 1 hadoop hadoop        4 Aug 16 14:16 leader-epoch-checkpoint

Index 採用稀疏存儲的方式,它不會爲每一條 Message 都建立索引,而是每隔一定的字節數建立一條索引,避免索引文件佔用過多的空間。
缺點是沒有建立索引的 Offset 不能一次定位到 Message 的位置,需要做一次順序掃描,但是掃描的範圍很小。
索引包含兩個部分(均爲 4 個字節的數字),分別爲相對 Offset 和 Position。
相對 Offset 表示 Segment 文件中的 Offset,Position 表示 Message 在數據文件中的位置。

小結:
1、Partition 是一個順序的追加日誌,屬於順序寫磁盤(順序寫磁盤效率比隨機寫內存要高,保障 Kafka 吞吐率)。
2、Kafka 的 Message 存儲採用了分區(Partition),磁盤順序讀寫,分段(LogSegment)和稀疏索引這幾個手段來達到高效性。


Partition and Replica

kafka深入研究之路(1)-剖析各原理01

一個 Topic 物理上分爲多個 Partition,位於不同的 Broker 上。如果沒有 Replica,一旦 Broker 宕機,其上所有的 Patition 將不可用。

每個 Partition 可以有多個Replica(對應server.properties/default.replication.factor),分配到不同的 Broker 上。本人默認習慣爲 default.replication.factor=2 也就是默認2個副本,比較合理

其中有一個 Leader 負責讀寫,處理來自 Producer 和 Consumer 的請求;其他作爲 Follower 從 Leader Pull 消息,保持與 Leader 的同步。

如何分配 Partition 和 Replica 到 Broker 上?步驟如下:

1、將所有 Broker(假設共 n 個 Broker)和待分配的 Partition 排序。
2、將第 i 個 Partition 分配到第(i mod n)個 Broker 上。
3、將第 i 個 Partition 的第 j 個 Replica 分配到第((i + j) mode n)個 Broker 上。
根據上面的分配規則,若 Replica 的數量大於 Broker 的數量,必定會有兩個相同的 Replica 分配到同一個 Broker 上,產生冗餘。因此 Replica 的數量應該小於或等於 Broker 的數量。
//這裏kafka硬性規定了創建的replica不能超過broker的數量,必須等於小於broker的數量

這裏有2個算法函數解釋一下
1、mod:求餘函數;
2、mode:返回在某數組或數據區域中出現頻率最多的數值,mode是一個位置測量函數。

我這裏只有3個broker 創建4個replica就出現報錯 具體見下
[root@kafka02-55-12 ~]# kafka-topics.sh --zookeeper 10.211.55.11:2181,10.211.55.12:2181,10.211.55.13:2181/kafkagroup  --replication-factor 4 --partitions 9 --create  --topic topic-zhuhair
**Error while executing topic command : Replication factor: 4 larger than available brokers: 3.**
[2019-08-24 20:41:40,611] ERROR org.apache.kafka.common.errors.InvalidReplicationFactorException: Replication factor: 4 larger than available brokers: 3.

pratition的leader是如何選舉的---broker failover故障轉移
//通俗點講也就是當broker發生宕機了,如何保證高可用的

kafka深入研究之路(1)-剖析各原理01

文字描述如下:
Kafka 在 Zookeeper 中(/brokers/topics/[topic]/partitions/[partition]/state)動態維護了一個 ISR(in-sync replicas)。
ISR 裏面的所有 Replica 都"跟上"了 Leader,Controller 將會從 ISR 裏選一個做 Leader。

具體流程文字描述如下:
1、Controller 在 Zookeeper 的 /brokers/ids/[brokerId] 節點註冊 Watcher,
2、當 Broker 宕機時 Zookeeper 會 Fire Watch。
3、Controller 從 /brokers/ids 節點讀取可用 Broker。
4、Controller 決定 set_p,該集合包含宕機 Broker 上的所有 Partition。
5、對 set_p 中的每一個 Partition,從/brokers/topics/[topic]/partitions/[partition]/state 節點讀取 ISR,決定新 Leader,將新 Leader、ISR、controller_epoch 和 leader_epoch 等信息寫入 State 節點。
6、zk通過 RPC 向相關 Broker 發送 leaderAndISRRequest 命令。

極端情況下需要考慮的是:
當 ISR 爲空時,會選一個 Replica(不一定是 ISR 成員)作爲 Leader;
當所有的 Replica 都歇菜了,會等任意一個 Replica 復活,將其作爲 Leader。
//
這就需要在可用性和一致性當中作出一個簡單的折衷。如果一定要等待ISR中的Replica“活”過來,那不可用的時間就可能會相對較長。而且如果ISR中的所有Replica都無法“活”過來了,或者數據都丟失了,這個Partition將永遠不可用。選擇第一個“活”過來的Replica作爲Leader,而這個Replica不是ISR中的Replica,那即使它並不保證已經包含了所有已commit的消息,它也會成爲Leader而作爲consumer的數據源(前文有說明,所有讀寫都由Leader完成)。Kafka0.8.*使用了第二種方式。根據Kafka的文檔,在以後的版本中,Kafka支持用戶通過配置選擇這兩種方式中的一種,從而根據不同的使用場景選擇高可用性還是強一致性。

ISR(同步列表)中的 Follower 都"跟上"了Leader,"跟上"並不表示完全一致,它由 server.properties/replica.lag.time.max.ms 配置。表示 Leader 等待 Follower 同步消息的最大時間,如果超時,Leader 將 Follower 移除 ISR。配置項 replica.lag.max.messages 已經移除。


Replica 副本如何同步 消息傳遞同步策略

1、Producer在發佈消息到某個Partition時,先通過ZooKeeper找到該Partition的Leader,
2、無論該Topic的Replication Factor爲多少,Producer只將該消息發送到該Partition的Leader。
3、Leader會將該消息寫入其本地Log。
4、每個Follower都從Leader pull數據。這種方式上,Follower存儲的數據順序與Leader保持一致。
5、Follower在收到該消息並寫入其Log後,向Leader發送ACK。
6、一旦Leader收到了ISR中的所有Replica的ACK,該消息就被認爲已經commit了,Leader將增加HW並且向Producer發送ACK。

爲了提高性能,每個Follower在接收到數據後就立馬向Leader發送ACK,而非等到數據寫入Log中。
因此,對於已經commit的消息,Kafka只能保證它被存於多個Replica的內存中,而不能保證它們被持久化到磁盤中,也就不能完全保證異常發生後該條消息一定能被Consumer消費。

Consumer讀消息也是從Leader讀取,只有被commit過的消息纔會暴露給Consumer。

kafka深入研究之路(1)-剖析各原理01

具體的可靠性,是由生產者(根據配置項 producer.properties/acks)來決定的。
有資料說 最新的文檔 2.2.x request.required.acks 已經不存在了,這一點有待我去確認

通俗一點講對ack的三個參數的含義爲
Kafka producer有三種ack機制  初始化producer時在config中進行配置

0 :意味着producer不等待broker同步完成的確認,繼續發送下一條(批)信息
提供了最低的延遲。但是最弱的持久性,當服務器發生故障時,就很可能發生數據丟失。例如leader已經死亡,producer不知情,還會繼續發送消息broker接收不到數據就會數據丟失
1:意味着producer要等待leader成功收到數據並得到確認,才發送下一條message。此選項提供了較好的持久性較低的延遲性。Partition的Leader死亡,follwer尚未複製,數據就會丟失
-1:意味着producer得到follwer確認,才發送下一條數據

持久性最好,延時性最差。

在這裏強調的一點是,在kafak的partition中的fllower和leader中的複製不是完全的同步複製,也不是單純的異步複製
同步複製:所有的fllower複製完才提交 這樣的缺點是極大的影響了吞吐率
異步複製:Follower異步的從Leader複製數據,數據只要被Leader寫入log就被認爲已經commit,這種情況下如果Follower都複製完都落後於Leader,而如果Leader突然宕機,則會丟失數據。
所有 kafak採用的是ISR 的方式則很好的均衡了確保數據不丟失以及吞吐率。Follower可以批量的從Leader複製數據,這樣極大的提高複製性能(批量寫磁盤),極大減少了Follower與Leader的差距。


producer如何發送消息
Producer 首先將消息封裝進一個 ProducerRecord 實例中。
kafka深入研究之路(1)-剖析各原理01

寫消息的路由模式

1、 指定了 patition,則直接使用;
2、 未指定 patition 但指定 key,通過對 key 的 value 進行hash 選出一個 patition
這個 Hash(即分區機制)由 producer.properties/partitioner.class 指定的類實現,這個路由類需要實現 Partitioner 接口。
3、 patition 和 key 都未指定,使用輪詢選出一個 patition。

備註:消息並不會立即發送,而是先進行序列化後,發送給 Partitioner,
也就是上面提到的 Hash 函數,由 Partitioner 確定目標分區後,發送到一塊內存緩衝區中(發送隊列)。Producer 的另一個工作線程(即 Sender 線程),
則負責實時地從該緩衝區中提取出準備好的消息封裝到一個批次內,統一發送到對應的 Broker 中。

具體寫數據流程如下:
kafka深入研究之路(1)-剖析各原理01

具體流程如下:
1、 producer 先從 zookeeper 的 "/brokers/.../state" 節點找到該 partition 的 leader
2、 producer 將消息發送給該 leader
3、 leader 將消息寫入本地 log
4、 followers 從 leader pull 消息,寫入本地 log 後 leader 發送 ACK
5、 leader 收到所有 ISR 中的 replica 的 ACK 後,增加 HW(high watermark,最後 commit 的 offset) 並向 producer 發送 ACK

參考鏈接:
架構成長之路:Kafka設計原理看了又忘,忘了又看?一文讓你掌握: https://www.toutiao.com/i6714606866355192328/
Kafka的ACK機制有三種,是哪三種 : https://blog.csdn.net/Sun1181342029/article/details/87806207
kafka原理系列ACK機制(數據可靠性和持久性保證) https://blog.csdn.net/bluehawksk/article/details/96120803
kafka入門介紹 https://www.orchome.com/5
Kafka學習之路 (三)Kafka的高可用 https://www.cnblogs.com/qingyunzong/p/9004703.html

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