目前,Apache Kafka已成為了應(yīng)用服務(wù)間通信的常見(jiàn)選擇。Kafka不但能夠通過(guò)消息并行處理的方式來(lái)聚合日志,而且能夠應(yīng)對(duì)低延遲、??高吞吐量??的需求。不過(guò),對(duì)于許多微服務(wù)應(yīng)用而言,Kafka的處理速度卻不一定夠快。

最近,異軍突起的開(kāi)源式??Chronicle Queue??卻可以被用來(lái)開(kāi)發(fā)一些只有微秒級(jí)延遲的消息傳遞框架。下面,我將和您深入地從微服務(wù)應(yīng)用的吞吐量和可擴(kuò)展性方面,比較Kafka與Chronicle Queue。
將延遲類比為距離
為了說(shuō)明延遲,讓我們來(lái)做一個(gè)類比:通常,光線會(huì)以大約三分之二的光速在真空的光纖和銅線中傳輸,由此產(chǎn)生的瞬間延遲,可以被理解為信號(hào)在這段時(shí)間內(nèi)所傳播的距離。
使用Chronicle的微服務(wù)延遲
Chronicle Queue的企業(yè)版(Enterprise)在500k msg/s的低吞吐量下,99%ile(正態(tài)分布中的平均值)的單個(gè)微服務(wù)的端到端延遲為3.69微秒。這等同于信號(hào)傳播了750米的距離,也就是普通人在倫敦市中心步行10分鐘的直線距離(請(qǐng)參見(jiàn)下圖)。

使用Kafka的微服務(wù)延遲
如果使用Kafka進(jìn)行相同的測(cè)試,那么在100k 消息/秒(msg/s)的低吞吐量下,99%ile的單個(gè)微服務(wù)的端到端延遲約為2633微秒(如果是150k msg/s的話,延遲則會(huì)顯著增加)。這等同于信號(hào)傳播了526公里,也就是普通人花費(fèi)100多個(gè)小時(shí),從倫敦步行到達(dá)蘇格蘭的鄧弗里斯(Dumfries)。

日志聚合
針對(duì)日志聚合的需求,Kafka在最初的設(shè)計(jì)中就能夠提供多個(gè)連接器。因此,在典型的系統(tǒng)中,使用Kafka代替寫(xiě)入日志文件,能夠達(dá)到提高性能、并顯著提高可管理性的效果。
測(cè)試場(chǎng)景
我在Ryzen 9 5950X服務(wù)器上部署并運(yùn)行著Ubuntu 21.04。為了保持一致性,所有測(cè)試均會(huì)使用相同的MP600 PRO XT 2TB M.2 NVMe驅(qū)動(dòng)。您可以通過(guò)鏈接--https://github.com/OpenHFT/Microservice-Benchmark,獲取基準(zhǔn)測(cè)試的源代碼。
開(kāi)源式Chronicle Queue v5.22ea14會(huì)使用Chronicle Wire進(jìn)行序列化,并以500k msg/s的速度寫(xiě)入。您可以針對(duì)單個(gè)生產(chǎn)者(Producer),以及下游的單個(gè)消費(fèi)者(Consumer)進(jìn)行如下配置:
-Dworkload=500kps.yaml chronicle.yaml
Chronicle Queue企業(yè)版v2.22ea72也使用Chronicle Wire進(jìn)行序列化,并以500k msg/s的速度寫(xiě)入。您可以針對(duì)異步緩沖區(qū)模式下的單個(gè)生產(chǎn)者,以及下游的單個(gè)消費(fèi)者,進(jìn)行如下配置:
-Dworkload=500kps.yaml chronicle-async.yaml
而帶有Jackson的Kafka 3.0.0在高吞吐量延遲的配置(主要是指linger.ms=1)下,可以100k msg/s的速度寫(xiě)入。您可以針對(duì)JSON的4個(gè)分區(qū)和8個(gè)消費(fèi)者,進(jìn)行如下配置:
-Dworkload=100kps.yamlKafka.yaml
帶有Jackson的Kafka 3.0.0在高吞吐量延遲的配置(主要是指linger.ms=1)下,則可以250k msg/s的速度寫(xiě)入。您可以針對(duì)JSON的4個(gè)分區(qū)和8個(gè)消費(fèi)者,進(jìn)行如下配置:
-Dworkload=250kps.yamlKafka.yaml
比較
分區(qū)和消費(fèi)者的數(shù)量會(huì)在一定程度上影響到延遲。對(duì)于Chronicle Queue而言,在100k msg/s和500k msg/s下的性能表現(xiàn)大致相同。也就是說(shuō),Chronicle Queue的一項(xiàng)重要特征便是:性能基本不會(huì)受到發(fā)布者和消費(fèi)者數(shù)量的影響。因此,我們針對(duì)500k msg/s的需求,采取一個(gè)發(fā)布者(publisher)、一個(gè)消費(fèi)者(consumer)和一個(gè)微服務(wù)。
如果以500k msg/s對(duì)Kafka進(jìn)行基準(zhǔn)測(cè)試,則會(huì)導(dǎo)致消息出現(xiàn)排隊(duì)。而且基準(zhǔn)運(yùn)行的時(shí)間越長(zhǎng),延遲就會(huì)越明顯。例如,一旦出現(xiàn)2分鐘的突發(fā)流量峰值,就會(huì)導(dǎo)致接近1分鐘的延遲。
而如果想讓Kafka以250k msg/s的水平運(yùn)行,則至少需要4個(gè)消費(fèi)者。當(dāng)然,如果設(shè)置8個(gè)消費(fèi)者的基準(zhǔn),那么效果會(huì)更好,畢竟它會(huì)調(diào)用到Kafka的擴(kuò)展技術(shù)。
發(fā)布延遲

