1、kafka是什麼
一種高吞吐量的分佈式、發佈訂閱消息系統,它可以處理消費者規模的,網站中的所有動作流數據,具有高性能、持久化、多副本備份、橫向擴展能力……
- 以時間複雜度爲 O(1) 的方式提供消息持久化能力,即使對 TB 級以上數據也能保證常數時間複雜度的訪問性能。
- 高吞吐率。即使在非常廉價的商用機器上也能做到單機支持每秒 100K 條以上消息的傳輸。
- 支持 Kafka Server 間的消息分區,及分佈式消費,同時保證每個 Partition 內的消息順序傳輸。
- 同時支持離線數據處理和實時數據處理。
- Scale out:支持在線水平擴展。
2、消息從生產到消費
2.1、發送數據
永遠找leader!消息寫入leader後,follower是主動的去leader進行同步的!producer採用push模式將數據發佈到broker,每條消息追加到分區中,順序寫入磁盤,所以保證同一分區內的數據是有序的!
- partition在寫入的時候可以指定需要寫入的partition,如果有指定,則寫入對應的partition。
- 如果沒有指定partition,但是設置了數據的key,則會根據key的值hash出一個partition。
- 如果既沒指定partition,又沒有設置key,則會輪詢選出一個partition。
發送數據可靠性保證:ACK機制!
0:代表producer往集羣發送數據不需要等到集羣的返回,不確保消息發送成功。安全性最低但是效率最高。
1:代表producer往集羣發送數據只要leader應答就可以發送下一條,只確保leader發送成功。
all:代表producer往集羣發送數據需要所有的follower都完成從leader的同步纔會發送下一條,確保leader發送成功和所有的副本都完成備份。安全性最高,但是效率最低。
敲黑板:所以這裏可能是會丟消息的喲!
0:發丟了,生產端不知道,就是丟了。
1:保證leader不丟,但是如果leader掛了,恰好選了一個沒有ACK的follower,那也丟了。
all:保證leader和follower不丟,但是如果網絡擁塞,沒有收到ACK,會有重複發的問題。
2.2、保存數據
你以爲是這樣的:
其實是這樣的:
操作系統本身有一層緩存,叫做 Page Cache,是在內存裏的緩存,我們也可以稱之爲 OS Cache,意思就是操作系統自己管理的緩存。你在寫入磁盤文件的時候,可以直接寫入這個 OS Cache 裏,也就是僅僅寫入內存中,接下來由操作系統自己決定什麼時候把 OS Cache 裏的數據真的刷入磁盤文件中。
Kafka提供了一個參數——producer.type來控制是不是主動flush,如果Kafka寫入到mmap之後就立即flush然後再返回Producer叫 同步 (sync);寫入mmap之後立即返回Producer不調用flush叫異步 (async)。
敲黑板:所以這裏也可能是會丟消息的喲!
假如已經寫在了OS cache中但是還沒來得及刷入磁盤,此時突然機器宕機,或者broker重啓,那就丟了。
2.3、消費數據
消費者通過pull模式主動的去kafka集羣拉取消息,與producer相同的是,消費者在拉取消息的時候也是找leader去拉取。
多個消費者可以組成一個消費者組(consumer group),每個消費者組都有一個組id!同一個消費組者的消費者可以消費同一topic下不同分區的數據,但是不會組內多個消費者消費同一分區的數據。
在早期的版本中,消費者將消費到的offset維護zookeeper中,consumer每間隔一段時間上報一次,這裏容易導致重複消費,且性能不好!在新的版本中消費者消費到的offset已經直接維護在kafka集羣的__consumer_offsets這個topic中!
消費消息的時候可以大致分爲兩個階段:1、標示該消息已被消費(commit記錄一下);2、處理消息。
敲黑板:所以這裏也可能是會丟消息的喲!
先commit,但是在處理消息的異常了,此時這條消息就丟了。
先處理,再commit,不丟。
3、消息可靠性保證
- At most once 消息可能會丟,但絕不會重複傳輸
- At least once 消息絕不會丟,但可能會重複傳輸
- Exactly once 每條消息肯定會被傳輸一次且僅傳輸一次,很多時候這是用戶所想要的
從 Producer 向 broker 發送消息時,通過ACK機制保證不丟消息,但是不可靠,依賴kafka的參數配置:
- 0:發丟了,生產端不知道,就是丟了,對應At most once。
- 1:保證leader不丟,但是如果leader掛了,恰好選了一個沒有ACK的follower,那也丟了,對應At most once。
- all:保證leader和follower不丟,但是如果網絡擁塞,沒有收到ACK,會有重複發的問題,對應At least once。
在broker存儲消息時,通過主動flush來保證可靠性,但是如果沒設置強制flush,那就有可能丟了。
從 broker 到 Consumer 消費消息時,數據處理與 commit 的順序在很大程度上決定了消息從 broker 和 consumer 的可靠性:
- 讀完消息先 commit 再處理消息。這種模式下,如果 Consumer 在 commit 後還沒來得及處理消息就 crash 了,下次重新開始工作後就無法讀到剛剛已提交而未處理的消息,這就對應於 At most once
- 讀完消息先處理再 commit。這種模式下,如果在處理完消息之後 commit 之前 Consumer crash 了,下次重新開始工作時還會處理剛剛未 commit 的消息,實際上該消息已經被處理過了。這就對應於 At least once。在很多使用場景下,消息的處理往往具有冪等性,即多次處理這一條消息跟只處理一次是等效的,那就可以認爲是 Exactly once。
- 如果一定要做到 Exactly once,就需要協調 offset 和實際操作的輸出。精典的做法是引入兩階段提交。如果能讓 offset 和操作輸入存在同一個地方,會更簡潔和通用。這種方式可能更好,因爲許多輸出系統可能不支持兩階段提交。比如,Consumer 拿到數據後可能把數據放到 HDFS,如果把最新的 offset 和數據本身一起寫到 HDFS,那就可以保證數據的輸出和 offset 的更新要麼都完成,要麼都不完成,間接實現 Exactly once。