本文主要介紹云音樂對kafka的優(yōu)化恋腕,給生產(chǎn)集群帶來了顯著的收益抹锄,初步統(tǒng)計每年為云音樂節(jié)省幾百萬的成本。本文主要分為兩部分荠藤,第一部分詳細(xì)介紹優(yōu)化kafka性能時遇到的問題及解決過程伙单,第二部分介紹kafka producer的寫入原理及batch優(yōu)化。如果對我們定位問題的過程不太感興趣的同學(xué)哈肖,可以直接閱讀第二部分吻育。
一、背景
隨著云音樂主站流量的增長以及曙光埋點的放量淤井,平臺的流量逐漸增長布疼,kafka平臺壓力越來越大摊趾。當(dāng)前云音樂平臺分3個kafka集群:老集群、mirror集群游两、新集群砾层。老集群和mirror集群歷史比較久遠(yuǎn),還在使用比較古老的kafka版本贱案。新集群是最近搭建的肛炮,使用最新kafka版本,拆分為兩個region:default和dawn轰坊,其中铸董,default集群用于新流量接入逗宜,dawn集群專屬用于曙光埋點流量使用习贫。
kafka集群穩(wěn)定性的兩個重要指標(biāo)為:NetworkProcessorIdle
和RequestHandlerIdle
,PE同學(xué)反饋新kafka集群的這兩個指標(biāo)很低茧跋,高峰期在10%以下颤芬,集群流量卻與老集群相差較大悲幅,新kafka集群性能存在嚴(yán)重問題,同時站蝠,老集群也不容樂觀汰具,idle高峰期15%左右,kafka集群的優(yōu)化迫在眉睫菱魔。
二留荔、問題定位
構(gòu)建完善監(jiān)控體系
兵馬未動糧草先行,優(yōu)化要做監(jiān)控先行澜倦,監(jiān)控指標(biāo)一來可以指明方向聚蝶,二來可以驗證優(yōu)化效果,所以藻治,必須要有比較完善的監(jiān)控才能去做相關(guān)優(yōu)化碘勉。由于歷史原因,云音樂的kafka集群缺少必要的體系化的監(jiān)控告警桩卵,只有一些比較宏觀的監(jiān)控指標(biāo)验靡,無法對問題進(jìn)行多維度深入分析。首先我們需要做的就是構(gòu)建完善的監(jiān)控體系雏节。
基于Prometheus構(gòu)建的監(jiān)控平臺已經(jīng)是當(dāng)前業(yè)界的標(biāo)配胜嗓,使用prometheus有2個最大優(yōu)點:
1. kafka社區(qū)提供一套完善的基于prometheus+grafana的監(jiān)控系統(tǒng)構(gòu)建方法,包括指標(biāo)采集上報和grafana模板钩乍,可以很快的構(gòu)建出kafka的監(jiān)控平臺兼蕊,詳細(xì)可參考此文章
2. 利用prometheus提供的PQL語言,可以很靈活的做到多監(jiān)控維度的下鉆和上卷分析件蚕,比較容易發(fā)現(xiàn)問題
另外孙技,當(dāng)前網(wǎng)易內(nèi)部已經(jīng)有現(xiàn)成的prometheus平臺可以直接使用,因此排作,此方案可謂是不二之選牵啦。
基于此,PE同學(xué)僅花了一天時間就構(gòu)建出kafka集群完善的監(jiān)控系統(tǒng)妄痪,在grafana上可以看到集群的各種維度詳細(xì)指標(biāo)信息哈雏,包括:大盤流量、topic流量衫生、broker流量裳瘪、副本復(fù)制流量、線程idle情況罪针、ISR信息彭羹、Consumer指標(biāo)、Producer指標(biāo)泪酱、Replica指標(biāo)派殷、Request指標(biāo)等,這些詳細(xì)的指標(biāo)為我們分析問題帶來很大的便利墓阀。有了監(jiān)控后毡惜,我們開始著手集群的分析和優(yōu)化工作。
監(jiān)控指標(biāo)分析及優(yōu)化過程
1. 第一次分析及優(yōu)化:流量均衡
分析
我們對新集群的核心指標(biāo)進(jìn)行了分析斯撮,并與老集群的指標(biāo)進(jìn)行對比经伙,發(fā)現(xiàn)新集群存在幾個現(xiàn)象:
1. bytesout分布不均,90MB/s ~ 180MB/s之間
2. request handler idle分布不均勿锅,20% ~ 80%之間
3. network processor idle都比較高帕膜,80%以上
4. msgin分布不均,18K op/s ~
5. Request Queue Size較高粱甫,有時會打滿到500泳叠,Response Queue Size較低
6. 磁盤IOUtil較低
7. 集群單臺機器出流量最高在1Gbps左右
基于上述現(xiàn)象我們基本可以得出以下幾個結(jié)論:
1. 請求處理線程不足
2. 流量分布不均是當(dāng)前主要矛盾
3. 當(dāng)前流量并未達(dá)到機器硬件的上限
木桶效應(yīng)很明顯,當(dāng)前集群吞吐量受限于壓力比較大的broker茶宵,因此危纫,首先我們需要對集群進(jìn)行流量均衡。
具體指標(biāo)如下:
優(yōu)化
PE同學(xué)均衡了kafka的bytesOut流量乌庶,idle稍微提升种蝶,但是效果不太明顯。
隨后對msgIn流量進(jìn)行均衡瞒大,操作后螃征,idle有了較明顯的變化,最低的idle從10%左右漲到了20%左右透敌。
雖然idle上升了不少盯滚,但是從絕對值來看踢械,kafka集群還是比較危險,一般idle在30%以下就說明集群已經(jīng)處于比較高負(fù)荷狀態(tài)魄藕,很難應(yīng)對流量突發(fā)情況内列。所以,優(yōu)化工作還需繼續(xù)背率。
2. 第二次分析及優(yōu)化:減少請求數(shù)
Network Processor Idle較高话瞧,Request Handler Idle較低,根據(jù)以前經(jīng)驗寝姿,當(dāng)大量meta請求時交排,會出現(xiàn)此類現(xiàn)象,查看metadata request指標(biāo)饵筑,每秒140左右埃篓,量級不大,應(yīng)該不是這個問題導(dǎo)致翻翩。關(guān)于這兩個idle的相關(guān)原理都许,可以參考kafka網(wǎng)絡(luò)模型總結(jié)
根據(jù)第一次優(yōu)化經(jīng)驗,我們有了一些優(yōu)化方向嫂冻,bytesOut影響不大胶征,msgIn影響較大,Request Handler是用來處理所有kafka的請求的(并不只是處理磁盤相關(guān)請求)桨仿,也就是如果能降低request的請求次數(shù)睛低,應(yīng)該就能提升idle值。分析request請求分布情況服傍,發(fā)現(xiàn)絕大部分是在produce和fetch請求钱雷,因此,接下來的優(yōu)化方向是如何減輕讀寫的請求量吹零。
kafka服務(wù)端使用zero copy的優(yōu)化技術(shù)罩抗,減輕服務(wù)端開銷,客戶端發(fā)送的數(shù)據(jù)塊灿椅,kafka服務(wù)端默認(rèn)不會做解包(compression.type=producer
)套蒂,也就是通過提升producer端的batch,是可以減輕request請求數(shù)的茫蛹。
分析MsgIn較大的幾個topic發(fā)現(xiàn)操刀,這些topic的batch size都很低,分布在1-2之間(對應(yīng)指標(biāo)messagesinpersec/totalproducerequestspersec
)婴洼,單條記錄大小不到1KB骨坑,對應(yīng)寫入任務(wù)的batch.size使用了默認(rèn)的16KB,按理來說柬采,batch應(yīng)該是可以生效的欢唾。通過查看相關(guān)文章且警,發(fā)現(xiàn)影響producer batch的還有一個參數(shù)linger.ms
(默認(rèn)為0),此參數(shù)原理可參考文章kafka producer linger.ms和batch.size參數(shù)說明礁遣。
我們將topic對應(yīng)的寫入任務(wù)振湾,配置linger.ms=50
,上線后產(chǎn)生了一定的效果亡脸,idle從12%左右提升到19%,如下:
查看對應(yīng)topic的batch size從1提升到4树酪,bytesIn從14MB/s降低到4MB/s浅碾,如下:
從上述優(yōu)化效果來看,基本可以明確優(yōu)化Producer的batchSize续语,可以給集群帶來很好的效果垂谢。隨后,我們優(yōu)化了流量最大的UA日志(占據(jù)集群大部分流量)的寫入任務(wù)配置疮茄,集群的整體指標(biāo)得到了極大的改善滥朱,kafka集群整體壓力降低60%+,具體指標(biāo)如下:
總體topic的batchSize從1.67提升到9.71
bytesOut從3.5GB/s降低到1.7GB/s力试,bytesIn從392MB/s降低到218MB/s
idle從20%增長到90%
3. 優(yōu)化經(jīng)驗遷移到老集群經(jīng)歷
優(yōu)化不生效
有了上述優(yōu)化經(jīng)驗徙邻,我們繼續(xù)將此優(yōu)化配置應(yīng)用到老集群。但是畸裳,在變更老集群的部分寫入任務(wù)后缰犁,發(fā)現(xiàn)調(diào)整linger.ms參數(shù)后,雖然參數(shù)已生效怖糊,但是集群指標(biāo)沒有明顯變化帅容,對應(yīng)topic的batchSize指標(biāo)也沒有明顯變化。
分析原因并調(diào)整生效
搜索了相關(guān)資料后伍伤,未發(fā)現(xiàn)特別有用的相關(guān)說明并徘。只能去看kafka producer的源碼了,通過分析kafka的源碼扰魂,發(fā)現(xiàn)kafka producer寫入record時麦乞,如果record中沒有指定partitionID,則會隨機分配partitionID阅爽,放入對應(yīng)的topic-partition級別的本地緩存batch路幸。同時。另外一個異步Sender線程從緩存batch中拿數(shù)據(jù)發(fā)送到對應(yīng)broker付翁,此過程會在下面原理章節(jié)詳細(xì)介紹简肴。
弄清楚相關(guān)原理后,經(jīng)過分析發(fā)現(xiàn)百侧,我們優(yōu)化的寫入任務(wù)并行度120砰识,對應(yīng)topic有150個分區(qū)能扒,寫入流量大概40K/s,這樣均攤到每個topic-partition的流量大概是:2.2條/s辫狼,而我們設(shè)置的linger.ms
為50初斑,也就是Sender線程在從topic-partition的batch拿數(shù)據(jù)的時候,只能拿到1條數(shù)據(jù)膨处,與監(jiān)控數(shù)據(jù)也比較吻合见秤。
為了驗證分析,我們將任務(wù)的并行度從120調(diào)整到1真椿,這樣每個topic-partition的流量大概是:266條/s鹃答。50ms一個batch,大概有13條左右突硝,調(diào)整后觀察對應(yīng)topic的batchSize指標(biāo)测摔,也與我們分析的相符合。
問題原因找到了解恰,接下來需要考慮如何進(jìn)行優(yōu)化锋八。只需要將每條記錄對應(yīng)的partition的策略修改即可,起初我們想通過flink的接口FlinkKafkaPartitioner
或者kafka自帶的接口Partitioner
來自定義record對應(yīng)partition的計算策略(增加計數(shù)器护盈,每N條記錄變更一次對應(yīng)的partition或者每N ms時間間隔變更一次)挟纱。但是更深入的看了kafka producer源碼后,發(fā)現(xiàn)2.4版本中黄琼,kafka新引入了一種Sticky Partitioner
的策略樊销,詳情可參考KIP-480,我們將寫入任務(wù)的kafka client版本升級到2.4使用sticky的partition策略進(jìn)行測試后發(fā)現(xiàn)脏款,batchSize有很大的提升围苫。
將相關(guān)原始流量的分發(fā)寫入任務(wù)調(diào)優(yōu)寫入batch后,老kafka集群的壓力降低了40%+撤师,具體指標(biāo)如下:
bytesIn從1.04GB/s降低到705MB/s剂府,bytesOut從15.4GB/s降低到9.56GB/s
idle從8%提升到50%+
batchSize從1提升到16
三、原理
通過上述優(yōu)化過程剃盾,我們梳理清楚kafka producer的寫入原理腺占,batch的寫入不止與linger.ms
、batch.size
的配置有關(guān)痒谴,還和partitioner計算record的分區(qū)規(guī)則有關(guān)衰伯。
首先,簡單介紹下积蔚,producer的寫入過程意鲸,如下圖:
kafka producer寫入主要分兩部分:寫入線程和異步sender發(fā)送線程。
1. 寫入線程
當(dāng)客戶端調(diào)用KafkaProducer.send(ProducerRecord)
方法,主要有以下四個步驟:
1. 等待topicPartition相關(guān)元信息獲仍豕恕(有緩存)
2. 序列化ProducerRecord中key和value(如果有)
3. 如果當(dāng)前reocord中沒有指定partition信息读慎,則調(diào)用Partitioner.partition()
獲取對應(yīng)的partition
4. 預(yù)估當(dāng)前記錄大小并將其追加到相應(yīng)的topic-partition緩存隊列的ProducerBatch中
2. Sender線程
發(fā)送線程會一直循環(huán)遍歷緩存隊列,并對數(shù)據(jù)做相關(guān)處理槐雾,發(fā)送你ProduceRequest請求到對應(yīng)broker夭委,主要分以下六個步驟:
1. 遍歷所有topic-partition隊列的隊首ProducerBatch
2. 拿到該ProducerBatch做如下判斷(圖中為了簡便并未全部寫明)
第一個元素的追加時間與當(dāng)前時間相比,是否超過
linger.ms
ProducerBatch是否滿
內(nèi)存Buffer是否使用完
是否close或flush中
3. 如果滿足上述的一個條件募强,則將當(dāng)前ProducerBatch對應(yīng)的TopicPartition的leader所在的brokerId放入ready節(jié)點隊列中
4. 得到可以發(fā)送請求的broker的ready節(jié)點后株灸,遍歷每個brokerId并做如下操作:
盡可能多的拿到該brokerId所負(fù)責(zé)的topicPartition對應(yīng)的ProducerBatch(當(dāng)record大小超過
max.request.size
時break,為了防止餓死擎值,每次從不同的topicPartition獲嚷烨摇)將這些ProducerBatch包裝成一個ProducerRequest
5. 更新每個batch相關(guān)的metric,如topic.{name}.records-per-batch/bytes/compression-rate
等
6. 遍歷發(fā)送每個ProducerRequest到對應(yīng)的brokerId
3. 服務(wù)端
關(guān)于服務(wù)端的邏輯幅恋,此處只做簡單介紹,不做深入分析泵肄。
服務(wù)端收到ProducerRequest后捆交,會將其中的每個ProducerBatch拆出來,使用zero-copy的方式將各topic-partition數(shù)據(jù)直接append到相應(yīng)的log-segment文件中腐巢,可極大提升服務(wù)端的性能品追。
4. 場景分析
現(xiàn)在我們來詳細(xì)看下為什么我們將老集群的寫入任務(wù)linger.ms
設(shè)置為50時沒有效果。
我們寫入任務(wù)的流量大概是40K/s冯丙,寫入的topic分區(qū)數(shù)為150肉瓦,任務(wù)并發(fā)為120,如下圖所示:
由于老版本(2.4之前)kafkaClient的每個record對應(yīng)的partition的分配邏輯是隨機的胃惜,從圖中可以很明顯看出泞莉,落到每個topicParitionQueue寫入緩存隊列的流量才不過40000/120/150=2.2
,而我們設(shè)置的linger.ms為40
船殉,也就是在40ms內(nèi)鲫趁,每個topicPartitionQueue中的數(shù)據(jù)最多也就一條,發(fā)送給每個的batch也就1左右利虫,經(jīng)過測試也驗證了這個結(jié)論挨厚。
起初我們計劃通過flink或者kafka暴露出來的Partitioner接口自定義record對應(yīng)的partition分配邏輯。但是糠惫,還有一個問題比較奇怪疫剃,為什么在新集群上卻有效果呢?我們深入比較新老集群對應(yīng)的寫入任務(wù)并看了kafka相關(guān)源碼發(fā)現(xiàn)硼讽,kafka 2.4版本中對record的partition分配策略做了優(yōu)化巢价。簡單來說sticky的分區(qū)策略是保證一個topic在一個producerBatch內(nèi)的所有rocords對應(yīng)的分區(qū)ID保證一樣,這樣就可以保證一個batch內(nèi)的數(shù)據(jù)都放到一個分區(qū)里,詳情可查看KIP-480蹄溉。我們將老機器的寫入任務(wù)使用的kafka client版本也升級到2.4后咨油,發(fā)現(xiàn)寫入的batch大小從1提升到12左右,也與我們預(yù)期相符柒爵。
5. 重要監(jiān)控指標(biāo)匯總
在此役电,總結(jié)下kafka broker端比較重要的一些監(jiān)控指標(biāo)以供后續(xù)完善相關(guān)告警做參考,具體信息可參考kafka監(jiān)控指標(biāo)官方文檔棉胀。
服務(wù)穩(wěn)定性相關(guān)
active controller個數(shù):
kafka_controller_kafkacontroller_activecontrollercount
法瑟,kafka中控節(jié)點,正常為1UR分區(qū)數(shù):
kafka_server_replicamanager_underreplicatedpartitions
唁奢,追不上的分區(qū)數(shù)霎挟,正常為0離線分區(qū)數(shù):
kafka_controller_kafkacontroller_offlinepartitionscount
,未追的分區(qū)數(shù)(有broker宕機)麻掸,正常為0網(wǎng)絡(luò)處理線程idle:
kafka_network_socketserver_networkprocessoravgidlepercent
酥夭,處理socket請求的線程池空閑值,需保持在至少30%之上請求處理線程idle:
kafka_server_kafkarequesthandlerpool_requesthandleravgidlepercent_total
脊奋,真正處理request請求的線程池空閑值熬北,需保持在至少30%以上請求的隊列大小:
kafka_network_requestchannel_requestqueuesize
诚隙,默認(rèn)500讶隐,如果經(jīng)常滿說明當(dāng)前broker的處理慢或壓力大ISR shrink頻率:
kafka_server_replicamanager_isrshrinkspersec
,如果經(jīng)常出現(xiàn)久又,則說明經(jīng)常有副本同步不上leader被移除isr巫延,預(yù)示集群存在穩(wěn)定性問題ISR expand頻率:
kafka_server_replicamanager_isrexpandspersec
,和shrink相反的指標(biāo)地消,表名副本追上leader放進(jìn)isr的頻率
流量相關(guān)
topic消息client寫入條數(shù):
kafka_server_brokertopicmetrics_messagesinpersec
topic消息client寫入大新濉:
kafka_server_brokertopicmetrics_bytesinpersec
topic消息client讀大小(不包含follower復(fù)制):
kafka_server_brokertopicmetrics_bytesoutpersec
follower的replication副本復(fù)制bytesIn和bytesOut:
ReplicationBytesInPerSec/ReplicationBytesOutPerSec
上述流量指標(biāo)均可細(xì)化到每個topic每個broker級別(無法到partition級別)脉执,通過PQL計算幾種組合信息讲冠,可查看broker/topic/總體級別流量信息。
請求相關(guān)
producer/client/副本復(fù)制請求數(shù):
kafka_network_requestmetrics_requestspersec
每個topic的producer請求數(shù):
kafka.server:type=BrokerTopicMetrics,name=TotalProduceRequestsPerSec
每個topic的拉取請求數(shù):
kafka.server:type=BrokerTopicMetrics,name=TotalFetchRequestsPerSec
上述指標(biāo)适瓦,可細(xì)化到請求類型(FetchConsumer/FetchFollower/Produce/ListOffset等)竿开、broker和topic級別,也可通過PQL進(jìn)行各維度組合查看相應(yīng)指標(biāo)玻熙。
四否彩、結(jié)尾
至此,本文完成對kafka的整個優(yōu)化過程和相關(guān)原理描述嗦随。后續(xù)也可將此優(yōu)化推廣到其他此類場景列荔。優(yōu)化前需要注意以下四項:
1. 通過監(jiān)控指標(biāo)確認(rèn)寫入的topic的batch大小是否為1左右敬尺,在kafka-overview中可查看相應(yīng)指標(biāo)
2. 確保程序中,寫入kafka的records中并未指定key(如果指定key贴浙,則默認(rèn)不會使用sticky分配策略砂吞,以確保相同key在一個partition,除非強行指定sticky partitioner)
3. 當(dāng)linger.ms設(shè)置為0時崎溃,batch也可能生效蜻直,只要數(shù)據(jù)產(chǎn)生速度大于發(fā)送速度即可
4. 當(dāng)設(shè)置linger.ms時,允許數(shù)據(jù)存在指定的延遲時間
另外袁串,kafka服務(wù)還存在一定的優(yōu)化空間概而,包括磁盤、網(wǎng)絡(luò)囱修、內(nèi)存等場景赎瑰,這里就做不詳細(xì)介紹了。
在此破镰,也感謝PE同學(xué):馬宏展餐曼、童罕,感謝他們提供的幫助鲜漩,尤其是kafka的監(jiān)控系統(tǒng)和promethus平臺晋辆,在優(yōu)化過程中發(fā)揮了重要的作用。
五宇整、參考文章
1. 使用promethus+grafana監(jiān)控kafka
2. kafka網(wǎng)絡(luò)模型總結(jié)
3. kafka producer linger.ms和batch.size參數(shù)說明
4. KIP-480