上圖比較了兩者在發(fā)布上的延遲。僅從圖表看來(lái),它們的差異可能十分明顯,但是在實(shí)際測(cè)試用例中,其間的延遲不會(huì)超過(guò)2.6微秒。就測(cè)試用例而言,其代碼如下。它在不同情況所發(fā)布的事件,都是512字節(jié)的JSON消息。而在消息被發(fā)送時(shí),我們添加了兩個(gè)字段以進(jìn)行跟蹤。

微服務(wù)消息傳輸
雖然我們?cè)谏厦嬗懻摰陌l(fā)布時(shí)間、以及收發(fā)預(yù)序列化消息的時(shí)間,能夠很好地比較Kafka與Chronicle這兩個(gè)消息傳遞方案。但是這只是延遲難題的一部分。對(duì)于微服務(wù)而言,您需要知道從描述待處理事件的DTO(數(shù)據(jù)傳輸對(duì)象)開(kāi)始,到下游消費(fèi)者從原始微服務(wù)中讀取生成的DTO的時(shí)間。對(duì)此,我們需要通過(guò)針對(duì)微服務(wù)的基準(zhǔn)測(cè)試,來(lái)獲悉如下發(fā)送相同事件的各個(gè)端到端階段的用時(shí):
- 添加高精度的時(shí)間戳 (System.nanoTime())
- 序列化第一條消息
- 發(fā)布第一條消息
- 消費(fèi)第一條消息
- 反序列化第一條消息
- 調(diào)用微服務(wù)
- 添加第二個(gè)高精度時(shí)間戳
- 序列化另一個(gè)主題/隊(duì)列上的第二條消息
- 發(fā)布第二條消息
- 消費(fèi)第二條消息
- 反序列化第二條消息
- 記錄端到端延遲
注意:每條消息在生成時(shí),都會(huì)創(chuàng)建第二條消息作為響應(yīng),因此與單跳躍(single-hop)消息傳遞基準(zhǔn)相比,實(shí)際消息的數(shù)量會(huì)翻一倍。
Kafka在其已發(fā)布的基準(zhǔn)測(cè)試中表現(xiàn)如何?
雖然在Kafka上發(fā)布事件通常需要幾微秒的時(shí)間,但端到端傳輸則會(huì)擴(kuò)大到幾毫秒。根據(jù)Confluent發(fā)布的有關(guān)??單跳躍復(fù)制消息基準(zhǔn)??的報(bào)告,有99%的端到端傳輸延遲為5毫秒。
而在我們的基準(zhǔn)測(cè)試中,一臺(tái)主機(jī)上有2個(gè)跳躍點(diǎn)、序列化和反序列化。它們?cè)?00k msg/s輸出和100k msg/s返回的情況下,單個(gè)跳躍消息傳輸所出現(xiàn)的延遲與200k msg/s基本類似。
端到端延遲
為了進(jìn)一步弄清楚到底Kafka與Chronicle在延遲上的差距有多大,我們需要通過(guò)下列圖表來(lái)進(jìn)一步分析。為了便于比較,后一張圖表是前一張表放大10倍情況。
延遲達(dá)100微秒的情況
在保持與之前的規(guī)模相同的情況下,即使跨越了2個(gè)跳躍點(diǎn)(包括序列化),我們?nèi)匀豢梢钥吹剑篊hronicle Queue企業(yè)版保持著延遲的一致性;而開(kāi)源式Chronicle Queue雖然在大部分時(shí)間內(nèi)執(zhí)行了相同的操作,但是它具有更高的延遲。這是因?yàn)镃hronicle Queue企業(yè)版在開(kāi)源的基礎(chǔ)上,包含了一些特定的功能,可以更好地控制異常值。當(dāng)然,由于延遲相當(dāng)高,因此您在下圖中看不到Kafka的相關(guān)曲線。

