餓了么BDI-大數(shù)據(jù)平臺研發(fā)團(tuán)隊(duì)目前共有20人左右,主要負(fù)責(zé)離線&實(shí)時(shí) Infra 和平臺工具開發(fā),其中包括20+組件的開發(fā)和維護(hù)、2K+ Servers 運(yùn)維及數(shù)據(jù)平臺周邊衍生工具研發(fā)&維護(hù)。離線 Infra 和平臺工具這一塊對外分享的比較多。
今天主要給大家講講餓了么在實(shí)時(shí)計(jì)算平臺方面的一些演進(jìn)經(jīng)驗(yàn),整個(gè)實(shí)時(shí)平臺也是經(jīng)歷了從無到有→快速發(fā)展→平臺化的階段,每個(gè)階段都面臨了不同的問題。
首先介紹下目前餓了么實(shí)時(shí)平臺的整體規(guī)模:
4 Kafka 集群,單 Kafka 高峰100wmsg/s;
2 ELK 集群用來做日志檢索,全網(wǎng)索引量86w/s;
4 Storm 集群,根據(jù)業(yè)務(wù) SLA 進(jìn)行物理拆分,全網(wǎng)高峰計(jì)算量1.6kw/s;
2 Spark Streaming 集群和2個(gè) Flink 集群,都是 On Yarn 模式,其中 Flink 在做一些線上業(yè)務(wù)嘗試;
20個(gè)以上業(yè)務(wù)方接入,共120+ Storm 任務(wù)(包括雙活 & DataPipeline 業(yè)務(wù))、26+ Spark Streaming 任務(wù)、10+ Streaming SQL 任務(wù),涉及實(shí)時(shí)搜索推薦、實(shí)時(shí)風(fēng)控、實(shí)時(shí)監(jiān)控、實(shí)時(shí)營銷等項(xiàng)目。
整體架構(gòu)圖如下:

可以看到,其中包括了數(shù)據(jù)采集、傳輸、計(jì)算、落地還有服務(wù)。組件多且系統(tǒng)壓力大,部分業(yè)務(wù)直接應(yīng)用于關(guān)鍵路徑,要求整體的 SLA 要在99.99%以上。
整體也經(jīng)歷幾個(gè)階段:從無到有、快速發(fā)展、平臺化。
餓了么從15年5月份上線實(shí)時(shí)計(jì)算,這個(gè)階段面臨的問題:
需求少。實(shí)時(shí)業(yè)務(wù)在公司推廣不夠,只有一個(gè) UBT Domain QoS 分析需求,用來計(jì)算網(wǎng)站錯(cuò)誤率、HTTPCode 分布、頁面加載速度等 QoS 數(shù)據(jù);
數(shù)據(jù)源單一。數(shù)據(jù)源只有用戶行為 Log,缺乏訂單、運(yùn)單等核心 DB 數(shù)據(jù);
容量有限。集群規(guī)模不到20臺,且和離線混部,要同時(shí)支持實(shí)時(shí)+離線需求,存在資源競爭;
穩(wěn)定性問題。缺乏統(tǒng)一的使用標(biāo)準(zhǔn)姿勢,各組件應(yīng)用問題也比較多。比如每種應(yīng)用需要什么類型的機(jī)器、怎么樣最佳化配置……同時(shí)因?yàn)槭褂玫拈_源組件版本較舊,存在一些穩(wěn)定性 bug;
數(shù)據(jù)延遲和數(shù)據(jù)不全。因?yàn)樵诔跏技夹g(shù)選項(xiàng)上存在問題,在數(shù)據(jù)采集端使用了自行開發(fā)的 Python 程序,同時(shí)使用了跨機(jī)房的傳輸,導(dǎo)致經(jīng)常出現(xiàn)數(shù)據(jù)丟失和延遲問題。最嚴(yán)重時(shí),實(shí)時(shí)的數(shù)據(jù)在高峰期要 Delay 2個(gè)小時(shí)以上,基本不可用。
這個(gè)階段主要解決了環(huán)境、穩(wěn)定性、數(shù)據(jù)延遲和數(shù)據(jù)丟失的問題,主要做了如下工作:
針對環(huán)境和標(biāo)準(zhǔn)化問題:
針對穩(wěn)定性問題:
在數(shù)據(jù)傳輸側(cè),重新調(diào)研數(shù)據(jù)傳輸方案,考慮到團(tuán)隊(duì)以 Java 為技術(shù)棧,以及外部案例,引入了 Flume 作為數(shù)據(jù)采集管道,以 Tail Log 的形式進(jìn)行數(shù)據(jù)采集并 Sink 到 Kafka 集群,并基于 HDFS Sink 開發(fā)根據(jù) EventTime 的 Partition 功能,同時(shí) fix Backlog 和 Kafka Sink 的 bug;
在數(shù)據(jù)落地側(cè),為了存儲中間狀態(tài)結(jié)果,引入 KV 存儲。最初使用單機(jī) Redis 存儲數(shù)據(jù),set 去重,遇到了嚴(yán)重的性能問題,所以后面開始逐步使用 Self Sharding->Redis+Tewmproxy 的方式,但是維護(hù)成本比較高。后面隨著 RedisCluster 穩(wěn)定版 Release 開始逐步遷移到 Cluster 模式,這個(gè)階段公司在 NoSQL 的經(jīng)驗(yàn)一直不足,所以都是團(tuán)隊(duì)內(nèi)部不斷地摸索前進(jìn)。
這個(gè)階段實(shí)時(shí)架構(gòu)是這樣的:

