kafka消息存儲設計

消息隊列的引入,什么時候使用MQ?

MQ(Message Queue),一種跨進程的通信機制,用于上下游傳遞消息。能達到解耦、異步、消峰限流的作用。舉幾個對應的適用例子。

解耦

1. 比如定時任務依賴的場景,晚上需要跑一些定時統(tǒng)計任務,任務2依賴任務1的結果,任務3依賴任務2的接口,一般開發(fā)人員會在每個定時任務之間,預留一些時間buffer處理。但是,當某一天其中一個任務超出常規(guī)時間,任務就跑亂套了,第二天肯定就有人來找到你了。這個場景就很適合用MQ去解耦,當任務1完成后,通知到任務2,任務二通過訂閱消息去實現(xiàn)觸發(fā)。

2. 比如統(tǒng)一充值網關服務,某個產品接入統(tǒng)一充值服務的微信渠道充值。充值成功后,微信服務端會通知到統(tǒng)一充值服務端,因為這些是異步的調用,且是公網接口,時間會相對長一些,業(yè)務上產品接入方會有需求想知道,到賬的結果。這個到賬通知就很適合MQ去實現(xiàn)。如果,這里由充值網關服務調用上游來通知結果的話,每次新增調用方,充值網關服務都需要修改代碼發(fā)布,依賴反轉了。充值網關+MQ的方式,業(yè)務調用方去訂閱消息實現(xiàn)解耦。

異步

1.場景上游不關心執(zhí)行結果。異步,rpc框架異步調用也可以。區(qū)別就是MQ消息會落地,并且消息中間件都會有HA的涉及,能保證消息語義的實現(xiàn)(至少一次、至少一次、至多一次)。rpc異步請求本身也會有本地內存隊列,所以數(shù)據(jù)不是要求很重要的場景,差不多。只是在工程上,有一點,使用rpc處理這種業(yè)務,經驗上要單拉出一個服務去調用下游,因為依賴倒置了。每增加接入方或者業(yè)務有修改,都要提供服務的工程去修改發(fā)布(有經驗的同學應該深有體會),作為基礎服務的話,應該減少這種依賴倒置的發(fā)布,獨立出來一個服務處理的話,會減少風險。

業(yè)務的話,處理推薦日志,處理app埋點的統(tǒng)計數(shù)據(jù)

消峰限流

這個應該是MQ另一個最主要的作用之一。做活動訪問量陡增,下游處理不過來的時候,使用消息隊列達到限流的作用。工程上有個C10K問題(雖然現(xiàn)在已經有現(xiàn)在我們早已經突破了C10K這個瓶頸,具體的思路就是通過單個進程或線程服務于多個客戶端請求,通過異步編程和事件觸發(fā)機制替換輪訓,IO 采用非阻塞的方式(reactor模型),減少不必要的性能損耗,等等)。但是這個要求下游的服務包括存儲和依賴,都要做到這點,哪個環(huán)節(jié)弱都不行。可能還會浪費資源,平時的量用不著那么多服務器。

相應的,那些調用方需要被調用方立刻返回結果的需求,就不適用于MQ,需要根據(jù)業(yè)務去考慮,脫離了業(yè)務去引入新技術就是耍流氓。


業(yè)務上有對消息組件的需求后,市面上陸續(xù)出現(xiàn)了很多成熟的消息中間件

IBM webSphere MQApache ActiveMQ、LinkedIn Kafka、阿里 rocketMQ、

