深入淺出談一下有關分布式消息技術:Kafka

Kafka的基本介紹

Kafka是一個分布式、分區的、多副本的、多訂閱者,基于zookeeper協調的分布式日志系統(也可以當做MQ系統),常見可以用于web/nginx日志、訪問日志,消息服務等等。

主要應用場景是:日志收集系統和消息系統。

Kafka主要設計目標如下:

以時間復雜度為O(1)的方式提供消息持久化能力,即使對TB級以上數據也能保證常數時間的訪問性能。

高吞吐率。即使在非常廉價的商用機器上也能做到單機支持每秒100K條消息的傳輸。

支持Kafka Server間的消息分區,及分布式消費,同時保證每個Partition內的消息順序傳輸。

同時支持離線數據處理和實時數據處理。

Kafka的設計原理分析

一個典型的Kafka集群中包含若干Producer,若干Broker,若干Consumer,以及一個Zookeeper集群。Kafka通過Zookeeper管理集群配置,選舉Leader,以及在Consumer Group發生變化時進行Rebalance。Producer使用push模式將消息發布到Broker,Consumer使用Pull模式從Broker訂閱并消費消息。

Kafka專用術語:

Broker:消息中間件處理結點,一個Kafka節點就是一個broker,多個broker可以組成一個Kafka集群。

Topic:一類消息,Kafka集群能夠同時負責多個topic的分發。

Partition:topic物理上的分組,一個topic可以分為多個partition,每個partition是一個有序的隊列。

Segment:partition物理上由多個segment組成。

offset:每個partition都由一系列有序的、不可變的消息組成,這些消息被連續的追加到partition中。partition中的每個消息都有一個連續的序列號叫做offset,用于partition唯一標識一條消息。

Producer:負責發布消息到Kafka broker。

Consumer:消息消費者,向Kafka broker讀取消息的客戶端。

Consumer Group:每個Consumer屬于一個特定的Consumer Group。

Kafka消息存儲格式

Topic & Partition

一個Topic可以認為一個一類消息,每個Topic將被分成多個Partition,每個Partition在存儲層面是Append Log文件。

在Kafka文件存儲中,同一個Topic下有多個不同Partition,每個Partition為一個目錄,Partiton命名規則為Topic名稱+有序序號,第一個Partiton序號從0開始,序號最大值為Partitions數量減1。

每個Partion(目錄)相當于一個巨型文件被平均分配到多個大小相等Segment(段)數據文件中。但每個段Segment file消息數量不一定相等,這種特性方便old segment file快速被刪除。

每個Partiton只需要支持順序讀寫就行了,segment文件生命周期由服務端配置參數決定。

這樣做的好處就是能快速刪除無用文件,有效提高磁盤利用率。

Segment file組成:由2大部分組成,分別為index file和data file,此2個文件一一對應,成對出現,后綴".index"和“.log”分別表示為segment索引文件、數據文件.

Segment文件命名規則:Partion全局的第一個Segment從0開始,后續每個Segment文件名為上一個Segment文件最后一條消息的Offset值。數值最大為64位long大小,19位數字字符長度,沒有數字用0填充。

Segment中index與data file對應關系物理結構如下:

上圖中索引文件存儲大量元數據,數據文件存儲大量消息,索引文件中元數據指向對應數據文件中message的物理偏移地址。

其中以索引文件中元數據3,497為例,依次在數據文件中表示第3個message(在全局partiton表示第368772個message),以及該消息的物理偏移地址為497。

了解到Segment data file由許多message組成,下面詳細說明message物理結構如下:

參數說明:

關鍵字解釋說明8 byte offset在parition(分區)內的每條消息都有一個有序的id號,這個id號被稱為偏移(offset),它可以唯一確定每條消息在parition(分區)內的位置。即offset表示partiion的第多少message4 byte message sizemessage大小4 byte CRC32用crc32校驗message1 byte “magic"表示本次發布Kafka服務程序協議版本號1 byte “attributes"表示為獨立版本、或標識壓縮類型、或編碼類型。4 byte key length表示key的長度,當key為-1時,K byte key字段不填K byte key可選value bytes payload表示實際消息數據。

Kafka數據傳輸的事務特點

At most once:最多一次,這個和JMS中"非持久化"消息類似,發送一次,無論成敗,將不會重發。消費者fetch消息,然后保存offset,然后處理消息;當client保存offset之后,但是在消息處理過程中出現了異常,導致部分消息未能繼續處理。那么此后"未處理"的消息將不能被fetch到,這就是"at most once"。

