Push VS Pull
An initial question we considered is whether consumers should pull data from brokers or brokers should push data to the consumer. In this respect Kafka follows a more traditional design, shared by most messaging systems, where data is pushed to the broker from the producer and pulled from the broker by the consumer.
Kafka遵循傳統設計,也是被大多數訊息系統所採用的,即,producer push資料到broker,consumer從broker那裡pull資料。
A push-based system has difficulty dealing with diverse consumers as the broker controls the rate at which data is transferred. The goal is generally for the consumer to be able to consume at the maximum possible rate; unfortunately, in a push system this means the consumer tends to be overwhelmed when its rate of consumption falls below the rate of production.
基於push的系統在面對不同的消費者的時候需要控制資料傳輸的速率這是很困難的,一般消費者都是以最大速率去消費的,不幸的是這對於push系統來說意味著當消費的速率低於生產的速率時消費者有崩潰的傾向。而基於pull的系統就有很好的屬性,消費者可以簡單的落後,並且在可能的時候迎頭趕上。在消費者感覺快要崩潰的時候可以緩和一下,讓消費者充分利用傳輸速率比看起來要複雜得多。
基於pull的系統的另一個優勢在於broker可以將自己的資料批量的傳送給消費者。而基於push的系統必須選擇是立即傳送請求還是累積更多的資料再發請求,而且傳送以後它不知道消費者是否能很快的處理它。基於pull的系統就能夠很好的解決這個問題,因為消費者總是拉取在當前日誌的位置之後的所有可用的訊息。
基於pull的系統有一個缺陷就是如果broker沒有資料,消費者會一直迴圈等待,有效的繁忙的等待直到有資料到達。為了避免這個問題, 我們可以讓消費者在請求的時候以“長輪詢”的形式阻塞,直到有資料到達。
Consumer Position
大部分的訊息系統在broker中維護關於訊息是否已經被消費的後設資料。因此,一個訊息被分發給消費者以後broker可以理解處理或者等到消費者確認。
或許你還沒有意識到讓broker和consumer就訊息是否已經被訊息達成一致是一個很重要的問題。如果每次訊息被分發出去以後broker就立即將這條訊息標記為consumed,這個時候如果消費者處理訊息失敗了(可能是因為它當機了或者請求超時了或者其它的原因),那麼這條訊息就丟失了。為了解決這個問題,許多訊息系統增加一個確認特性,這就意味著在訊息被髮送以後只能將它們標記為sent而不是consumed,broker會等待來自消費者的確認,之後才會將這條訊息標記為consumed。這個策略雖然解決了丟失訊息的問題,但是又帶來了新的問題。首先,如果消費者已經處理完訊息,但是在它傳送確認之前它失敗了,那麼這種情況下會導致訊息被消費兩次。第二個問題跟效能有關,現在broker對於每條訊息必須維護多個狀態(首先鎖定它以至於它不會被分發第二次,然後永久的標記它為consumed以至於它肯能會被刪除)。棘手的問題必須被處理,比如訊息傳送後沒有確認。
Kafka handles this differently. Our topic is divided into a set of totally ordered partitions, each of which is consumed by exactly one consumer within each subscribing consumer group at any given time. This means that the position of a consumer in each partition is just a single integer, the offset of the next message to consume. This makes the state about what has been consumed very small, just one number for each partition. This state can be periodically checkpointed. This makes the equivalent of message acknowledgements very cheap.
Kafka對這些問題的處理不同,主題被劃分為一系列有序的分割槽,在任意時刻,一個分割槽只能被訂閱這個主題的每個消費者組中的一個消費者消費。這就意味著,消費者的位置在每個分割槽中僅僅是一個整數,這個整數就是即將要消費的下一個訊息的offset(偏移量)。這使得標記關於訊息是否已經被消費的狀態變得很簡單,每個分割槽僅僅一個數。這個狀態可以週期性的被檢查。這跟訊息確認是等價的,而且也是非常廉價的。
消費者可以故意倒回到一箇舊的offset去重新消費資料。當然,這一點違反了佇列的公共契約,但事實上這是許多消費者的一個基本特性。例如,如果消費者的程式碼有bug,並且在一些訊息被消費以後才發現,那麼消費者可以重新消費這些訊息以修復這個bug。
參考 http://kafka.apache.org/documentation/#design