導(dǎo)讀:埋點(diǎn)數(shù)據(jù)是數(shù)據(jù)分析、推薦、運(yùn)營(yíng)的基礎(chǔ),低延時(shí)、穩(wěn)定、高效的埋點(diǎn)數(shù)據(jù)流對(duì)提高用戶(hù)體驗(yàn)有著非常重要的作用。而隨著流量的增大,埋點(diǎn)的增多,在大流量場(chǎng)景下,埋點(diǎn)數(shù)據(jù)流的建設(shè)和治理也面臨不同的挑戰(zhàn)。本文將介紹字節(jié)跳動(dòng)在埋點(diǎn)數(shù)據(jù)流業(yè)務(wù)場(chǎng)景遇到的需求和挑戰(zhàn),以及為了應(yīng)對(duì)這些需求和挑戰(zhàn)在建設(shè)和治理過(guò)程中的具體實(shí)踐。主要包含以下幾個(gè)部分內(nèi)容:
- 埋點(diǎn)數(shù)據(jù)流簡(jiǎn)介
- 埋點(diǎn)數(shù)據(jù)流建設(shè)實(shí)踐
- 埋點(diǎn)數(shù)據(jù)流治理實(shí)踐
- 未來(lái)規(guī)劃
01
埋點(diǎn)數(shù)據(jù)流簡(jiǎn)介
1. 埋點(diǎn)數(shù)據(jù)流在字節(jié)
埋點(diǎn)數(shù)據(jù)流主要處理的數(shù)據(jù)是埋點(diǎn),埋點(diǎn)也叫Event Tracking,是數(shù)據(jù)和業(yè)務(wù)之間的橋梁,也是數(shù)據(jù)分析、推薦、運(yùn)營(yíng)的基石。
用戶(hù)在使用 App 、小程序、 Web 等各種線上應(yīng)用時(shí)產(chǎn)生的行為數(shù)據(jù)主要通過(guò)埋點(diǎn)的形式進(jìn)行采集上報(bào),按不同的來(lái)源可以分為:
① 客戶(hù)端埋點(diǎn)
② Web端埋點(diǎn)
③ 服務(wù)端埋點(diǎn)
埋點(diǎn)通過(guò)埋點(diǎn)收集服務(wù)接收到MQ,經(jīng)過(guò)一系列的Flink實(shí)時(shí)ETL對(duì)埋點(diǎn)進(jìn)行數(shù)據(jù)標(biāo)準(zhǔn)化、數(shù)據(jù)清洗、數(shù)據(jù)字段擴(kuò)充、實(shí)時(shí)風(fēng)控反作弊等處理,最終分發(fā)到不同的下游。下游主要包括推薦、廣告、ABTest、行為分析系統(tǒng)、實(shí)時(shí)數(shù)倉(cāng)、離線數(shù)倉(cāng)等。因?yàn)槁顸c(diǎn)數(shù)據(jù)流處在整個(gè)數(shù)據(jù)處理鏈路的最上游,所以決定了“穩(wěn)定性”是埋點(diǎn)數(shù)據(jù)流最為關(guān)注的一點(diǎn)。
2. 字節(jié)的埋點(diǎn)數(shù)據(jù)流規(guī)模
字節(jié)跳動(dòng)埋點(diǎn)數(shù)據(jù)流的規(guī)模比較大,體現(xiàn)在以下幾個(gè)方面:
① 接入的業(yè)務(wù)數(shù)量很多,包括抖音、今日頭條、西瓜視頻、番茄小說(shuō)在內(nèi)的多個(gè)大大小小的App和服務(wù),都接入了埋點(diǎn)數(shù)據(jù)流。
② 流量很大,當(dāng)前字節(jié)跳動(dòng)埋點(diǎn)數(shù)據(jù)流峰值流量超過(guò)1億每秒,每天處理超過(guò)萬(wàn)億量級(jí)埋點(diǎn),PB級(jí)數(shù)據(jù)存儲(chǔ)增量。
③ ETL任務(wù)規(guī)模體量較大,在多個(gè)機(jī)房部署了超過(guò)1000個(gè)Flink任務(wù)和超過(guò)1000個(gè)MQ Topic,使用了超過(guò)50萬(wàn)Core CPU資源,單個(gè)任務(wù)最大超過(guò)12萬(wàn)Core CPU,單個(gè)MQ Topic最大達(dá)到10000個(gè)partition。
在這么巨大的流量和任務(wù)規(guī)模下,埋點(diǎn)數(shù)據(jù)流主要處理的是哪些問(wèn)題?我們來(lái)看幾個(gè)具體的業(yè)務(wù)場(chǎng)景。
3. 業(yè)務(wù)場(chǎng)景-UserAction ETL
在推薦場(chǎng)景中,由于埋點(diǎn)種類(lèi)多、流量巨大,而推薦只關(guān)注其中部分埋點(diǎn),因此需要通過(guò)UserAction ETL對(duì)埋點(diǎn)流進(jìn)行處理,對(duì)這個(gè)場(chǎng)景來(lái)說(shuō)有兩個(gè)需求點(diǎn):
① 數(shù)據(jù)流的時(shí)效性
② ETL規(guī)則動(dòng)態(tài)更新
為了提升下流推薦系統(tǒng)的處理效率,我們?cè)跀?shù)據(jù)流配置ETL規(guī)則對(duì)推薦關(guān)注的埋點(diǎn)進(jìn)行過(guò)濾,并對(duì)字段進(jìn)行刪減、映射、標(biāo)準(zhǔn)化等清洗處理,將埋點(diǎn)打上不同的動(dòng)作類(lèi)型標(biāo)識(shí),處理之后的埋點(diǎn)內(nèi)部一般稱(chēng)為UserAction。UserAction與服務(wù)端展現(xiàn)、Feature等數(shù)據(jù)會(huì)在推薦Joiner任務(wù)的分鐘級(jí)窗口中進(jìn)行拼接處理,產(chǎn)出instance訓(xùn)練樣本。
舉個(gè)例子:一個(gè)客戶(hù)端的文章點(diǎn)贊埋點(diǎn),描述了一個(gè)用戶(hù)在某一個(gè)時(shí)間點(diǎn)對(duì)某一篇文章進(jìn)行了點(diǎn)贊操作,這個(gè)埋點(diǎn)經(jīng)過(guò)埋點(diǎn)收集服務(wù)進(jìn)入ETL鏈路,通過(guò)UserAction ETL處理后,實(shí)時(shí)地進(jìn)入推薦Joiner任務(wù)中拼接生成樣本,更新推薦模型,從而提升用戶(hù)的使用體驗(yàn)。
如果產(chǎn)出UserAction數(shù)據(jù)的ETL鏈路出現(xiàn)比較大的延遲,就不能在拼接窗口內(nèi)及時(shí)地完成訓(xùn)練樣本的拼接,可能會(huì)導(dǎo)致用戶(hù)體驗(yàn)下降。因此,對(duì)于推薦來(lái)說(shuō),數(shù)據(jù)流的時(shí)效性是比較強(qiáng)的需求。而推薦模型的迭代和產(chǎn)品埋點(diǎn)的變動(dòng)都可能導(dǎo)致UserAction ETL規(guī)則的變動(dòng),如果我們把這個(gè)ETL規(guī)則硬編碼在代碼中,每次修改都需要升級(jí)代碼并重啟相關(guān)的Flink ETL任務(wù),這樣會(huì)影響數(shù)據(jù)流的穩(wěn)定性和數(shù)據(jù)的時(shí)效性,因此這個(gè)場(chǎng)景的另一個(gè)需求是ETL規(guī)則的動(dòng)態(tài)更新。
4. 業(yè)務(wù)場(chǎng)景-數(shù)據(jù)分流
抖音的埋點(diǎn)Topic晚高峰超過(guò)一億每秒,而下游電商、直播、短視頻等不同業(yè)務(wù)關(guān)注的埋點(diǎn)都只是其中一部分。如果每個(gè)業(yè)務(wù)都分別使用一個(gè)Flink任務(wù)去消費(fèi)抖音的全量埋點(diǎn)去過(guò)濾出自己關(guān)注的埋點(diǎn),會(huì)消耗大量的計(jì)算資源,同時(shí)也會(huì)造成MQ集群帶寬扇出非常嚴(yán)重,影響MQ集群的穩(wěn)定性。
因此我們提供了數(shù)據(jù)分流服務(wù)。如何實(shí)現(xiàn)?我們使用一個(gè)Flink任務(wù)去消費(fèi)上游埋點(diǎn)Topic,通過(guò)在任務(wù)中配置分流規(guī)則的方式,將各個(gè)業(yè)務(wù)關(guān)注的埋點(diǎn)分流到下游的小Topic中提供給各業(yè)務(wù)消費(fèi),減少不必要的資源開(kāi)銷(xiāo),同時(shí)也降低了MQ集群出帶寬。
分流需求大多對(duì)SLA有一定要求,斷流和數(shù)據(jù)延遲可能會(huì)影響下流的推薦效果、廣告收入以及數(shù)據(jù)報(bào)表更新等。另外隨著業(yè)務(wù)的發(fā)展,實(shí)時(shí)數(shù)據(jù)需求日益增加,分流規(guī)則新增和修改變得非常頻繁,如果每次規(guī)則變動(dòng)都需要修改代碼和重啟任務(wù)會(huì)對(duì)下游造成較大影響,因此在數(shù)據(jù)分流這個(gè)場(chǎng)景,規(guī)則的動(dòng)態(tài)更新也是比較強(qiáng)的需求。
5. 業(yè)務(wù)場(chǎng)景-容災(zāi)降級(jí)
另一個(gè)場(chǎng)景是容災(zāi)降級(jí)。數(shù)據(jù)流容災(zāi)首先考慮的是防止單個(gè)機(jī)房級(jí)別的故障導(dǎo)致埋點(diǎn)數(shù)據(jù)流完全不可用,因此埋點(diǎn)數(shù)據(jù)流需要支持多機(jī)房的容災(zāi)部署。其次當(dāng)出現(xiàn)機(jī)房級(jí)別的故障時(shí),需要將故障機(jī)房的流量快速調(diào)度到可用機(jī)房實(shí)現(xiàn)服務(wù)的容災(zāi)恢復(fù),因此需要埋點(diǎn)數(shù)據(jù)流具備機(jī)房間快速切流的能力。
而數(shù)據(jù)流降級(jí)主要考慮的是埋點(diǎn)數(shù)據(jù)流容量不足以承載全部流量的場(chǎng)景,比如春晚活動(dòng)、電商大促這類(lèi)有較大突發(fā)流量的場(chǎng)景。為了保障鏈路的穩(wěn)定性和可用性,需要服務(wù)具備主動(dòng)或者被動(dòng)的降級(jí)能力。
6. 埋點(diǎn)數(shù)據(jù)流遇到的挑戰(zhàn)
挑戰(zhàn)主要是流量大和業(yè)務(wù)多導(dǎo)致的。流量大服務(wù)規(guī)模就大,不僅會(huì)導(dǎo)致成本治理的問(wèn)題,還會(huì)帶來(lái)單機(jī)故障多、性能瓶頸等因素引發(fā)的穩(wěn)定性問(wèn)題。而下游業(yè)務(wù)多、需求變化頻繁,推薦、廣告、實(shí)時(shí)數(shù)倉(cāng)等下游業(yè)務(wù)對(duì)穩(wěn)定性和實(shí)時(shí)性都有比較高的要求。
在流量大、業(yè)務(wù)多這樣的背景下,如何保障埋點(diǎn)數(shù)據(jù)流穩(wěn)定性的同時(shí)降低成本、提高效率,是埋點(diǎn)數(shù)據(jù)流穩(wěn)定性治理和成本治理面對(duì)的挑戰(zhàn)。
--
02
埋點(diǎn)數(shù)據(jù)流建設(shè)實(shí)踐
上文我們了解了埋點(diǎn)數(shù)據(jù)流的業(yè)務(wù)場(chǎng)景和面對(duì)的挑戰(zhàn),接下來(lái)會(huì)介紹埋點(diǎn)數(shù)據(jù)流在ETL鏈路建設(shè)和容災(zāi)與降級(jí)能力上的一些實(shí)踐。
1. ETL鏈路建設(shè)-發(fā)展歷程
埋點(diǎn)數(shù)據(jù)流ETL鏈路發(fā)展到現(xiàn)在主要經(jīng)歷了三個(gè)階段。
第一個(gè)階段是2018年以前,業(yè)務(wù)需求快速迭代的早期階段。那時(shí)我們主要使用PyJStorm與基于Python/ target=_blank class=infotextkey>Python的規(guī)則引擎構(gòu)建主要的流式處理鏈路。特點(diǎn)是比較靈活,可以快速支持業(yè)務(wù)的各種需求,伴隨著埋點(diǎn)量的快速上漲,PyJStorm暴露出很多穩(wěn)定性和運(yùn)維上的問(wèn)題,性能也不足以支撐業(yè)務(wù)增長(zhǎng)。2018年內(nèi)部開(kāi)始大力推廣Flink,并且針對(duì)大量舊任務(wù)使用PyJStorm的情況提供了PyJStorm到PyFlink的兼容適配,流式任務(wù)托管平臺(tái)的建設(shè)一定程度上也解決了流式任務(wù)運(yùn)維管理問(wèn)題,數(shù)據(jù)流ETL鏈路也在2018年全面遷移到了PyFlink,進(jìn)入到Flink流式計(jì)算的新時(shí)代。
第二個(gè)階段是2018年到2020年,隨著流量的進(jìn)一步上漲,PyFlink和kafka的性能瓶頸以及當(dāng)時(shí)使用的JSON數(shù)據(jù)格式帶來(lái)的性能和數(shù)據(jù)質(zhì)量問(wèn)題紛紛顯現(xiàn)出來(lái)。與此同時(shí),下流業(yè)務(wù)對(duì)數(shù)據(jù)延遲、數(shù)據(jù)質(zhì)量的敏感程度與日俱增。我們不僅對(duì)一些痛點(diǎn)進(jìn)行了針對(duì)性?xún)?yōu)化,還花費(fèi)一年多的時(shí)間將整個(gè)ETL鏈路從PyFlink切換到JAVA Flink,使用基于Groovy的規(guī)則引擎替換了基于Python的規(guī)則引擎,使用Protobuf替代了JSON,新鏈路相比舊鏈路性能提升了數(shù)倍。同時(shí)大數(shù)據(jù)開(kāi)發(fā)平臺(tái)和流量平臺(tái)的建設(shè)提升了埋點(diǎn)數(shù)據(jù)流在任務(wù)開(kāi)發(fā)、ETL規(guī)則管理、埋點(diǎn)管理、多機(jī)房容災(zāi)降級(jí)等多方面的能力。
第三個(gè)階段是從2021年開(kāi)始至今,進(jìn)一步提升數(shù)據(jù)流ETL鏈路的性能和穩(wěn)定性,在滿足流量增長(zhǎng)和需求增長(zhǎng)的同時(shí),降低資源成本和運(yùn)維成本是這一階段的主要目標(biāo)。我們主要從三個(gè)方面進(jìn)行了優(yōu)化。
① 優(yōu)化了引擎性能,隨著流量和ETL規(guī)則的不斷增加,我們基于Groovy的規(guī)則引擎使用的資源也在不斷增加,所以基于Janino對(duì)規(guī)則引擎進(jìn)行了重構(gòu),引擎的性能得到了十倍的提升。
② 基于流量平臺(tái)建設(shè)了一套比較完善的埋點(diǎn)治理體系,通過(guò)埋點(diǎn)下線、埋點(diǎn)管控、埋點(diǎn)采樣等手段降低埋點(diǎn)成本。
③ 將鏈路進(jìn)行了分級(jí),不同的等級(jí)的鏈路保障不同的SLA,在資源不足的情況下,優(yōu)先保障高優(yōu)鏈路。
接下來(lái)是我們2018至2020年之間埋點(diǎn)數(shù)據(jù)流ETL鏈路建設(shè)的一些具體實(shí)踐。
2. ETL鏈路建設(shè)-基于規(guī)則引擎的Flink ETL
在介紹業(yè)務(wù)場(chǎng)景時(shí),提到我們一個(gè)主要的需求是ETL規(guī)則的動(dòng)態(tài)更新,那么我們來(lái)看一下埋點(diǎn)數(shù)據(jù)流Flink ETL任務(wù)是如何基于規(guī)則引擎支持動(dòng)態(tài)更新的,如何在不重啟任務(wù)的情況下,實(shí)時(shí)地更新上下游的Schema信息、規(guī)則的處理邏輯以及修改路由拓?fù)洹?/p>
首先,我們?cè)诹髁科脚_(tái)上配置了上下游數(shù)據(jù)集的拓?fù)潢P(guān)系、Schema和ETL規(guī)則,然后通過(guò)ConfigCenter將這些元數(shù)據(jù)發(fā)送給Flink ETL Job,每個(gè)Flink ETL Job的TaskManager都有一個(gè)Meta Updater更新線程,更新線程每分鐘通過(guò)RPC請(qǐng)求從流量平臺(tái)拉取并更新相關(guān)的元數(shù)據(jù),Source operator從MQ Topic中消費(fèi)到的數(shù)據(jù)傳入ProcessFunction,根據(jù)MQ Topic對(duì)應(yīng)的Schema信息反序列化為InputMessage,然后進(jìn)入到規(guī)則引擎中,通過(guò)規(guī)則索引算法匹配出需要運(yùn)行的規(guī)則,每條規(guī)則我們抽象為一個(gè)Filter模塊和一個(gè)Action模塊,F(xiàn)liter和Action都支持UDF,F(xiàn)ilter篩選命中后,會(huì)通過(guò)Action模塊對(duì)數(shù)據(jù)進(jìn)行字段的映射和清洗,然后輸出到OutputMessage中,每條規(guī)則也指定了對(duì)應(yīng)的下游數(shù)據(jù)集,路由信息也會(huì)一并寫(xiě)出。
當(dāng)OutputMessage輸出到Slink后,Slink根據(jù)其中的路由信息將數(shù)據(jù)發(fā)送到SlinkManager管理的不同的Client中,然后由對(duì)應(yīng)的Client發(fā)送到下游的MQ中。
3. ETL鏈路建設(shè)-規(guī)則引擎
規(guī)則引擎為埋點(diǎn)數(shù)據(jù)流ETL鏈路提供了動(dòng)態(tài)更新規(guī)則的能力,而埋點(diǎn)數(shù)據(jù)流Flink ETL Job使用的規(guī)則引擎也經(jīng)歷了從Python到Groovy再到Janino的迭代。
由于Python腳本語(yǔ)言本身的靈活性,基于Python實(shí)現(xiàn)動(dòng)態(tài)加載規(guī)則比較簡(jiǎn)單。通過(guò)Compile函數(shù)可以將一段代碼片段編譯成字節(jié)代碼,再通過(guò)eval函數(shù)進(jìn)行調(diào)用就可以實(shí)現(xiàn)。但Python規(guī)則引擎存在性能較弱、規(guī)則缺乏管理等問(wèn)題。
遷移到Java Flink后,在流量平臺(tái)上統(tǒng)一管理運(yùn)維ETL規(guī)則以及schema、數(shù)據(jù)集等元數(shù)據(jù),用戶(hù)在流量平臺(tái)編輯相應(yīng)的ETL規(guī)則,從前端發(fā)送到后端,經(jīng)過(guò)一系列的校驗(yàn)最終保存為邏輯規(guī)則。引擎會(huì)將這個(gè)邏輯規(guī)則編譯為實(shí)際執(zhí)行的物理規(guī)則,基于Groovy的引擎通過(guò)GroovyClassLoader動(dòng)態(tài)加載規(guī)則和對(duì)應(yīng)的UDF。雖然Groovy引擎性能比Python引擎提升了多倍,但Groovy本身也存在額外的性能開(kāi)銷(xiāo),因此我們又借助Janino可以動(dòng)態(tài)高效地編譯Java代碼直接執(zhí)行的能力,將Groovy替換成了Janino,同時(shí)也將處理Protobuf數(shù)據(jù)時(shí)使用的DynamicMessage替換成了GeneratedMessage,整體性能提升了10倍。
除了規(guī)則引擎的迭代,我們?cè)谄脚_(tái)側(cè)的測(cè)試發(fā)布和監(jiān)控方面也做了很多建設(shè)。測(cè)試發(fā)布環(huán)節(jié)支持了規(guī)則的線下測(cè)試、線上調(diào)試,以及灰度發(fā)布的功能。監(jiān)控環(huán)節(jié)支持了字段、規(guī)則、任務(wù)等不同粒度的異常監(jiān)控,如規(guī)則的流量波動(dòng)報(bào)警、任務(wù)的資源報(bào)警等。
4. ETL鏈路建設(shè)-Flink拆分任務(wù)
規(guī)則引擎的應(yīng)用解決了埋點(diǎn)數(shù)據(jù)流ETL鏈路如何快速響應(yīng)業(yè)務(wù)需求的問(wèn)題,實(shí)現(xiàn)了ETL規(guī)則的動(dòng)態(tài)更新,從而修改ETL規(guī)則不需要修改代碼和重啟任務(wù)。
但規(guī)則引擎本身的迭代、流量增長(zhǎng)導(dǎo)致的資源擴(kuò)容等場(chǎng)景,還是需要升級(jí)重啟Flink任務(wù),導(dǎo)致下游斷流。
除了重啟斷流外,大任務(wù)還可能在重啟時(shí)遇到啟動(dòng)慢、隊(duì)列資源不足或者資源碎片導(dǎo)致起不來(lái)等情況。
針對(duì)這些痛點(diǎn)我們上線了Flink拆分任務(wù),本質(zhì)上是將一個(gè)大任務(wù)拆分為一組子任務(wù),每個(gè)子任務(wù)按比例去消費(fèi)上游Topic的部分Partition,按相同的邏輯處理后再分別寫(xiě)出到下游Topic。
舉個(gè)例子:上游Topic有200個(gè)Partition,我們?cè)谝徽臼介_(kāi)發(fā)平臺(tái)上去配置Flink拆分任務(wù)時(shí)只需要指定每個(gè)子任務(wù)的流量比例,每個(gè)子任務(wù)就能自動(dòng)計(jì)算出它需要消費(fèi)的topic partition區(qū)間,其余參數(shù)也支持按流量比例自動(dòng)調(diào)整。
拆分任務(wù)的應(yīng)用使得數(shù)據(jù)流除了規(guī)則粒度的灰度發(fā)布能力之外,還具備了Job粒度的灰度發(fā)布能力,升級(jí)擴(kuò)容的時(shí)候不會(huì)發(fā)生斷流,上線的風(fēng)險(xiǎn)更可控。同時(shí)由于拆分任務(wù)的各子任務(wù)是獨(dú)立的,因此單個(gè)子任務(wù)出現(xiàn)反壓、Failover對(duì)下游的影響更小。另一個(gè)優(yōu)點(diǎn)是,單個(gè)子任務(wù)的資源使用量更小,資源可以同時(shí)在多個(gè)隊(duì)列進(jìn)行靈活的部署。
5. 容災(zāi)與降級(jí)能力建設(shè)
說(shuō)到ETL鏈路建設(shè),埋點(diǎn)數(shù)據(jù)流在容災(zāi)與降級(jí)能力建設(shè)方面也進(jìn)行了一些實(shí)踐。
首先是容災(zāi)能力的建設(shè),埋點(diǎn)數(shù)據(jù)流在Flink, MQ, Yarn, HDFS等組件支持多機(jī)房容災(zāi)的基礎(chǔ)上完成了多機(jī)房容災(zāi)部署,并準(zhǔn)備了多種流量調(diào)度的預(yù)案。
正常情況下流量會(huì)均勻打到多個(gè)機(jī)房,MQ在多個(gè)機(jī)房間同步,F(xiàn)link ETL Job默認(rèn)從本地MQ進(jìn)行消費(fèi),如果某個(gè)機(jī)房出現(xiàn)故障,我們根據(jù)情況可以選擇通過(guò)配置下發(fā)的方式從客戶(hù)端將流量調(diào)度到其他非受災(zāi)機(jī)房,也可以在CDN側(cè)將流量調(diào)度到其他非受災(zāi)機(jī)房。埋點(diǎn)數(shù)據(jù)流ETL鏈路可以分鐘級(jí)地進(jìn)入容災(zāi)模式,迅速將故障機(jī)房的Flink Job切換到可用的機(jī)房。
其次是服務(wù)降級(jí)能力的建設(shè),主要包含服務(wù)端降級(jí)策略和客戶(hù)端降級(jí)策略。服務(wù)端降級(jí)策略主要通過(guò)LB限流、客戶(hù)端進(jìn)行退避重試的機(jī)制來(lái)實(shí)現(xiàn),客戶(hù)端降級(jí)策略通過(guò)配置下發(fā)可以降低埋點(diǎn)的上報(bào)頻率。
舉個(gè)例子:在春晚活動(dòng)中參與的用戶(hù)很多,口播期間更是有著非常巨大的流量洪峰,2021年春晚活動(dòng)期間為了應(yīng)對(duì)口播期間的流量洪峰,埋點(diǎn)數(shù)據(jù)流開(kāi)啟了客戶(hù)端的降級(jí)策略,動(dòng)態(tài)降低了一定比例用戶(hù)的埋點(diǎn)上報(bào)頻率,在口播期間不上報(bào),口播結(jié)束后迅速恢復(fù)上報(bào)。在降級(jí)場(chǎng)景下,下游的指標(biāo)計(jì)算是通過(guò)消費(fèi)未降級(jí)用戶(hù)上報(bào)的埋點(diǎn)去估算整體指標(biāo)。目前我們?cè)诖嘶A(chǔ)上進(jìn)行了優(yōu)化,客戶(hù)端目前的降級(jí)策略可以更近一步地根據(jù)埋點(diǎn)的分級(jí)信息去保障高優(yōu)的埋點(diǎn)不降級(jí),這樣可以在活動(dòng)場(chǎng)景下保障活動(dòng)相關(guān)的埋點(diǎn)不降級(jí)的上報(bào),支持下游指標(biāo)的準(zhǔn)確計(jì)算。
--
03
埋點(diǎn)數(shù)據(jù)流治理實(shí)踐
介紹完埋點(diǎn)數(shù)據(jù)流建設(shè)的實(shí)踐,接下來(lái)給大家分享的是埋點(diǎn)數(shù)據(jù)流治理方面的一些實(shí)踐。埋點(diǎn)數(shù)據(jù)流治理包含多個(gè)治理領(lǐng)域,比如穩(wěn)定性、成本、埋點(diǎn)質(zhì)量等,每個(gè)治理領(lǐng)域下面又有很多具體的治理項(xiàng)目。
比如在穩(wěn)定性治理中我們通過(guò)優(yōu)化減少了由于單機(jī)問(wèn)題、MQ性能問(wèn)題和混布問(wèn)題等導(dǎo)致的各種穩(wěn)定性問(wèn)題;
成本治理方面,我們通過(guò)組件選型、性能優(yōu)化、埋點(diǎn)治理等方式取得了顯著降本增效的成果;
埋點(diǎn)質(zhì)量治理方面,我們對(duì)臟數(shù)據(jù)問(wèn)題、埋點(diǎn)字段類(lèi)型錯(cuò)誤問(wèn)題和埋點(diǎn)數(shù)據(jù)的丟失重復(fù)問(wèn)題進(jìn)行了監(jiān)控和治理。
這次我們主要選取了其中部分治理項(xiàng)目和大家分享。
1. 單機(jī)問(wèn)題優(yōu)化-Flink BacklogRescale
Yarn單機(jī)問(wèn)題導(dǎo)致Flink任務(wù)Failover、反壓、消費(fèi)能力下降是比較常見(jiàn)的case。
單機(jī)問(wèn)題的類(lèi)型有很多:隊(duì)列負(fù)載不均、單機(jī)load高或者其他進(jìn)程導(dǎo)致CPU負(fù)載高,以及一些硬件故障都可能導(dǎo)致Yarn單機(jī)問(wèn)題。針對(duì)Yarn單機(jī)問(wèn)題,我們從Flink和Yarn兩個(gè)層面分別進(jìn)行了優(yōu)化,最終使單機(jī)load高導(dǎo)致的數(shù)據(jù)延遲減少了80%以上。
首先是Flink層面的優(yōu)化,在埋點(diǎn)數(shù)據(jù)流ETL場(chǎng)景中,為了減少不必要的網(wǎng)絡(luò)傳輸,我們的Partitioner主要采用的是Rescale Partitioner,而Rescale Partitioner會(huì)使用Round-Robin的方式發(fā)送數(shù)據(jù)到下游Channel中。由于單機(jī)問(wèn)題可能導(dǎo)致下游個(gè)別Task反壓或者處理延遲從而引起反壓,而實(shí)際上在這個(gè)場(chǎng)景里面,數(shù)據(jù)從上游task發(fā)送到任何一個(gè)下游的Task都是可以的,合理的策略應(yīng)該是根據(jù)下游的Task的處理能力去發(fā)送數(shù)據(jù),而不是用Round-Robin方式。
另一方面我們注意到Flink Credit-Based flow control反壓機(jī)制中,可以用backlog size去判斷下游Task的處理負(fù)載,我們也就可以將Round Robin的發(fā)送方式修改為根據(jù)Channel的Backlog size信息,去選擇負(fù)載更低的下游Channel進(jìn)行發(fā)送。這個(gè)Feature上線后,隊(duì)列的負(fù)載變得更加均衡,CPU的使用率也提升了10%。
2. 單機(jī)問(wèn)題優(yōu)化-Yarn優(yōu)化
Yarn層面的優(yōu)化,第一個(gè)是隊(duì)列資源層面,我們使用獨(dú)立的Label隊(duì)列可以避免高峰期被其他低優(yōu)任務(wù)影響。
第二個(gè)是對(duì)于Yarn節(jié)點(diǎn)上的DataNode把帶寬打滿或者CPU使用比較高影響節(jié)點(diǎn)上埋點(diǎn)數(shù)據(jù)流Flink任務(wù)穩(wěn)定性的情況,通過(guò)給DataNode進(jìn)行網(wǎng)絡(luò)限速,CPU綁核等操作,避免了DataNode對(duì)Flink進(jìn)程的影響。
第三個(gè)是Yarn反調(diào)度的策略,目前字節(jié)跳動(dòng)Flink使用的Yarn Gang Scheduler會(huì)按條件約束選擇性地分配Yarn資源,在任務(wù)啟動(dòng)時(shí)均衡的放置Container,但是由于時(shí)間的推移,流量的變化等各種因素,隊(duì)列還是會(huì)出現(xiàn)負(fù)載不均衡的情況,所以反調(diào)度策略就是為了解決這種負(fù)載不均衡而生的二次調(diào)度機(jī)制。
反調(diào)度策略中,Yarn會(huì)定期檢查不滿足原有約束的Container,并在這些Container所在節(jié)點(diǎn)上篩選出需要重新調(diào)度的Container返還給Flink Job Manager,然后Flink會(huì)重新調(diào)度這些Container,重新調(diào)度會(huì)按照原有的約束條件嘗試申請(qǐng)等量的可用資源,申請(qǐng)成功后再進(jìn)行遷移。
另外,我們會(huì)針對(duì)一些頻繁出問(wèn)題的節(jié)點(diǎn)把它們加入調(diào)度的黑名單,在調(diào)度的時(shí)候避免將container調(diào)度到這些節(jié)點(diǎn)。
3. MQ優(yōu)化-Databus應(yīng)用
在流量迅速增長(zhǎng)的階段,埋點(diǎn)數(shù)據(jù)流Flink任務(wù)一開(kāi)始是通過(guò)Kafka Connecter直接寫(xiě)入Kafka。但由于任務(wù)處理的流量非常大,F(xiàn)link任務(wù)中Sink并發(fā)比較多,導(dǎo)致批量發(fā)送的效率不高,Kafka集群寫(xiě)入的請(qǐng)求量非常大。并且,由于每個(gè)Sink一個(gè)或多個(gè)Client,Client與Kafka之間建立的連接數(shù)也非常多,而Kafka由于Controller的性能瓶頸無(wú)法繼續(xù)擴(kuò)容,所以為了緩解Kafka集群的壓力,埋點(diǎn)數(shù)據(jù)流的Flink任務(wù)引入了Databus組件。
Databus是一種以Agent方式部署在各個(gè)節(jié)點(diǎn)上的MQ寫(xiě)入組件。Databus Agent可以配置多個(gè)Channel,每個(gè)Channel對(duì)應(yīng)一個(gè)Kafka的Topic。Flink Job每個(gè)Task Manager里面的Sink會(huì)通過(guò)Unix Domain Socket的方式將數(shù)據(jù)發(fā)送到節(jié)點(diǎn)上Databus Agent的Channel里面,再由Databus將數(shù)據(jù)批量地發(fā)送到對(duì)應(yīng)的Kafka Topic。由于一個(gè)節(jié)點(diǎn)上會(huì)有多個(gè)Task Manager,每個(gè)Task Manager都會(huì)先把數(shù)據(jù)發(fā)送到節(jié)點(diǎn)上的Databus Agent,Databus Agent中的每個(gè)Channel實(shí)際上聚合了節(jié)點(diǎn)上所有Task Manager寫(xiě)往同一個(gè)Topic數(shù)據(jù),因此批量發(fā)送的效率非常高,極大地降低了Kafka集群的寫(xiě)入請(qǐng)求量,并且與Kafka集群之間建立的連接數(shù)也更少,通過(guò)Agent也能方便地設(shè)置數(shù)據(jù)壓縮算法,由于批量發(fā)送的原因壓縮效率比較高。在我們開(kāi)啟了Zstd壓縮后,Kafka集群的寫(xiě)入帶寬降低了37%,極大地緩解了Kafka集群的壓力。
4. MQ優(yōu)化-Kafka遷移BMQ
在埋點(diǎn)數(shù)據(jù)流這種大流量場(chǎng)景下使用Kafka,會(huì)經(jīng)常遇到Broker或者磁盤(pán)負(fù)載不均、磁盤(pán)壞掉等情況導(dǎo)致的穩(wěn)定性問(wèn)題,以及Kafka擴(kuò)容、Broker替換等運(yùn)維操作也會(huì)影響集群任務(wù)正常的讀寫(xiě)性能,除此之外Kafka還有controller性能瓶頸、多機(jī)房容災(zāi)部署成本高等缺點(diǎn)。
為了優(yōu)化這些問(wèn)題,BMQ這款字節(jié)跳動(dòng)自研的存儲(chǔ)計(jì)算分離的MQ應(yīng)運(yùn)而生。BMQ的數(shù)據(jù)存儲(chǔ)使用了HDFS分布式存儲(chǔ),每個(gè)Partition的數(shù)據(jù)切分為多個(gè)segment,每個(gè)segment對(duì)應(yīng)一個(gè)HDFS文件,Proxy和Broker都是無(wú)狀態(tài)的,因此可以支持快速的擴(kuò)縮容,并且由于沒(méi)有數(shù)據(jù)拷貝所以擴(kuò)縮容操作也不會(huì)影響讀寫(xiě)性能。
受益于HDFS已經(jīng)建設(shè)得比較完善的多機(jī)房容災(zāi)能力,BMQ多機(jī)房容災(zāi)部署就變得非常簡(jiǎn)單,數(shù)據(jù)同時(shí)寫(xiě)入所有容災(zāi)機(jī)房后再返回成功即可保障多機(jī)房容災(zāi)。數(shù)據(jù)消費(fèi)是在每個(gè)機(jī)房讀取本地的HDFS進(jìn)行消費(fèi),減少了跨機(jī)房帶寬。除此之外,由于基于多機(jī)房HDFS存儲(chǔ)比Kafka集群多機(jī)房部署所需的副本更少,所以最終實(shí)現(xiàn)了單GB流量成本對(duì)比Kafka下降了50%的資源收益。
5. 成本治理-埋點(diǎn)治理
在埋點(diǎn)治理方面,通過(guò)對(duì)流量平臺(tái)的建設(shè),提供了從埋點(diǎn)設(shè)計(jì)、埋點(diǎn)注冊(cè)、埋點(diǎn)驗(yàn)證、埋點(diǎn)上報(bào)、埋點(diǎn)采樣、流式ETL處理,再到埋點(diǎn)下線的埋點(diǎn)全生命周期的管理能力。
6. 成本治理-埋點(diǎn)管控
目前字節(jié)跳動(dòng)所有的產(chǎn)品都開(kāi)啟了埋點(diǎn)管控。所有的埋點(diǎn)都需要在我們的流量平臺(tái)上注冊(cè)埋點(diǎn)元數(shù)據(jù)之后才能上報(bào)。而我們的埋點(diǎn)數(shù)據(jù)流ETL也只會(huì)處理已經(jīng)注冊(cè)的埋點(diǎn),這是從埋點(diǎn)接入流程上進(jìn)行的管控。
在埋點(diǎn)上報(bào)環(huán)節(jié),通過(guò)在流量平臺(tái)配置埋點(diǎn)的采樣率對(duì)指定的埋點(diǎn)進(jìn)行采樣上報(bào),在一些不需要統(tǒng)計(jì)全量埋點(diǎn)的場(chǎng)景能顯著地降低埋點(diǎn)的上報(bào)量。
對(duì)于已經(jīng)上報(bào)的埋點(diǎn),通過(guò)埋點(diǎn)血緣統(tǒng)計(jì)出已經(jīng)沒(méi)有在使用的埋點(diǎn),自動(dòng)通知埋點(diǎn)負(fù)責(zé)人在流量平臺(tái)進(jìn)行自助下線。埋點(diǎn)下線流程完成后會(huì)通過(guò)服務(wù)端動(dòng)態(tài)下發(fā)配置到埋點(diǎn)SDK以及埋點(diǎn)數(shù)據(jù)流ETL任務(wù)中,確保未注冊(cè)的埋點(diǎn)在上報(bào)或者ETL環(huán)節(jié)被丟棄掉。還支持通過(guò)埋點(diǎn)黑名單的方式對(duì)一些異常的埋點(diǎn)進(jìn)行動(dòng)態(tài)的封禁。
7. 埋點(diǎn)治理-埋點(diǎn)分級(jí)
埋點(diǎn)分級(jí)主要是針對(duì)離線存儲(chǔ)成本進(jìn)行優(yōu)化,首先在流量平臺(tái)上對(duì)埋點(diǎn)進(jìn)行分級(jí),埋點(diǎn)數(shù)據(jù)流ETL任務(wù)會(huì)將分級(jí)信息寫(xiě)入到埋點(diǎn)數(shù)據(jù)中。埋點(diǎn)數(shù)據(jù)在從MQ Dump到HDFS這個(gè)階段根據(jù)這些分級(jí)的信息將埋點(diǎn)數(shù)據(jù)寫(xiě)入不同的HDFS分區(qū)路徑下。然后通過(guò)不同的Spark任務(wù)消費(fèi)不同分級(jí)分區(qū)的HDFS數(shù)據(jù)寫(xiě)入Hive Table。不同等級(jí)的分區(qū)可以?xún)?yōu)先保障高優(yōu)埋點(diǎn)的產(chǎn)出,另外不同分區(qū)也可以配置不同的TTL,通過(guò)縮減低優(yōu)數(shù)據(jù)的TTL節(jié)省了大量的存儲(chǔ)資源。
--
04
未來(lái)規(guī)劃
目前Flink能做到計(jì)算層面的流批一體,但計(jì)算和存儲(chǔ)的流批一體還在探索階段,接下來(lái)我們也會(huì)繼續(xù)關(guān)注社區(qū)的進(jìn)展。另外我們會(huì)嘗試探索一些云原生的實(shí)時(shí)數(shù)據(jù)處理框架,嘗試解決資源動(dòng)態(tài)rescale的問(wèn)題,以此來(lái)提升資源利用率。最后是在一些高優(yōu)鏈路上,我們希望保障更高的SLA,比如端到端的exactly-once語(yǔ)義。
相關(guān)技術(shù)實(shí)踐已經(jīng)通過(guò)火山引擎數(shù)據(jù)中臺(tái)產(chǎn)品對(duì)外輸出,大家感興趣的話也可以登陸火山引擎的官網(wǎng)進(jìn)行了解。
最后打個(gè)小廣告這是我們字節(jié)跳動(dòng)數(shù)據(jù)平臺(tái)的官方公眾號(hào),我們會(huì)在上面分享我們數(shù)據(jù)平臺(tái)的技術(shù)干貨、產(chǎn)品動(dòng)態(tài)和招聘信息。
--
05
精彩問(wèn)答
Q:業(yè)務(wù)方查不到自己的埋點(diǎn)數(shù)據(jù)或者和業(yè)務(wù)系統(tǒng)的埋點(diǎn)數(shù)據(jù)對(duì)比UV不對(duì),字節(jié)跳動(dòng)是否會(huì)遇到這種情況,你們排查的思路是怎樣的?
A:在埋點(diǎn)治理環(huán)節(jié)有講到,在字節(jié)跳動(dòng)我們把埋點(diǎn)的整個(gè)生命周期都管理起來(lái)了,從埋點(diǎn)設(shè)計(jì)開(kāi)始,到埋點(diǎn)先注冊(cè)后上報(bào)的管控流程。在埋點(diǎn)上報(bào)之前需要通過(guò)平臺(tái)提供的埋點(diǎn)驗(yàn)證功能驗(yàn)證埋點(diǎn)是否上報(bào)成功以及埋點(diǎn)字段跟它注冊(cè)的元數(shù)據(jù)是否是匹配的。我們通過(guò)這樣的流程可以很大程度上減少埋點(diǎn)上報(bào)缺失和埋點(diǎn)質(zhì)量問(wèn)題。
Q:中間隊(duì)列用MQ不用Kafka是出于什么樣的考慮?
A:如果是問(wèn)我們?yōu)槭裁磸腒afka切換到BMQ,可以了解一下Kafka和一些存儲(chǔ)計(jì)算分離MQ的對(duì)比如Apache Pulsar,通常來(lái)說(shuō)存儲(chǔ)計(jì)算分離架構(gòu)的MQ會(huì)具備更好的伸縮性以及不會(huì)因?yàn)閿?shù)據(jù)復(fù)制帶來(lái)讀寫(xiě)性能的下降,成本也會(huì)更低,會(huì)更貼近云原生的形態(tài)。
Q:埋點(diǎn)的數(shù)據(jù)質(zhì)量治理還有什么好的例子嗎?
A:我們現(xiàn)在做的比較多的是埋點(diǎn)上報(bào)前的埋點(diǎn)質(zhì)量治理,比如剛才提到了在上報(bào)前通過(guò)注冊(cè)埋點(diǎn)的信息開(kāi)發(fā)完埋點(diǎn)后用驗(yàn)證工具去做自動(dòng)化測(cè)試以保障埋點(diǎn)開(kāi)發(fā)的準(zhǔn)確性和質(zhì)量,上報(bào)后我們也會(huì)用一些離線工具對(duì)埋點(diǎn)質(zhì)量進(jìn)行監(jiān)控,但從我們的經(jīng)驗(yàn)上來(lái)看,上報(bào)前埋點(diǎn)質(zhì)量的把控會(huì)解決大部分的問(wèn)題,上報(bào)后的埋點(diǎn)質(zhì)量治理目前主要是離線的,也能解決一些場(chǎng)景的問(wèn)題。
Q:埋點(diǎn)數(shù)據(jù)準(zhǔn)確性怎么保障的?埋點(diǎn)數(shù)據(jù)處理的過(guò)程中是給到一個(gè)什么樣級(jí)別的保障?
A:目前我們的處理鏈路是基于Flink實(shí)現(xiàn)的,大多數(shù)場(chǎng)景是沒(méi)有開(kāi)啟checkpoint,原因是我們的下游需求很多樣化,有的下游不接受數(shù)據(jù)的大量重復(fù),有的下游不接受數(shù)據(jù)延遲,如果開(kāi)checkpoint,會(huì)在任務(wù)Failover過(guò)程中有大量的數(shù)據(jù)重復(fù)和延遲,這個(gè)是下游沒(méi)有辦法接受的。我們通過(guò)SLA數(shù)據(jù)質(zhì)量監(jiān)控指標(biāo)是可以觀測(cè)出整個(gè)鏈路各個(gè)環(huán)節(jié)的數(shù)據(jù)丟失率和重復(fù)率,通過(guò)SLA指標(biāo)來(lái)反映和保障數(shù)據(jù)準(zhǔn)確性。
今天的分享就到這里,謝謝大家。
閱讀更多技術(shù)干貨文章、下載講師PPT,請(qǐng)關(guān)注微信公眾號(hào)“DataFunTalk”。
分享嘉賓:劉石偉 字節(jié)跳動(dòng) 埋點(diǎn)數(shù)據(jù)流技術(shù)負(fù)責(zé)人
編輯整理:Rissy 易顯智能科技
出品平臺(tái):DataFunTalk