java社區(qū)肯定要跟著一起玩的,社區(qū)也定義jms規(guī)范(JMS規(guī)范百度詞條maid。這些消息組件,前兩個是基于jms規(guī)范實現(xiàn)的,后兩個沒有,rocketMQ開始是kafka的java版實現(xiàn),現(xiàn)在已經從Apache社區(qū)正式畢業(yè)(17-0925),成為Apache頂級項目。在原有設計的基礎上,比如提供事務消息等一些功能,kafka0.11版開始也提供可事務的支持,還沒發(fā)布太久,效果還有待觀察;但是都用別的方式實現(xiàn)了jms定義的一些功能,比如發(fā)布訂閱,點對點通信。


如何設計實現(xiàn)一個消息隊列:

實現(xiàn)一個消息組件不可避免的要處理如下問題(消息中間件精要設計):

1.通信協(xié)議的選擇

2.消息的分布式存儲設計,關系型數(shù)據(jù)庫 磁盤 kv存儲

3.如何分布式設計生產者和消費者保證高吞吐

4.如何實現(xiàn)順序消費

5.如何保證HA,在高吞吐和HA上做平衡

6.事務消息的支持

7.push還是pull

8.單播、廣播、訂閱發(fā)布的實現(xiàn)

9.性能的優(yōu)化,同步異步、批量等

以上問題有交叉,以這些問題出發(fā),看看kafka是如何設計實現(xiàn)的。


一.kafka基本概念的介紹

1.topics 主題,隊列的邏輯概念。可以有多個producer生產消息往topic發(fā)送,有個consumer從topic消費消息。

topic結構圖

topic有partition組成,partition個數(shù)有server.property配置文件指定,partition的服務器的分布,會由算法均分到不同的服務器上。每個partition上的消息是有序的不可變的。


partition上生產消費情況

生產的消息,是以log的文件格式存儲在服務器端。具體格式下邊會單獨再說下。每條消息有一個位移信息offset,生產者在隊列尾添加消息,offset+1。生產者消費消息的進度位置也是用offset標記,消費后,消息是不刪除的,所以可以指定offset重新消費。offset的值的提交存儲是放到客戶端完成的所以服務端是無消費狀態(tài)的

offset的存儲位置老版本是放到zookeeper上,考慮到集群topic很多的話,zookeeper的讀寫操作很頻繁,zookeeper是不適合有大量寫入操作的。所以新版本把offset存儲到服務器端一個單獨的topic下__consumer-offsets。這個topic默認50個分區(qū)

offset存儲位置__consumer-offsets

2.producer

生產者往指定的topic發(fā)消息,生產者發(fā)消息到不同的partition上算法有根據(jù)key值hash、輪訓和新版本最新的算法,也可以自己實現(xiàn)指定partition的策略。

生產者如何指定發(fā)送的partition?如何指定發(fā)送的策略?

3.consumer and consumer group

consumer group,consumer,partition的關系

1.消費者消費topic,必須指定consumer group,其中配置文件group.id唯一標識一個consumer group

2.topic上可以有多個consumer group去訂閱,kafka使用這個概念實現(xiàn)JMS規(guī)范里邊發(fā)布訂閱功能,即不同的接入方想實現(xiàn)訂閱功能,只需要指定不同的consumer group即可。

3.consumer group上可以有多個消費者,并且一個partition只有consumer group的其中一個consumer在消費。一個consumer可以消費多個partition

看到后會想到的問題:consumer group里的consumer如何分配消費partition的關系?

4.replica 副本

HA設計的概念,副本對應的是partition的概念。每個partition的副本個數(shù),在配置文件有指定。如果有3機器,3副本能保證(3-1)個server fail的情況下,不丟消息。見kafka的HA設計

問題:kafka是如何利用replica概念設計HA的?


二.服務端消息log存儲設計

選擇的是磁盤文件的持久化方式,沒有提供不持久化的選擇。

3個partition文件夾

可以看到kafka10-topic-20170924有三個文件夾,每個文件夾代表一個分區(qū),每個分區(qū)下的存儲由segment組成,一個segment包括index索引文件,時間戳index索引文件和實際存儲數(shù)據(jù)的log文件組成。

每個log文件的大小默認是10M(可配置),超過10M,新建文件,文件的名字是第一個消息的offset值。

為什么這樣設計?好處是什么?

segment結構
segment結構
索引文件和log文件關系

索引文件的結構是一個map,key是當前segment的offset的偏移量,從0開始。value是對應的log文件中消息開始位置的實際物理位置偏移量。索引文index file采取稀疏索引存儲方式,它減少索引文件大小,通過mmap可以直接內存操作,稀疏索引為數(shù)據(jù)文件的每個對應message設置一個元數(shù)據(jù)指針,它比稠密索引節(jié)省了更多的存儲空間,但查找起來需要消耗更多的時間。

舉個??,消息的查找過程,比如offset的值是368772,如何查找消費對應消息內容。

1.根據(jù)offset找到所在的segment,根據(jù)二分查找,找到消息所在的log文件0000000000000368769.log和索引文件0000000000000368769.index

2.計算下差368772-368769=3,在索引文件中也是二分查找,定位到是<3,497>記錄,即對應的物理位置是497,從而找到消息

3.根據(jù)物理位置497在0000000000000368769.log文件找到消息。

問題:如果稀疏索引沒有找到怎么辦?

如果是索引文件沒有命中怎么辦。這就要繼續(xù)在看下每條log的消息格式:

每條消息日志的協(xié)議格式

字段解釋:

offset:8bytes長度的偏移量,唯一表示一條消息

message length :消息長度

crc:CRC32校驗消息

magic value:標示是否允許格式化改變

attributes:bit 0~2:壓縮方法,0沒有壓縮 1gzip 2 snappy 3 lz4;bit 3:時間戳類型,0創(chuàng)建時間 1日志的追加時間;bit 4~7預留

timestamp:當時magic value的值是1是,有效。表示時間戳。這個新版本引入的字段

key length:消息key的長度

key:key的值

value length:消息內容的長度

value:消息的具體內容

所以沒有索引到的查找,就先根據(jù)二分找到最近的一條內容,然后根據(jù)每條消息的格式,知道消息的長度。依次計算出下一條消息的位置,直到找到offset相等的那條記錄。

服務器上的日志文件是二進制的,kafka提供很多方便的腳本工具,可以使用kafka的工具類DumpLogSegments類解析查看一下結構如圖

log二進制文件轉換后的結構輸出

轉換腳本指令,要加上--print-data-log參數(shù),不加的話,默認不輸出key值和value值。其中圖中payload是value值。

./kafka-run-class.sh? kafka.tools.DumpLogSegments --print-data-log --files /tmp/kafka-logs/kafka10-topic-20170924-0/000000000000000000.log

同樣的看下offset索引文件和時間戳索引文件,嗯,跟官網文檔描述的一樣,就放心了。

offset索引文件
時間戳索引文件
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 背景介紹 Kafka簡介 Kafka是一種分布式的,基于發(fā)布/訂閱的消息系統(tǒng)。主要設計目標如下: 以時間復雜度為O...
    高廣超閱讀 12,884評論 8 167
  • 本文轉載自http://dataunion.org/?p=9307 背景介紹Kafka簡介Kafka是一種分布式的...
    Bottle丶Fish閱讀 5,491評論 0 34
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,923評論 18 139
  • Kafka系列一- Kafka背景及架構介紹 Kafka簡介 Kafka是一種分布式的,基于發(fā)布/訂閱的消息系統(tǒng)。...
    raincoffee閱讀 2,229評論 0 22
  • 孔子曰,食色,性也。世界上所有的雄性動物都好色,這是由雄性荷爾蒙激素導致的。好色不是貶義詞,好是喜歡,色是...
    言行合一閱讀 849評論 0 1