這個(gè)階段整個(gè)平臺研發(fā)只有4個(gè)人,既要負(fù)責(zé)離線、實(shí)時(shí)、平臺工具的開發(fā)和維護(hù),還要支撐業(yè)務(wù)的開發(fā),資源比較緊張,實(shí)時(shí)方面投入捉襟見肘。
雖然解決了基本的穩(wěn)定性 & 數(shù)據(jù)延遲和丟失的問題,但是整體鏈路 SLA 還是不高,同時(shí)還存在數(shù)據(jù)源單一、應(yīng)用單一的問題。
16年公司業(yè)務(wù)大力發(fā)展,實(shí)時(shí)方面的需求越來越多。SLA 不高、數(shù)據(jù)源單一、應(yīng)用單一的問題亟待解決。
由于業(yè)務(wù)需求,需開發(fā)實(shí)時(shí) Dashboard 來實(shí)時(shí)關(guān)注業(yè)務(wù)情況,涉及流量、訂單、運(yùn)單等重要數(shù)據(jù),項(xiàng)目需要涉及不同的數(shù)據(jù)源(Log & DB &業(yè)務(wù)數(shù)據(jù)),同時(shí)要求 SLA 99.99%以上。
為了提高整體的 SLA,同時(shí)覆蓋 DB 側(cè)的數(shù)據(jù)源,針對整個(gè)鏈路做了如下調(diào)整優(yōu)化:
數(shù)據(jù)源方面:
計(jì)算方面:
前面提到用戶 Log 是合并發(fā)送,在 Kafka 中的表現(xiàn)是多條 Merge 成一條,應(yīng)用如果需要使用的話,需要按照一定的規(guī)則 Split。同時(shí)每個(gè)業(yè)務(wù)關(guān)注的 Type 不一樣,不同的業(yè)務(wù)需要全量消費(fèi)所有 Log,同時(shí)需要自行進(jìn)行 Split,計(jì)算量大,維護(hù)成本比較高。為了解決這個(gè)問題引入了雙層 Kafka 結(jié)構(gòu),在第一層進(jìn)行統(tǒng)一的 Split 和 Filter,過濾異常流量,同時(shí)分 Type 寫入二層 Topic,這樣每個(gè)消費(fèi)方只需消費(fèi)對應(yīng)的數(shù)據(jù)部分即可,整體流量相關(guān)業(yè)務(wù)計(jì)算量對比之前降低了一半以上;
涉及 UV 計(jì)算的場景,初始使用 Redis Set 去重,但是內(nèi)存消耗過大。由于 UV 指標(biāo)允許1%以內(nèi)誤差,在精度和時(shí)空效率上做 Trade Off,轉(zhuǎn)而使用 Redis 的 HLL 來估算。隨著業(yè)務(wù)量的增大,Redis 的 QPS 成為瓶頸,同時(shí) Redis 無法跨實(shí)例進(jìn)行 HLL 的 Merge,又演化為基于內(nèi)存的 HLL 估算和 Merge,同時(shí)使用 Redis 直接存儲對象,節(jié)省百倍內(nèi)存的同時(shí),支持多維度的 Merge 操作;
同時(shí)考慮到多個(gè)系統(tǒng)共用 ZK,ZK 可能存在比較大的壓力,因此通過分析 ZK 的 Transcation Log 來確定調(diào)用分布。比如通過分析發(fā)現(xiàn) Storm Worker 的 Heartbeat 會頻繁訪問 ZK,因此通過增加 Heartbeat Commit 時(shí)間減少 ZK 的壓力;
為了減少重復(fù)的代碼開發(fā),對基礎(chǔ)組件進(jìn)行了封裝:包括數(shù)據(jù)消費(fèi)、去重、累加、數(shù)據(jù)寫入等算子,最終減少了部分任務(wù)50%的代碼量,提高了整體的開發(fā)效率,讓用戶關(guān)注業(yè)務(wù)邏輯即可。

