MessageQuene是干嘛的?
簡單來說就是用來分發消息用的。它的出現并非為了提高性能,加速消息傳輸。
消息隊列提供了數據上的冗余,但它不是一種緩存。
如果只是為了加速傳輸,直接把producer和consumer結合一起,中間放一個全內存的quene,不用網絡傳輸,沒有持久化,那豈不是更快。
也并非只是消息的存放源,如果只是存放消息,也許用數據庫和redis會更快。
對MessageQuene最好的詮釋:"fire and forget"(來自active mq文檔),中文叫解耦。
它有效實現了producer和consumer的解耦,降低了系統的復雜性。
生產者只需要關系自己的生產工作,不需要關心自己生產的東西被誰消費,如何消費。它應該簡單地把東西生產好,往倉庫一放(fire),然后就不用管了(forget)。后面的如何交付消費者,是否丟失消息之類的不用再管。
負責與消費者打交道,交付的可靠性問題,這個由MessageQuene來處理。消息隊列就是一個接盤手。
kafka存儲特點:
與傳統的JMS不一樣,它不是為了實現JMS而設計,它追求分布式,高可用和并發性能而生。
kafka集群會保存所有發布的消息,無論是否確認被消費者接收了。所有的這些都會作為log保存起來,好幾天才會刪,
最神奇的是:完全不會因為這種持久化而導致性能變差。
這跟kafka的log存儲方式有關系,數據文件(.index)配合索引文件(.log)的形式來查詢,所以每次都是通過offset對消息進行讀取。基本都只需要恒定次數的尋址就可以完成。
上圖中,左邊為index文件,其中存儲的是k-v格式的鍵值對,key是消息在對應log文件的position(如1,3,6,8...)
這些序號并不是連續的,這種方式叫稀疏存儲,并沒有為每條消息都在index文件中建立索引,
而是每隔一定字節的數據建立索引。這樣是為了避免索引文件占用過多的空間,從而可以把索引文件保存在內存中(通過nmap直接內存操作,時間換空間)。
缺點就是:沒有建立索引的message不能一次定位,需要進行一次順序掃描,只是這次的范圍會很小。
例如:index文件元數據3,497,3代表在log文件順序第3條(全局partiton為第368772條消息),497為消息的物理偏移位置(內存的地址)。
Segment file是什么?
生產者生產的消息按照分組策略被發送到broker的partition的時候,這些消息在內存放不下會存放到磁盤的文件中,物理上,partition包含多個segment。每個segment的消息數量不一定相等,這樣的特性方便segment file被快速刪除,它的生命周期由服務端配置決定。
partition在磁盤就是一個目錄,目錄名=topic名+序號。在這個目錄下,有兩類文件:1,log后綴的文件 2,index后綴的文件。
每一個log文件和index文件相對應,這一對文件就是segment file,也就是一個段。
log文件存放數據的,就是消息。
index文件是索引文件,存放相關metadata。
index文件存放大量的元數據,而log文件則存儲大量的消息。元數據是指向對應的log文件中消息的物理偏移地址(如消費到第18條,offset=10,在第二個文件,指向地址=8)。
上圖為Segment文件的命名規則。第2個命名:多個0+(offset=368769),即上一個文件最后一條消息的序號。offset是消息在partition唯一標識,可以認為是增量序列索引。
這樣命名的好處是顯而易見的!
假如一個消費者消費到了第368770消息(offset=368770),要繼續消費,怎么做?
1,從所有log文件找出對應的文件,第368770條消息位于00000000000000368769.log這個文件中,這一步采用的就是常見的“二分法查找”,比較快速定位到所在文件。
2.然后再根據當前segment的最小offset值取得差值(368770-368769),就是它在.log的相對位置,再去讀取即可
如何保證消息消費的有序性呢?
答案:沒辦法。
因為消息會被分組到不同的broker,大多數情況下都沒法做到全局的有序性。保證有序性的前提是,有且只有一個partition。
那怎么去分布式,負載均衡?所以kafka消息的全局有序,其實是個偽命題。我們只能保證當前的partition的有序性。
針對一個topic里面的數據,只能做到partition內部的有序。
kafka極少進行進行大量的讀磁盤操作,主要定期進行寫磁盤操作,此操作的效率很高。這跟kafka的讀寫設計息息相關
kafka讀寫消息特點:
寫:
消息從java heap轉入page cache(ram)
由異步線程刷盤(flush),消息從ram刷入磁盤(rom)
讀:
先查找page cache,有的話直接socket發送出去
沒有找到則會進行磁盤I/O操作,從磁盤加載消息到page cache,然后直接socket發送出去。
kafka高效文件存儲的設計特點:
topic中一個parition大文件分若干segment file小文件,很容易定期清除已經消費完的文件。減少磁盤占用。
通過索引可快速定位msg。
通過index元數據全部映射memory,可以避免segment file的IO磁盤操作。
通過索引文件稀疏存儲,大幅降低索引文件占用磁盤空間的大小。
總之,kafka的消息存儲采用分區(partition),分段(Segment),和稀疏存儲來達到高效性。