消息分發語義(可靠性)
1、消息發送可靠性保證
1)acks=0 --- producer不等待broker的acks。發送的消息可能丟失,但永遠不會重發。
2)acks=1 --- leader不等待其他follower同步,leader直接寫log然后發送acks給producer。這種情況下會有重發現象,可靠性比only once好點,但是仍然會丟消息。例如leader掛了了,但是其他replication還沒同步完成。
3)acks=all --- leader等待所有follower同步完成才返回acks。消息可靠不丟失(丟了會重發),沒收到ack會重發。
2、消費者的可靠性保障(關鍵是保存offset的時機):
1)至多一次(at most once):讀取消息->保存offset->處理消息。處理消息時崩潰則會丟失消息,因為此時offset已經改變了。
2)至少一次(at least once):讀取消息->處理消息->保存offset。保存offset失敗,會造成重復消費,但是不會丟消息。如果重讀消費時冪等操作,那就不會出現重復消息了。前面2個步驟失敗可以在offset位置重新消費。
3)有且僅有一次(exactly once):保存offset和處理消息這兩個環節采用two-phase commit(2PC)。但是,在Kafka中,一種更簡單的方法就是可以把offset和處理后的結果一起存儲。有點把處理結果和offset做成原子性的感覺。這樣可以避免重復消費。
Kafka的組件和特性
1、分區
1)靈活性(負載均衡控制、靈活消費)
A、Kafka允許Partition在集群內的Broker之間任意移動,以此來均衡可能存在的數據傾斜問題。
B、Partition支持自定義的分區算法,例如可以將同一個Key的所有消息都路由到同一個Partition上去。
C、同時Leader也可以在In-Sync的Replica中遷移。由于針對某一個Partition的所有讀寫請求都是只由Leader來處理,所以Kafka會盡量把Leader均勻的分散到集群的各個節點上,以免造成網絡流量過于集中。
分區有偏移量的概念。消費者通過控制偏移量,可以靈活的消費消息。
2)并發性
任意Partition在某一個時刻只能被一個Consumer Group內的一個Consumer消費(反過來一個Consumer則可以同時消費多個Partition),Kafka非常簡潔的Offset機制最小化了Broker和Consumer之間的交互,這使Kafka并不會像同類其他消息隊列一樣,隨著下游Consumer數目的增加而成比例的降低性能。此外,如果多個Consumer恰巧都是消費時間序上很相近的數據,可以達到很高的PageCache命中率,因而Kafka可以非常高效的支持高并發讀操作,實踐中基本可以達到單機網卡上限
3)高可用
分區采用leader-follower的組織架構來保證高可用
分區有序消費
kafka中每個分區都是一個順序、不可變的消息隊列。提供一個分區內順序消費的語義
2、消費者
1)High-level api 和 low-level api
Consumer API分為High level和Low level兩種。前一種重度依賴Zookeeper,所以性能差一些且不自由,但是超省心。第二種不依賴Zookeeper服務,無論從自由度和性能上都有更好的表現,但是所有的異常(Leader遷移、Offset越界、Broker宕機等)和Offset的維護都需要自行處理。
總結:
high level api: zookeeper自動管理offset,自動獲取last offset,包括leader遷移、broker宕機都自動化管理
low level api :手動管理offset、leader遷移、broker宕機的事情
2)Kafka是pull模型
消費者應該從broker中pull數據還是broker應該向消費者push數據,在這方面,kafka遵循比較傳統的設計,大多數消息系統,生產者推消息到broker,消費者從broker拉取消息,一些logging-centric的系統,比如 Scribe 和Apache Flume ,采用非常不同的push模式。事實上,push模式和pull模式各有優劣。push模式很難適應消費速率不同的消費者,因為消息發送速率是由broker決定的。push模式的目標是盡可能以最快速度傳遞消息,但是這樣很容易造成consumer來不及處理消息,典型的表現就是拒絕服務以及網絡擁塞。而pull模式則可以根據consumer的消費能力以適當的速率消費消息。
基于pull模式的另一個優點是,它有助于積極的批處理的數據發送到消費者。基于push模式必須選擇要么立即發送請求或者積累更多的數據,稍后發送它,無論消費者是否能立刻處理它,如果是低延遲,這將導致短時間只發送一條消息,不用緩存,這是實在是一種浪費,基于pull的設計解決這個問題,消費者總是pull在日志的當前位置之后pull所有可用的消息(或配置一些大size),所以消費者可設置消費多大的量,也不會引入不必要的等待時間。
3、生產者
1)消息丟失問題
不過Kafka采用MessageSet也導致在可用性上一定程度的妥協。每次發送數據時,Producer都是send()之后就認為已經發送出去了,但其實大多數情況下消息還在內存的MessageSet當中,尚未發送到網絡,這時候如果Producer掛掉,那就會出現丟數據的情況。
解決辦法:
采用網絡中的ack機制。當然這種是可選的。通過配置acks的值來控制。
4、大吞吐量、強大消息堆積能力等特性
1)依賴OS文件系統的頁緩存
當上層有寫操作時,操作系統只是將數據寫入PageCache,同時標記Page屬性為Dirty。當讀操作發生時,先從PageCache中查找,如果發生缺頁才進行磁盤調度,最終返回需要的數據。實際上PageCache是把盡可能多的空閑內存都當做了磁盤緩存來使用。同時如果有其他進程申請內存,回收PageCache的代價又很小。
總結:依賴OS的頁緩存能大量減少IO,高效利用內存來作為緩存
2)順序IO以及常量時間get、put消息
順序IO:只采用順序IO不僅可以利用RAID技術帶來很高的吞吐量,同時可以利用隊列來提供常量時間的get和put。這樣獲取消息的效率也就是O(1)了。這種設計方法使得消息訪問速度和消息堆積的量剝離了聯系。而且操作系統對順序IO都會進行優化,提升整體順序IO的性能
3)sendfile技術(零拷貝)
傳統網絡IO流程:
A、OS 從硬盤把數據讀到內核區的PageCache。
B、用戶進程把數據從內核區Copy到用戶區。
C、然后用戶進程再把數據寫到Socket,數據流入內核區的Socket Buffer上。
D、OS 再把數據從Buffer中Copy到網卡的Buffer上,這樣完成一次發送。
4)Producer支持End-to-End的壓縮。數據在本地壓縮后放到網絡上傳輸,在Broker一般不解壓(除非指定要Deep-Iteration),直至消息被Consume之后在客戶端解壓。
當然用戶也可以選擇自己在應用層上做壓縮和解壓的工作(畢竟Kafka目前支持的壓縮算法有限,只有GZIP和Snappy),不過這樣做反而會意外的降低效率!!!! Kafka的End-to-End壓縮與MessageSet配合在一起工作效果最佳,上面的做法直接割裂了兩者間聯系。至于道理其實很簡單,壓縮算法中一條基本的原理重復的數據量越多,壓縮比越高。無關于消息體的內容,無關于消息體的數量,大多數情況下輸入數據量大一些會取得更好的壓縮比。
知識點擴展:
MessageSet : https://segmentfault.com/a/1190000006875926
Kafka高可用
kafka每個主題分區的復制日志跨多個可配置的服務器(可設置 topic-by-topic 的復制因子),允許自動故障轉到這些副本,當集群服務器發生故障時,消息仍可用。
kafka通過分區的復制,來實現高可用。當leader掛了,可以重新選舉新的leader來保證消費的高可用.
選舉算法(選日志最完整的作為新leader)
總結:zk的quorum選舉適用在共享集群配置而不是主數據存儲。因為其吞吐量低,容忍故障所需要的冗余副本比較多
以上內容均為作者個人筆記,如有錯誤歡迎指正...
關注CSDN博客 Zonzereal,更多大數據筆記等你...
掃碼關注,共同進步!

? 2025. All Rights Reserved. 滬ICP備2023009024號-1