封裝組件列表
運(yùn)維管理方面:
為了解整體的容量情況,開發(fā)實(shí)時(shí)容量看板,通過實(shí)時(shí)獲取 Zabbix Item LastValue 來監(jiān)控 Storm & RedisCluster 實(shí)時(shí)壓力情況;
為方便用戶可以快速查看任務(wù) Log 引入 ELK,同時(shí)在 Kafka2es 這一層引入 Hangout 替代 Flume (可支持3x以上性能提升),最終實(shí)現(xiàn)了 Storm Top Log->Logstash->Kafka->Hangout->ES->Kanbana 的整個(gè) Log 鏈路。
整體 SLA 增強(qiáng)方面:
通過上述的一系列調(diào)整,最終抗住業(yè)務(wù)幾倍的流量增長,保證了整體服務(wù)的穩(wěn)定性。
業(yè)務(wù)監(jiān)控 Dashboard 部分示例:

這個(gè)階段實(shí)時(shí)平臺的主要用戶還是大數(shù)據(jù)自身,應(yīng)用架構(gòu)如下:

這個(gè)階段雖然解決了數(shù)據(jù)源單一、整體 SLA 不高的問題,但是也帶來了新的問題:
17年初各產(chǎn)研逐步接入實(shí)時(shí)計(jì)算,上述問題也逐漸暴露出來,平臺層面亟需一個(gè)統(tǒng)一的方案來解決用戶的痛點(diǎn)。因此在年初,我們確定了“以 ERDP 實(shí)時(shí)平臺為核心,打通數(shù)據(jù)采集、數(shù)據(jù)傳輸、數(shù)據(jù)計(jì)算、數(shù)據(jù)落地 DataPipeline 整體流程,為用戶提供一個(gè)一站式的實(shí)時(shí)平臺”的方向。
在此目標(biāo)之上,我們做了如下的調(diào)整:
開發(fā)資源聚焦:
解決數(shù)據(jù)采集痛點(diǎn):
解決數(shù)據(jù)傳輸接入痛點(diǎn):
為支持更細(xì)粒度的任務(wù)調(diào)度,在 EDSink 中集成基于 EventTime 分區(qū)功能,可以支持分鐘粒度分區(qū),結(jié)合 Spark 來支持半小時(shí) ETL 鏈路的開發(fā),小時(shí)整體鏈路從之前的40min縮短到20min左右即可完成;
同時(shí)和 Binlog 解析工具聯(lián)動打通,支持用戶自助申請落地 DB 數(shù)據(jù),目前基于此方案,團(tuán)隊(duì)在進(jìn)行 DB 數(shù)據(jù)去 Sqoop 化,預(yù)計(jì)可大大節(jié)省線上 DB Slave 服務(wù)器成本。
提供更多的計(jì)算方式:
引入 Spark Streaming 并集成到 ERDP 平臺,封裝基本的 Spark Streaming 算子,用戶可以通過平臺對 Spark Streaming 任務(wù)進(jìn)行管理;
考慮到需要支持部分 SQL 的需求,在 Spark Streaming、Flink、Storm CQL 等引擎中做了對比,從團(tuán)隊(duì)的技術(shù)棧、引擎的成熟度、穩(wěn)定性等層面綜合考慮最終選擇了 Spark Streaming。并基于 Spark Streaming 的 SQL 功能,為用戶封裝基本算子,同時(shí)支持上傳 Jar 包提供 UDF 功能及 Scala 腳本支持,支持 Structured Streaming 以支持帶狀態(tài)的增量計(jì)算,實(shí)現(xiàn)用戶寫 SQL 即可滿足實(shí)時(shí)開發(fā)的需求(目前可支持90%的業(yè)務(wù)場景)。
自動化&自助化便于任務(wù)和資源管理:
通過打通各個(gè)資源申請流程,支持 Kafka Topic 等資源的自助化申請和自動化創(chuàng)建,基于 Topic 數(shù)據(jù)完善元數(shù)據(jù)的管理,為資源的核算和實(shí)時(shí)元數(shù)據(jù)血緣做數(shù)據(jù)基礎(chǔ);
為了方便任務(wù)的監(jiān)控,將 Storm,SparkStreaming,Kafka 層面監(jiān)控統(tǒng)一入庫 InfluxDB,并自動化模板生成功能,用戶無需手動添加監(jiān)控和報(bào)警,任務(wù)上線后 Metric & Dashboard 自動上報(bào)和創(chuàng)建,通過自動采集 API 的數(shù)據(jù)寫入 InfluxDB,同時(shí)做了一個(gè)標(biāo)準(zhǔn)的 Template 用來自動生成 Grafana 的監(jiān)控模板。