At least once:消息至少發送一次,如果消息未能接受成功,可能會重發,直到接收成功。消費者fetch消息,然后處理消息,然后保存offset。如果消息處理成功之后,但是在保存offset階段zookeeper異常導致保存操作未能執行成功,這就導致接下來再次fetch時可能獲得上次已經處理過的消息,這就是"at least once",原因offset沒有及時的提交給zookeeper,zookeeper恢復正常還是之前offset狀態。

Exactly once:消息只會發送一次。kafka中并沒有嚴格的去實現(基于2階段提交),我們認為這種策略在kafka中是沒有必要的。

通常情況下"at-least-once"是我們首選。

副本(replication)策略

Kafka的高可靠性的保障來源于其健壯的副本(replication)策略。

1. 副本放置策略

為了更好的做負載均衡,Kafka盡量將所有的Partition均勻分配到整個集群上。

Kafka分配Replica的算法如下:

將所有存活的N個Brokers和待分配的Partition排序

將第i個Partition分配到第(i mod n)個Broker上,這個Partition的第一個Replica存在于這個分配的Broker上,并且會作為partition的優先副本

將第i個Partition的第j個Replica分配到第((i + j) mod n)個Broker上

假設集群一共有4個brokers,一個Topic有4個partition,每個Partition有3個副本。下圖是每個Broker上的副本分配情況。

2. 同步策略

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

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

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

Kafka Replication的數據流如下圖所示:

對于Kafka而言,定義一個Broker是否“活著”包含兩個條件:

一是它必須維護與ZooKeeper的session(這個通過ZooKeeper的Heartbeat機制來實現)。

二是Follower必須能夠及時將Leader的消息復制過來,不能“落后太多”。

Leader會跟蹤與其保持同步的Replica列表,該列表稱為ISR(即in-sync Replica)。如果一個Follower宕機,或者落后太多,Leader將把它從ISR中移除。這里所描述的“落后太多”指Follower復制的消息落后于Leader后的條數超過預定值或者Follower超過一定時間未向Leader發送fetch請求。

Kafka只解決fail/recover,一條消息只有被ISR里的所有Follower都從Leader復制過去才會被認為已提交。這樣就避免了部分數據被寫進了Leader,還沒來得及被任何Follower復制就宕機了,而造成數據丟失(Consumer無法消費這些數據)。而對于Producer而言,它可以選擇是否等待消息commit。這種機制確保了只要ISR有一個或以上的Follower,一條被commit的消息就不會丟失。

3. 數據同步

Kafka在0.8版本前沒有提供Partition的Replication機制,一旦Broker宕機,其上的所有Partition就都無法提供服務,而Partition又沒有備份數據,數據的可用性就大大降低了。所以0.8后提供了Replication機制來保證Broker的failover。

引入Replication之后,同一個Partition可能會有多個Replica,而這時需要在這些Replication之間選出一個Leader,Producer和Consumer只與這個Leader交互,其它Replica作為Follower從Leader中復制數據。

4. leader選舉

Leader選舉本質上是一個分布式鎖,有兩種方式實現基于ZooKeeper的分布式鎖:

節點名稱唯一性:多個客戶端創建一個節點,只有成功創建節點的客戶端才能獲得鎖

臨時順序節點:所有客戶端在某個目錄下創建自己的臨時順序節點,只有序號最小的才獲得鎖

Majority Vote的選舉策略和ZooKeeper中的Zab選舉是類似的,實際上ZooKeeper內部本身就實現了少數服從多數的選舉策略。kafka中對于Partition的leader副本的選舉采用了第一種方法:為Partition分配副本,指定一個ZNode臨時節點,第一個成功創建節點的副本就是Leader節點,其他副本會在這個ZNode節點上注冊Watcher監聽器,一旦Leader宕機,對應的臨時節點就會被自動刪除,這時注冊在該節點上的所有Follower都會收到監聽器事件,它們都會嘗試創建該節點,只有創建成功的那個follower才會成為Leader(ZooKeeper保證對于一個節點只有一個客戶端能創建成功),其他follower繼續重新注冊監聽事件。

Kafka消息分組,消息消費原理

同一Topic的一條消息只能被同一個Consumer Group內的一個Consumer消費,但多個Consumer Group可同時消費這一消息。