延遲達(dá)1000微秒的情況
下圖是放大10倍比例的情況。如您所見(jiàn),雖然Chronicle Queue帶有更高的異常值,但是它們?cè)?9.99的分位上是相當(dāng)一致的。同樣,Kafka的曲線仍然沒(méi)法顯示。

延遲達(dá)10,000微秒的情況
下圖是再放大10倍的情況。在這種規(guī)模下,我們無(wú)法看到Chronicle基準(zhǔn)測(cè)試的太多細(xì)節(jié),不過(guò)出現(xiàn)了兩種Kafka配置的典型延遲。特別是在100k msg/s(總共200k msg/s)的情況下,99%延遲約為2,630微秒。這與Confluent的5毫秒基準(zhǔn)測(cè)試非常相似。

對(duì)延遲使用對(duì)數(shù)標(biāo)度
對(duì)于較大范圍的數(shù)值,使用對(duì)數(shù)標(biāo)度往往非常實(shí)用。如下圖所示,它雖然具有一定的可讀性,但是由于多數(shù)人不太習(xí)慣認(rèn)讀對(duì)數(shù)比例圖表,因此他們很難解讀出延遲到底有多大的不同。

延遲到底有多大?
另一種可視化Kafka延遲的方法是繪制Kafka和Chronicle之間的延遲比率。下圖是Kafka在100k msg/s(最佳結(jié)果之一)和Chronicle Queue企業(yè)版在500k msg/s(即負(fù)載為Kafka的5倍)之間的延遲比率圖。在該基準(zhǔn)測(cè)試中,即使其吞吐量只是Chronicle Queue的五分之一,Kafka仍然始終慢了至少680倍。
而且,為了讓Kafka能夠以100k msg/s的吞吐量實(shí)現(xiàn)其最低延遲,我們使用了4個(gè)分區(qū)和8個(gè)微服務(wù)。作為比較,Chronicle Queue在所有情況下都只需要1個(gè)足矣。

Chronicle Queue堆(Heap)的使用
我們讓Chronicle Queue以500k msg/s的消息寫(xiě)入速度,并通過(guò)使用G1收集器和默認(rèn)的GC參數(shù),產(chǎn)生40 MB的峰值堆大小,并持續(xù)運(yùn)行了5分鐘(總共3億條消息)的基準(zhǔn)測(cè)試。其結(jié)果如下圖所示。當(dāng)然,Chronicle Queue并沒(méi)有使用到標(biāo)準(zhǔn)的Java序列化功能。

Kafka內(nèi)存的使用
我們讓Kafka以250k/s的消息寫(xiě)入速度,使用2.87 GB的堆峰值,持續(xù)了10分鐘基準(zhǔn)測(cè)試(總共3億條消息)。在啟動(dòng)后,它觸發(fā)了2,410個(gè)暫停收集,以及182個(gè)并發(fā)循環(huán)收集。下圖展示了該測(cè)試在128 MB堆大小下運(yùn)行時(shí),所產(chǎn)生的超過(guò)139 k的GC。

小結(jié)
雖然Kafka是日志聚合的不錯(cuò)選擇,但由于其相對(duì)較高的端到端延遲,對(duì)于許多涉及到微服務(wù)的用例而言,其延遲可能會(huì)比較明顯。開(kāi)源式的Chronicle Queue在超過(guò)99.99%的時(shí)間內(nèi),都能夠?qū)崿F(xiàn)低于100微秒的一致性延遲。而Kafka即使在吞吐量只有Chronicle的五分之一的情況下,也會(huì)有7毫秒的異常值。
譯者介紹
陳峻 (Julian Chen),51CTO社區(qū)編輯,具有十多年的IT項(xiàng)目實(shí)施經(jīng)驗(yàn),善于對(duì)內(nèi)外部資源與風(fēng)險(xiǎn)實(shí)施管控,專注傳播網(wǎng)絡(luò)與信息安全知識(shí)與經(jīng)驗(yàn);持續(xù)以博文、專題和譯文等形式,分享前沿技術(shù)與新知;經(jīng)常以線上、線下等方式,開(kāi)展信息安全類培訓(xùn)與授課。
原文標(biāo)題:??Kafka vs Chronicle for Microservices??,作者:Peter Lawrey