Kafka監(jiān)控示例
通過上述一系列的調(diào)整,最終完善了整個(gè)平臺,解決了用戶開發(fā)成本高、接入成本高、管理成本高等痛點(diǎn),最終的架構(gòu)圖就是文章開始的狀況。
雖然經(jīng)過了一些演進(jìn),現(xiàn)有的平臺仍然存在一些問題,比如:
SQL 方式覆蓋場景有限;
用戶在引擎中選擇困難,沒有一個(gè)引擎可以解決大部分需求;
Kafka0.8.2 版本功能有限,不支持 Excatly Once,不支持 JBOD Markdown 等;
實(shí)時(shí)和離線分離,數(shù)據(jù)重復(fù)建設(shè),因?yàn)閷?shí)現(xiàn)方式不同,實(shí)時(shí)和離線很難做到數(shù)據(jù)口徑完全一致;
實(shí)時(shí)業(yè)務(wù)場景在公司內(nèi)覆蓋不夠;
……
因此針對這些痛點(diǎn)我們也在做如下嘗試:
Flink 以其性能&易用性在實(shí)時(shí)計(jì)算領(lǐng)域開始大行其道,我們也在做一些業(yè)務(wù)試點(diǎn);
實(shí)時(shí)和離線融合 CEP 場景的試水;
Kafka 新版本的引入,包括 Qouta 限速、JBOD Markdown、Stream API、Excatly Once 等功能的支持;
實(shí)時(shí)平臺集成到統(tǒng)一的一體化平臺,用戶在一個(gè)平臺完成實(shí)時(shí) & 離線開發(fā);
發(fā)掘線上的業(yè)務(wù)場景,比如我們目前和策略部門合作的實(shí)時(shí)營銷項(xiàng)目就是通過用戶的行為數(shù)據(jù)來做一些策略,提高轉(zhuǎn)化率。
最后說一下關(guān)于平臺化演進(jìn)中的心得:
學(xué)會資源聚集(善于借助外力,must to have VS nice to have);
MVP 最小化可行性調(diào)研(產(chǎn)品是迭代出來的,不是一蹴而就的,先完成后完美);
偷懶思想,不重復(fù)造輪子(他山之石可以攻玉);
賦能用戶,盡量自動化&自助化(提高效率,解放生產(chǎn)力);
數(shù)據(jù)化運(yùn)營(用數(shù)據(jù)說話);
資源隔離,重點(diǎn)業(yè)務(wù)的 SLA 保證(降級限流策略,反壓功能等);
做好監(jiān)控(全鏈路的監(jiān)控,并做必要測試);
防范墨菲定律,及時(shí)做好容量規(guī)劃 & 壓測,同時(shí)不斷完善 SOP;
抽象思維(從一個(gè)到一類問題的抽象);
以解決實(shí)際問題為目的,而不是炫技;
關(guān)注用戶體驗(yàn)(做完了 VS 做好了);
防火勝于救火,多想幾步,不斷總結(jié)并完善標(biāo)準(zhǔn) & 規(guī)劃 & 流程(上線規(guī)范/ Checklist / SOP 流程等)。
近期熱文
云象區(qū)塊鏈CEO黃步添 | 分布式賬本
《白話區(qū)塊鏈》蔣勇 | 白話區(qū)塊鏈技術(shù)棧與應(yīng)用
元界CTO陳浩 | 從區(qū)塊鏈即服務(wù)(BaaS)到價(jià)值互聯(lián)網(wǎng)