這是Kafka用來實現一個Topic消息的廣播(發給所有的Consumer)和單播(發給某一個Consumer)的手段。一個Topic可以對應多個Consumer Group。如果需要實現廣播,只要每個Consumer有一個獨立的Group就可以了。要實現單播只要所有的Consumer在同一個Group里。用Consumer Group還可以將Consumer進行自由的分組而不需要多次發送消息到不同的Topic。

Push vs. Pull

作為一個消息系統,Kafka遵循了傳統的方式,選擇由Producer向broker push消息并由Consumer從broker pull消息。

push模式很難適應消費速率不同的消費者,因為消息發送速率是由broker決定的。push模式的目標是盡可能以最快速度傳遞消息,但是這樣很容易造成Consumer來不及處理消息,典型的表現就是拒絕服務以及網絡擁塞。而pull模式則可以根據Consumer的消費能力以適當的速率消費消息。

對于Kafka而言,pull模式更合適。pull模式可簡化broker的設計,Consumer可自主控制消費消息的速率,同時Consumer可以自己控制消費方式——即可批量消費也可逐條消費,同時還能選擇不同的提交方式從而實現不同的傳輸語義。

Kafak順序寫入與數據讀取

生產者(producer)是負責向Kafka提交數據的,Kafka會把收到的消息都寫入到硬盤中,它絕對不會丟失數據。為了優化寫入速度Kafak采用了兩個技術,順序寫入和MMFile。

順序寫入

因為硬盤是機械結構,每次讀寫都會尋址,寫入,其中尋址是一個“機械動作”,它是最耗時的。所以硬盤最“討厭”隨機I/O,最喜歡順序I/O。為了提高讀寫硬盤的速度,Kafka就是使用順序I/O。

每條消息都被append到該Partition中,屬于順序寫磁盤,因此效率非常高。

對于傳統的message queue而言,一般會刪除已經被消費的消息,而Kafka是不會刪除數據的,它會把所有的數據都保留下來,每個消費者(Consumer)對每個Topic都有一個offset用來表示讀取到了第幾條數據。

即便是順序寫入硬盤,硬盤的訪問速度還是不可能追上內存。所以Kafka的數據并不是實時的寫入硬盤,它充分利用了現代操作系統分頁存儲來利用內存提高I/O效率。

在Linux Kernal 2.2之后出現了一種叫做“零拷貝(zero-copy)”系統調用機制,就是跳過“用戶緩沖區”的拷貝,建立一個磁盤空間和內存空間的直接映射,數據不再復制到“用戶態緩沖區”系統上下文切換減少2次,可以提升一倍性能。

通過mmap,進程像讀寫硬盤一樣讀寫內存(當然是虛擬機內存)。使用這種方式可以獲取很大的I/O提升,省去了用戶空間到內核空間復制的開銷(調用文件的read會把數據先放到內核空間的內存中,然后再復制到用戶空間的內存中。)

消費者(讀取數據)

試想一下,一個Web Server傳送一個靜態文件,如何優化?答案是zero copy。傳統模式下我們從硬盤讀取一個文件是這樣的。

先復制到內核空間(read是系統調用,放到了DMA,所以用內核空間),然后復制到用戶空間(1、2);從用戶空間重新復制到內核空間(你用的socket是系統調用,所以它也有自己的內核空間),最后發送給網卡(3、4)。

Zero Copy中直接從內核空間(DMA的)到內核空間(Socket的),然后發送網卡。這個技術非常普遍,Nginx也是用的這種技術。

實際上,Kafka把所有的消息都存放在一個一個的文件中,當消費者需要數據的時候Kafka直接把“文件”發送給消費者。當不需要把整個文件發出去的時候,Kafka通過調用Zero Copy的sendfile這個函數,這個函數包括:

out_fd作為輸出(一般及時socket的句柄)

in_fd作為輸入文件句柄

off_t表示in_fd的偏移(從哪里開始讀取)

size_t表示讀取多少個

小編自己有整理更多大數據相關資料

關注我;轉發文章;++我威信 bmaaa01,獲取。


?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,406評論 6 538
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,034評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,413評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,449評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,165評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,559評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,606評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,781評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,327評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,084評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,278評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,849評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,495評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,927評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,172評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,010評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,241評論 2 375

推薦閱讀更多精彩內容