Kafka為什么快到根本停不下來?
目前來說市面上可以選擇的消息隊(duì)列非常多,像 ActiveMQ,RabbitMQ,ZeroMQ 已經(jīng)被大多數(shù)人耳熟能詳。
特別像 ActiveMQ 早期應(yīng)用在企業(yè)中的總線通信,基本作為企業(yè)級(jí) IT 設(shè)施解決方案中不可或缺的一部分。
目前 Kafka 已經(jīng)非常穩(wěn)定,并且逐步應(yīng)用更加廣泛,已經(jīng)算不得新生事物,但是不可否認(rèn) Kafka 一枝獨(dú)秀如同雨后春筍,非常耀眼,今天我們仔細(xì)分解一下 Kafka,了解一下它的內(nèi)幕。
以下的內(nèi)容版本基于當(dāng)前最新的 Kafka 穩(wěn)定版本 2.4.0。文章主要包含以下內(nèi)容:
- Kafka 為什么快
- Kafka 為什么穩(wěn)
- Kafka 該怎么用
該文章為開篇引導(dǎo)之做,后續(xù)會(huì)有對(duì)應(yīng)的 HBase,Spark,Kylin,Pulsar 等相關(guān)組件的剖析。
Kafka 為什么快
快是一個(gè)相對(duì)概念,沒有對(duì)比就沒有傷害,因此通常我們說 Kafka 是相對(duì)于我們常見的 ActiveMQ,RabbitMQ 這類會(huì)發(fā)生 IO,并且主要依托于 IO 來做信息傳遞的消息隊(duì)列。
像 ZeroMQ 這種基本純粹依靠內(nèi)存做信息流傳遞的消息隊(duì)列,當(dāng)然會(huì)更快,但是此類消息隊(duì)列只有特殊場景下會(huì)使用,不在對(duì)比之列。
因此當(dāng)我們說 Kakfa 快的時(shí)候,通常是基于以下場景:
- 吞吐量:當(dāng)我們需要每秒處理幾十萬上百萬 Message 的時(shí)候,相對(duì)其他 MQ,Kafka 處理的更快。
- 高并發(fā):當(dāng)具有百萬以及千萬的 Consumer 的時(shí)候,同等配置的機(jī)器下,Kafka 所擁有的 Producer 和 Consumer 會(huì)更多。
- 磁盤鎖:相對(duì)其他 MQ,Kafka 在進(jìn)行 IO 操作的時(shí)候,其同步鎖住 IO 的場景更少,發(fā)生等待的時(shí)間更短。
那么基于以上幾點(diǎn),我們來仔細(xì)探討一下,為什么 Kafka 就快了。
消息隊(duì)列的推拉模型
首先,如果我們單純站在 Consumer 的角度來看“Kafka 快”,是一個(gè)偽命題,因?yàn)橄啾绕渌?MQ,Kafka 從 Producer 產(chǎn)生一條 Message 到 Consumer 消費(fèi)這條 Message 來看,它的時(shí)間一定是大于等于其他 MQ 的。
背后的原因涉及到消息隊(duì)列設(shè)計(jì)的兩種模型:
- 推模型
- 拉模型
如下圖所示:
對(duì)于拉模型來說,Producer 產(chǎn)生 Message 后,會(huì)主動(dòng)發(fā)送給 MQ Server,為了提升性能和減少開支,部分 Client 還會(huì)設(shè)計(jì)成批量發(fā)送。
但是無論是單條還是批量,Producer 都會(huì)主動(dòng)推送消息到 MQ Server。
當(dāng) MQ Server 接收到消息后,對(duì)于拉模型,MQ Server 不會(huì)主動(dòng)發(fā)送消息到 Consumer,同時(shí)也不會(huì)維持和記錄消息的 Offset,Consumer 會(huì)自動(dòng)設(shè)置定時(shí)器到服務(wù)端去詢問是否有新的消息產(chǎn)生。
通常時(shí)間是不超過 100ms 詢問一次,一旦產(chǎn)生新的消息則會(huì)同步到本地,并且修改和記錄 Offset,服務(wù)端可以輔助存儲(chǔ) Offset,但是不會(huì)主動(dòng)記錄和校驗(yàn) Offset 的合理性。
同時(shí) Consumer 可以完全自主的維護(hù) offset 以便實(shí)現(xiàn)自定義的信息讀取。
對(duì)于推模型來說,服務(wù)端收到 Message 后,首先會(huì)記錄消息的信息,并且從自己的元信息數(shù)據(jù)庫中查詢對(duì)應(yīng)的消息的 Consumer 有誰。
由于服務(wù)器和 Consumer 在鏈接的時(shí)候建立了長鏈接,因此可以直接發(fā)送消息到 Consumer。
Kafka 是基于拉模型的消息隊(duì)列,因此從 Consumer 獲取消息的角度來說,延遲會(huì)小于等于輪詢的周期,所以會(huì)比推模型的消息隊(duì)列具有更高的消息獲取延遲,但是推模型同樣又其問題。
首先,由于服務(wù)器需要記錄對(duì)應(yīng)的 Consumer 的元信息,包括消息該發(fā)給誰,Offset 是多少,同時(shí)需要向 Consumer 推送消息,必然會(huì)帶來系列的問題。
假如這一刻網(wǎng)絡(luò)不好,Consumer 沒有收到,消息沒有發(fā)成功怎么辦?假設(shè)消息發(fā)出去了,我怎么知道它有沒有收到?
因此服務(wù)器和 Consumer 之間需要首先多層確認(rèn)口令,以達(dá)到至少消費(fèi)一次,僅且消費(fèi)一次等特性。
Kafka 此類的拉模型將這一塊功能都交由 Consumer 自動(dòng)維護(hù),因此服務(wù)器減少了更多的不必要的開支。
因此從同等資源的角度來講,Kafka 具備鏈接的 Producer 和 Consumer 將會(huì)更多,極大的降低了消息堵塞的情況,因此看起來更快了。
OS Page Cache 和 Buffer Cache
太陽底下無新鮮事,對(duì)于一個(gè)框架來說,要想運(yùn)行的更快,通常能用的手段也就那么幾招,Kafka 在將這一招用到了極致。
其中之一就是極大化的使用了 OS 的 Cache,主要是 Page Cache 和 Buffer Cache。
對(duì)于這兩個(gè) Cache,使用 Linux 的同學(xué)通常不會(huì)陌生,例如我們在 Linux 下執(zhí)行 free 命令的時(shí)候會(huì)看到如下的輸出:
圖片來自網(wǎng)絡(luò)
會(huì)有兩列名為 buffers 和 cached,也有一行名為“-/+ buffers/cache”,這兩個(gè)信息的具體解釋如下:
pagecache:文件系統(tǒng)層級(jí)的緩存,從磁盤里讀取的內(nèi)容是存儲(chǔ)到這里,這樣程序讀取磁盤內(nèi)容就會(huì)非???,比如使用 Linux 的 grep 和 find 等命令查找內(nèi)容和文件時(shí),第一次會(huì)慢很多,再次執(zhí)行就快好多倍,幾乎是瞬間。
另外 page cache 的數(shù)據(jù)被修改過后,也即臟數(shù)據(jù),等到寫入磁盤時(shí)機(jī)到來時(shí),會(huì)轉(zhuǎn)移到 buffer cache 而不是直接寫入到磁盤。
我們看到的 cached 這列的數(shù)值表示的是當(dāng)前的頁緩存(page cache)的占用量,page cache 文件的頁數(shù)據(jù),頁是邏輯上的概念,因此 page cache 是與文件系統(tǒng)同級(jí)的。
buffer cache:磁盤等塊設(shè)備的緩沖,內(nèi)存的這一部分是要寫入到磁盤里的 。
buffers 列表示當(dāng)前的塊緩存(buffer cache)占用量,buffer cache 用于緩存塊設(shè)備(如磁盤)的塊數(shù)據(jù)。塊是物理上的概念,因此 buffer cache 是與塊設(shè)備驅(qū)動(dòng)程序同級(jí)的。
兩者都是用來加速數(shù)據(jù) IO,將寫入的頁標(biāo)記為 dirty,然后向外部存儲(chǔ) flush,讀數(shù)據(jù)時(shí)首先讀取緩存,如果未命中,再去外部存儲(chǔ)讀取,并且將讀取來的數(shù)據(jù)也加入緩存。
操作系統(tǒng)總是積極地將所有空閑內(nèi)存都用作 Page Cache 和 Buffer Cache,當(dāng) OS 的內(nèi)存不夠用時(shí)也會(huì)用 LRU 等算法淘汰緩存頁。
有了以上概念后,我們再看來 Kafka 是怎么利用這個(gè)特性的。
首先,對(duì)于一次數(shù)據(jù) IO 來說,通常會(huì)發(fā)生以下的流程:
- 操作系統(tǒng)將數(shù)據(jù)從磁盤拷貝到內(nèi)核區(qū)的 Page Cache。
- 用戶程序?qū)?nèi)核區(qū)的 Page Cache 拷貝到用戶區(qū)緩存。
- 用戶程序?qū)⒂脩魠^(qū)的緩存拷貝到 Socket 緩存中。
- 操作系統(tǒng)將 Socket 緩存中的數(shù)據(jù)拷貝到網(wǎng)卡的 Buffer 上,發(fā)送數(shù)據(jù)。
可以發(fā)現(xiàn)一次 IO 請(qǐng)求操作進(jìn)行了 2 次上下文切換和 4 次系統(tǒng)調(diào)用,而同一份數(shù)據(jù)在緩存中多次拷貝,實(shí)際上對(duì)于拷貝來說完全可以直接在內(nèi)核態(tài)中進(jìn)行。
也就是省去第二和第三步驟,變成這樣:
正因?yàn)榭梢匀绱说男薷臄?shù)據(jù)的流程,于是 Kafka 在設(shè)計(jì)之初就參考此流程,盡可能大的利用 OS 的 Page Cache 來對(duì)數(shù)據(jù)進(jìn)行拷貝,盡量減少對(duì)磁盤的操作。
如果 Kafka 生產(chǎn)消費(fèi)配合的好,那么數(shù)據(jù)完全走內(nèi)存,這對(duì)集群的吞吐量提升是很大的。
早期的操作系統(tǒng)中的 Page Cache 和 Buffer Cache 是分開的兩塊 Cache,后來發(fā)現(xiàn)同樣的數(shù)據(jù)可能會(huì)被 Cache 兩次,于是大部分情況下兩者都是合二為一的。
Kafka 雖然使用 JVM 語言編寫,在運(yùn)行的時(shí)候脫離不了 JVM 和 JVM 的 GC,但是 Kafka 并未自己去管理緩存,而是直接使用了 OS 的 Page Cache 作為緩存。
這樣做帶來了以下好處:
- JVM 中的一切皆對(duì)象,所以無論對(duì)象的大小,總會(huì)有些額外的 JVM 的對(duì)象元數(shù)據(jù)浪費(fèi)空間。
- JVM 自己的 GC 不受程序手動(dòng)控制,所以如果使用 JVM 作為緩存,在遇到大對(duì)象或者頻繁 GC 的時(shí)候會(huì)降低整個(gè)系統(tǒng)的吞吐量。
- 程序異常退出或者重啟,所有的緩存都將失效,在容災(zāi)架構(gòu)下會(huì)影響快速恢復(fù)。而 Page Cache 因?yàn)槭?OS 的 Cache,即便程序退出,緩存依舊存在。
所以 Kafka 優(yōu)化 IO 流程,充分利用 Page Cache,其消耗的時(shí)間更短,吞吐量更高,相比其他 MQ 就更快了。
用一張圖來簡述三者之間的關(guān)系如下:
當(dāng) Producer 和 Consumer 速率相差不大的情況下,Kafka 幾乎可以完全實(shí)現(xiàn)不落盤就完成信息的傳輸。
追加順序?qū)懭?/span>
除了前面的重要特性之外,Kafka 還有一個(gè)設(shè)計(jì),就是對(duì)數(shù)據(jù)的持久化存儲(chǔ)采用的順序的追加寫入,Kafka 在將消息落到各個(gè) Topic 的 Partition 文件時(shí),只是順序追加,充分的利用了磁盤順序訪問快的特性。
圖片來自網(wǎng)絡(luò)
Kafka 的文件存儲(chǔ)按照 Topic 下的 Partition 來進(jìn)行存儲(chǔ),每一個(gè) Partition 有各自的序列文件,各個(gè) Partition 的序列不共享,主要的劃分按照消息的 Key 進(jìn)行 Hash 決定落在哪個(gè)分區(qū)之上。
我們先來詳細(xì)解釋一下 Kafka 的各個(gè)名詞,以便充分理解其特點(diǎn):
- Broker:Kafka 中用來處理消息的服務(wù)器,也是 Kafka 集群的一個(gè)節(jié)點(diǎn),多個(gè)節(jié)點(diǎn)形成一個(gè) Kafka 集群。
- Topic:一個(gè)消息主題,每一個(gè)業(yè)務(wù)系統(tǒng)或者 Consumer 需要訂閱一個(gè)或者多個(gè)主題來獲取消息,Producer 需要明確發(fā)生消息對(duì)于的 Topic,等于信息傳遞的口令名稱。
- Partition:一個(gè) Topic 會(huì)拆分成多個(gè) Partition 落地到磁盤,在 Kafka 配置的存儲(chǔ)目錄下按照對(duì)應(yīng)的分區(qū) ID 創(chuàng)建的文件夾進(jìn)行文件的存儲(chǔ),磁盤可以見的最大的存儲(chǔ)單元。
- Segment:一個(gè) Partition 會(huì)有多個(gè) Segment 文件來實(shí)際存儲(chǔ)內(nèi)容。
- Offset:每一個(gè) Partition 有自己的獨(dú)立的序列編號(hào),作用域僅在當(dāng)前的 Partition 之下,用來對(duì)對(duì)應(yīng)的文件內(nèi)容進(jìn)行讀取操作。
- Leader:每一個(gè) Topic 需要有一個(gè) Leader 來負(fù)責(zé)該 Topic 的信息的寫入,數(shù)據(jù)一致性的維護(hù)。
- Controller:每一個(gè) Kafka 集群會(huì)選擇出一個(gè) Broker 來充當(dāng) Controller,負(fù)責(zé)決策每一個(gè) Topic 的 Leader 是誰,監(jiān)聽集群 Broker 信息的變化,維持集群狀態(tài)的健康。
可以看到最終落地到磁盤都是 Segment 文件,每一個(gè) Partion(目錄)相當(dāng)于一個(gè)巨型文件被平均分配到多個(gè)大小相等 Segment(段)數(shù)據(jù)文件中。
但每個(gè)段 segment file 消息數(shù)量不一定相等,這種特性方便老的 segment file 快速被刪除。
因?yàn)?Kafka 處理消息的力度是到 Partition,因此只需要保持好 Partition 對(duì)應(yīng)的順序處理,Segment 可以單獨(dú)維護(hù)其狀態(tài)。
Segment 的文件由 index file 和 data file 組成,落地在磁盤的后綴為 .index 和 .log,文件按照序列編號(hào)生成,如下所示:
圖片來自網(wǎng)絡(luò)
其中 index 維持著數(shù)據(jù)的物理地址,而 data 存儲(chǔ)著數(shù)據(jù)的偏移地址,相互關(guān)聯(lián),這里看起來似乎和磁盤的順序?qū)懭腙P(guān)系不大,想想 HDFS 的塊存儲(chǔ),每次申請(qǐng)固定大小的塊和這里的 Segment?是不是挺相似的?
另外因?yàn)橛?index 文的本身命名是以 Offset 作為文件名的,在進(jìn)行查找的時(shí)候可以快速根據(jù)需要查找的 Offset 定位到對(duì)應(yīng)的文件,再根據(jù)文件進(jìn)行內(nèi)容的檢索。
因此 Kafka 的查找流程為先根據(jù)要查找的 Offset 對(duì)文件名稱進(jìn)行二分查找,找到對(duì)應(yīng)的文件,再根據(jù) index 的元數(shù)據(jù)的物理地址和 log 文件的偏移位置結(jié)合順序讀區(qū)到對(duì)應(yīng) Offset 的位置的內(nèi)容即可。
segment index file 采取稀疏索引存儲(chǔ)方式,它減少索引文件大小,通過 mmap 可以直接內(nèi)存操作,稀疏索引為數(shù)據(jù)文件的每個(gè)對(duì)應(yīng) Message 設(shè)置一個(gè)元數(shù)據(jù)指針。
它比稠密索引節(jié)省了更多的存儲(chǔ)空間,但查找起來需要消耗更多的時(shí)間,特別是在隨機(jī)讀取的場景下,Kafka 非常不合適。所以因?yàn)?Kafka 特殊的存儲(chǔ)設(shè)計(jì),也讓 Kafka 感覺起來,更快。
Kafka 為什么穩(wěn)
前面提到 Kafka 為什么快,除了快的特性之外,Kafka 還有其他特點(diǎn),那就是:穩(wěn)。
Kafka 的穩(wěn)體現(xiàn)在幾個(gè)維度:
- 數(shù)據(jù)安全,幾乎不會(huì)丟數(shù)據(jù)。
- 集群安全,發(fā)生故障幾乎可以 Consumer 無感知切換。
- 可用性強(qiáng),即便部分 Partition 不可用,剩余的 Partition 的數(shù)據(jù)依舊不影響讀取。
- 流控限制,避免大量 Consumer 拖垮服務(wù)器的帶寬。
限流機(jī)制
對(duì)于 Kafka 的穩(wěn),通常是由其整體架構(gòu)設(shè)計(jì)決定,很多優(yōu)秀的特性結(jié)合在一起,就更加的優(yōu)秀,像 Kafka 的 Qutota 就是其中一個(gè)。
既然是限流,那就意味著需要控制 Consumer 或者 Producer 的流量帶寬,通常限制流量這件事需要在網(wǎng)卡上作處理,像常見的 N 路交換機(jī)或者高端路由器。
所以對(duì)于 Kafka 來說,想要操控 OS 的網(wǎng)卡去控制流量顯然具有非常高的難度,因此 Kafka 采用了另外一個(gè)特別的思路。
即:沒有辦法控制網(wǎng)卡通過的流量大小,就控制返回?cái)?shù)據(jù)的時(shí)間。對(duì)于 JVM 程序來說,就是一個(gè) Wait 或者 Seelp 的事情。
所以對(duì)于 Kafka 來說,有一套特殊的時(shí)延計(jì)算規(guī)則,Kafka 按照一個(gè)窗口來統(tǒng)計(jì)單位時(shí)間傳輸?shù)牧髁俊?/p>
當(dāng)流量大小超過設(shè)置的閾值的時(shí)候,觸發(fā)流量控制,將當(dāng)前請(qǐng)求丟入 Kafka 的 Qutota Manager,等到延遲時(shí)間到達(dá)后,再次返回?cái)?shù)據(jù)。
我們通過 Kafka 的 ClientQutotaManager 類中的方法來看:
這幾行代碼代表了 Kafka 的限流計(jì)算邏輯,大概的思路為:假設(shè)我們設(shè)定當(dāng)前流量上限不超過 T,根據(jù)窗口計(jì)算出當(dāng)前的速率為 O。
如果 O 超過了 T,那么會(huì)進(jìn)行限速,限速的公示為:
- X = (O - T)/ T * W
X 為需要延遲的時(shí)間,讓我舉一個(gè)形象的例子,假設(shè)我們限定流量不超過 10MB/s,過去 5 秒(公示中的 W,窗口區(qū)間)內(nèi)通過的流量為 100MB,則延遲的時(shí)間為:(100-5*10)/10=5 秒。
這樣就能夠保障在下一個(gè)窗口運(yùn)行完成后,整個(gè)流量的大小是不會(huì)超過限制的。
通過 KafkaApis 里面對(duì) Producer 和 Consumer 的 call back 代碼可以看到對(duì)限流的延遲返回:
對(duì)于 Kafka 的限流來講,默認(rèn)是按照 client id 或者 user 來進(jìn)行限流的,從實(shí)際使用的角度來說,意義不是很大,基于 Topic 或者 Partition 分區(qū)級(jí)別的限流,相對(duì)使用場景更大。
競選機(jī)制
Kafka 背后的元信息重度依賴 Zookeeper,再次我們不解釋 Zookeeper 本身,而是關(guān)注 Kafka 到底是如何使用 ZK 的。
首先一張圖解釋 Kafka 對(duì) ZK 的重度依賴:
圖片來源于網(wǎng)絡(luò)
利用 ZK 除了本身信息的存儲(chǔ)之外,最重要的就是 Kafka 利用 ZK 實(shí)現(xiàn)選舉機(jī)制,其中以 Controller 為主要的介紹。
首先 Controller 作為 Kafka 的心臟,主要負(fù)責(zé)著包括不限于以下重要事項(xiàng):
也就是說 Controller 是 Kafka 的核心角色,對(duì)于 Controller 來說,采用公平競爭,任何一個(gè) Broker 都有可能成為 Controller,保障了集群的健壯性。
對(duì)于 Controller 來說,其選舉流程如下:
①先獲取 ZK 的 /Cotroller 節(jié)點(diǎn)的信息,獲取 Controller 的 broker id,如果該節(jié)點(diǎn)不存在(比如集群剛創(chuàng)建時(shí)),* 那么獲取的 controller id 為 -1。
②如果 controller id 不為 -1,即 Controller 已經(jīng)存在,直接結(jié)束流程。
③如果 controller id 為 -1,證明 Controller 還不存在,這時(shí)候當(dāng)前 Broker 開始在 ZK 注冊 Controller。
④如果注冊成功,那么當(dāng)前 Broker 就成為了 Controller,這時(shí)候開始調(diào)用 onBecomingLeader() 方法,正式初始化 Controller。
注意:Controller 節(jié)點(diǎn)是臨時(shí)節(jié)點(diǎn),如果當(dāng)前 Controller 與 ZK 的 Session 斷開,那么 Controller 的臨時(shí)節(jié)點(diǎn)會(huì)消失,會(huì)觸發(fā) Controller 的重新選舉。
⑤如果注冊失敗(剛好 Controller 被其他 Broker 創(chuàng)建了、拋出異常等),那么直接返回。
其代碼直接通過 KafkaController 可以看到:
一旦 Controller 選舉出來之后,則其他 Broker 會(huì)監(jiān)聽 ZK 的變化,來響應(yīng)集群中 Controller 掛掉的情況:
從而觸發(fā)新的 Controller 選舉動(dòng)作。對(duì)于 Kafka 來說,整個(gè)設(shè)計(jì)非常緊湊,代碼質(zhì)量相當(dāng)高,很多設(shè)計(jì)也非常具有借鑒意義,類似的功能在 Kafka 中有非常多的特性體現(xiàn),這些特性結(jié)合一起,形成了 Kafka 整個(gè)穩(wěn)定的局面。
Kafka 該怎么用
雖然 Kafka 整體看起來非常優(yōu)秀,但是 Kafka 也不是全能的銀彈,必然有其對(duì)應(yīng)的短板,那么對(duì)于 Kafka 如何,或者如何能用的更好,則需要經(jīng)過實(shí)際的實(shí)踐才能得感悟的出。
經(jīng)過歸納和總結(jié),能夠發(fā)現(xiàn)以下不同的使用場景和特點(diǎn):
①Kafka 并不合適高頻交易系統(tǒng)
Kafka 雖然具有非常高的吞吐量和性能,但是不可否認(rèn),Kafka 在單條消息的低延遲方面依舊不如傳統(tǒng) MQ,畢竟依托推模型的 MQ 能夠在實(shí)時(shí)消息發(fā)送的場景下取得先天的優(yōu)勢。
②Kafka 并不具備完善的事務(wù)機(jī)制
0.11 之后 Kafka 新增了事務(wù)機(jī)制,可以保障 Producer 的批量提交,為了保障不會(huì)讀取到臟數(shù)據(jù),Consumer 可以通過對(duì)消息狀態(tài)的過濾過濾掉不合適的數(shù)據(jù),但是依舊保留了讀取所有數(shù)據(jù)的操作。
即便如此,Kafka 的事務(wù)機(jī)制依舊不完備,背后主要的原因是 Kafka 對(duì) Client 并不感冒,所以不會(huì)統(tǒng)一所有的通用協(xié)議,因此在類似僅且被消費(fèi)一次等場景下,效果非常依賴于客戶端的實(shí)現(xiàn)。
③Kafka 的異地容災(zāi)方案非常復(fù)雜
對(duì)于 Kafka 來說,如果要實(shí)現(xiàn)跨機(jī)房的無感知切換,就需要支持跨集群的代理。
因?yàn)?Kafka 特殊的 append log 的設(shè)計(jì)機(jī)制,導(dǎo)致同樣的 Offset 在不同的 Broker 和不同的內(nèi)容上無法復(fù)用。
也就是文件一旦被拷貝到另外一臺(tái)服務(wù)器上,將不可讀取,相比類似基于數(shù)據(jù)庫的 MQ,很難實(shí)現(xiàn)數(shù)據(jù)的跨集群同步。
同時(shí)對(duì)于 Offset 的復(fù)現(xiàn)也非常難,曾經(jīng)幫助客戶實(shí)現(xiàn)了一套跨機(jī)房的 Kafka 集群 Proxy,投入了非常大的成本。
④Kafka Controller 架構(gòu)無法充分利用集群資源
Kafka Controller 類似于 ES 的去中心化思想,按照競選規(guī)則從集群中選擇一臺(tái)服務(wù)器作為 Controller。
意味著改服務(wù)器即承擔(dān)著 Controller 的職責(zé),同時(shí)又承擔(dān)著 Broker 的職責(zé),導(dǎo)致在海量消息的壓迫下,該服務(wù)器的資源很容易成為集群的瓶頸,導(dǎo)致集群資源無法最大化。
Controller 雖然支持 HA 但是并不支持分布式,也就意味著如果要想 Kafka 的性能最優(yōu),每一臺(tái)服務(wù)器至少都需要達(dá)到最高配置。
⑤Kafka 不具備非常智能的分區(qū)均衡能力
通常在設(shè)計(jì)落地存儲(chǔ)的時(shí)候,對(duì)于熱點(diǎn)或者要求性能足夠高的場景下,會(huì)是 SSD 和 HD 的結(jié)合。
同時(shí)如果集群存在磁盤容量大小不均等的情況,對(duì)于 Kafka 來說會(huì)有非常嚴(yán)重的問題,Kafka 的分區(qū)產(chǎn)生是按照 Paratition 的個(gè)數(shù)進(jìn)行統(tǒng)計(jì),將新的分區(qū)創(chuàng)建在個(gè)數(shù)最少的磁盤上,見下圖:
曾經(jīng)我?guī)椭称髽I(yè)修改了分區(qū)創(chuàng)建規(guī)則,考慮了容量的情況,也就是按照磁盤容量進(jìn)行分區(qū)的選擇。
緊接著帶來第二個(gè)問題:容量大的磁盤具備更多的分區(qū),則會(huì)導(dǎo)致大量的 IO 都?jí)合蛟摫P,最后問題又落回 IO,會(huì)影響該磁盤的其他 Topic 的性能。
所以在考慮 MQ 系統(tǒng)的時(shí)候,需要合理的手動(dòng)設(shè)置 Kafka 的分區(qū)規(guī)則。
結(jié)尾
Kafka 并不是唯一的解決方案,像幾年前新生勢頭挺厲害的 Pulsar,以取代 Kafka 的口號(hào)沖入市場,也許會(huì)成為下一個(gè)解決 Kafka 部分痛點(diǎn)的框架,下文再講述 Pulsar。
作者:白發(fā)川
編輯:陶家龍
出處:轉(zhuǎn)載自微信公眾號(hào) ThoughtWorks 洞見(ID:TW-Insight)