www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁(yè) > 公眾號(hào)精選 > 架構(gòu)師社區(qū)
[導(dǎo)讀]來(lái)自:阿里巴巴中間件 文? |??挽晴 個(gè)人簡(jiǎn)介: 2014年12月加入餓了么,當(dāng)時(shí)參與后臺(tái)系統(tǒng)的研發(fā)(Walis+Javis=>Walle),主要面向客服和BD。 2015年5月開(kāi)始接觸訂單系統(tǒng)的研發(fā),7月負(fù)責(zé)訂單研發(fā)組;度過(guò)單體應(yīng)用到服務(wù)化這個(gè)階段。 2016年初搭建訂單的測(cè)試團(tuán)隊(duì),


千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!

來(lái)自:阿里巴巴中間件

文  |  挽晴


個(gè)人簡(jiǎn)介:
2014年12月加入餓了么,當(dāng)時(shí)參與后臺(tái)系統(tǒng)的研發(fā)(Walis+Javis=>Walle),主要面向客服和BD。
2015年5月開(kāi)始接觸訂單系統(tǒng)的研發(fā),7月負(fù)責(zé)訂單研發(fā)組;度過(guò)單體應(yīng)用到服務(wù)化這個(gè)階段。
2016年初搭建訂單的測(cè)試團(tuán)隊(duì),訂單拆分為正逆向后,主要負(fù)責(zé)正向和交付部分。
2017年做了一些平臺(tái)搭建的探索。
2018年初負(fù)責(zé)整個(gè)訂單正逆向和交付,年中將下單、購(gòu)物車(chē)部分一起歸并,年底和商戶(hù)訂單部分整合,形成交易中臺(tái)。
2019年10月從交易中臺(tái)轉(zhuǎn)出,近期做了一小段時(shí)間的組織效能和架構(gòu)。


我為什么會(huì)寫(xiě)這篇文章,究其緣由:
 
一是自己在交易域做了 4 年,有很多只有我才知道,才能串起來(lái)的故事,想把這些記錄并保留下來(lái)。

二是發(fā)現(xiàn)后邊的很多同學(xué)看交易體系時(shí),一接觸就是分布式、SOA、每日百萬(wàn)、千萬(wàn)數(shù)據(jù)量,只知道它是這個(gè)樣子,很難理解背后的思考和緣由。伴隨自己這幾年的經(jīng)驗(yàn),想讓大家能夠更容易的理解這個(gè)演化過(guò)程的原因和歷程,有甘有苦。

三是很多總結(jié)也好,方法論也好,更多是去除了“糟粕”呈現(xiàn)在大家面前,這里可能會(huì)稍微加一點(diǎn)“毒雞湯”,現(xiàn)實(shí)不一定那么美好,我們有很多抉擇,現(xiàn)在回過(guò)頭來(lái)看,也許是慶幸,也許是錯(cuò)誤。
 
這篇文章希望通過(guò)一些發(fā)展的故事和思考來(lái)給讀者呈現(xiàn)整個(gè)歷程,大家可以看到非常多野蠻生長(zhǎng)的痕跡,并會(huì)附帶一些思考和總結(jié),但不會(huì)像快餐式的總結(jié)很多大道理。

那我們就從2012年的太古時(shí)期講起。
 

太古



在談?dòng)唵沃?,我們往前再考古考古,在太古時(shí)代,有一套使用 Python 寫(xiě)的系統(tǒng),叫做 Zeus 的系統(tǒng),這個(gè) Zeus 包含了當(dāng)時(shí)餓了么最核心的幾大模塊,比如訂單、用戶(hù)、餐廳,這些統(tǒng)統(tǒng)在一個(gè)代碼庫(kù)中,并且部署在同一臺(tái)機(jī)器, Zeus 之外還有兩大核心,即餓了么 PC ,也就是很多老人常提的「主站」,以及面向商戶(hù)的 NaposPC 。這些系統(tǒng)通過(guò) Thrif 協(xié)議通信。除開(kāi)這條鏈路之外,所有雜亂的內(nèi)部功能,全在一個(gè)叫 walle 的系統(tǒng)中,這個(gè) Walle 系統(tǒng)是采用 PHP 寫(xiě)的。

 
那么當(dāng)時(shí)的 Zeus ,大概長(zhǎng)這個(gè)樣子:
                                 千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!
 據(jù)不嚴(yán)格考究,從 Git 的提交歷史看,訂單部分的第一個(gè) commit 是余立鑫同學(xué)于 2012 年 9 月 1 日提交的,內(nèi)容是" add eos service for zeus. currently only defind a simple get api. ",這個(gè) EOS 指的就是訂單系統(tǒng),即 ElemeOrderService 的簡(jiǎn)稱(chēng),這個(gè)名詞沿用到了今天,成為交易正向的訂單部分,甚至一段時(shí)間是訂單組的代名詞。
 
 Zeus 在后來(lái)其實(shí)經(jīng)過(guò)了一定的重構(gòu),叫做 Zeus2 ,但具體時(shí)間已不可考。
 

萌芽



2014 年 10 月我到餓了么來(lái)面試,面試官是商戶(hù)端負(fù)責(zé)人磊哥。 12 月 1 日,我入職餓了么, HR 領(lǐng)著帶著一臉萌新的我,到磊哥面前時(shí),磊哥把我?guī)У?JN 面前說(shuō),“這就是那個(gè)實(shí)習(xí)生”,然后扭頭就跑了。后來(lái)得知,當(dāng)時(shí)面試結(jié)束后,磊哥和 JN 同學(xué)說(shuō),剛剛面了一個(gè)實(shí)習(xí)生,湊合能用,正巧商戶(hù)組有計(jì)劃轉(zhuǎn)型 Java ,而佳寧還很缺 python 的人,然后就騙了 JN 一頓飯把我賣(mài)了。

 
回到正題,在 2014 年 12 月~ 2014 年 4 月這幾個(gè)月的時(shí)間里,我配合完成了一個(gè)更老的 BD 系統(tǒng)后端遷移到 Walis ,并且在我的導(dǎo)師轉(zhuǎn)崗到 CI 團(tuán)隊(duì)后,自己完成了 Walis 從單應(yīng)用遷移到分布式應(yīng)用。
 

訂單組的成立

 
對(duì)我來(lái)說(shuō),完全是運(yùn)氣和緣分...
  
接近 2015 年 5 月的時(shí)候,我的主管,JN同學(xué),有一天突然找到我,看起來(lái)很興奮,告訴我,公司打算成立一個(gè)訂單組,這個(gè)訂單組由他來(lái)負(fù)責(zé),除了他之外,他唯獨(dú)選中了我(大概是因?yàn)樯隙挝姨岬降囊恍┙?jīng)歷,在可選的人里,還湊合~),說(shuō)是我怎么怎么讓他相中,這個(gè)男人忽悠起人來(lái),一套一套的。
 
作為一個(gè)技術(shù)人員,內(nèi)心非常沸騰。一是高并發(fā)、高流量、分布式這些耳熟能詳?shù)母叽笊厦~之前只是聽(tīng)說(shuō)過(guò),不曾想這么快就能夠接觸到這樣的系統(tǒng);二是我們此前做的系統(tǒng)很“邊緣”,有多邊緣呢,白天幾乎沒(méi)什么請(qǐng)求, BD 走訪(fǎng)商戶(hù)回來(lái),恰巧晚上才是高峰期,即使是晚上,關(guān)鍵的單接口也就偶爾幾個(gè)、十幾個(gè)請(qǐng)求,是當(dāng)時(shí)那種掛 2 個(gè)小時(shí)才可能有人發(fā)現(xiàn),掛半天不一定有人叫的系統(tǒng),那時(shí)候我們幸福的晚上 7 點(diǎn)前就下班了,第一次發(fā)布的時(shí)候非常鄭重的和我說(shuō),可能要加班到晚上 8 點(diǎn)半。
 
之所以選擇 JN 做訂單組負(fù)責(zé)人,因?yàn)樗m然是個(gè)前端工程師起家,做的是“邊緣”后臺(tái)系統(tǒng),但卻是對(duì)整個(gè)公司所有系統(tǒng)和業(yè)務(wù)都比較熟悉的人,很適合發(fā)展訂單系統(tǒng)。
 
嗯,沒(méi)錯(cuò),這個(gè)組在成立前一天,一直只有我們兩個(gè)人。當(dāng)時(shí)的我還沒(méi)畢業(yè),除了興奮,更多的是忐忑。
 
2015 年 5 月 12 日,訂單組正式成立,成立當(dāng)天,拉來(lái)了隔壁組的 ZH (是個(gè)PHPer,招進(jìn)來(lái)的時(shí)候是計(jì)劃去接Walle),然后聊到一半的時(shí)候,當(dāng)時(shí)的部門(mén)總監(jiān)跑過(guò)來(lái),說(shuō)正巧有個(gè)小哥哥當(dāng)天入職,還不錯(cuò),正好給訂單組吧,是個(gè) Java 工程師。于是乎,成立當(dāng)天,我們?nèi)藬?shù)翻了一倍,變成了 4 個(gè)人。
 
我們給自己的第一個(gè)任務(wù): 讀代碼,理業(yè)務(wù),畫(huà)圖。和 CTO 申請(qǐng)到了 1 個(gè)月的時(shí)間來(lái)緩沖,這段時(shí)間不接任何業(yè)務(wù)需求!

分別請(qǐng)來(lái)了訂單的前主程、Python 框架負(fù)責(zé)人、Zeus 系應(yīng)用運(yùn)維負(fù)責(zé)人給我們講解。實(shí)際上,每個(gè)人的分享也就 1 個(gè)多小時(shí)。那一個(gè)月真是從幾萬(wàn)行 Python 代碼,沒(méi)有任何產(chǎn)品文檔,極其稀少的注釋?zhuān)恍行械目?,每個(gè)人解讀一部分。我最后匯總把整個(gè)訂單的生命周期、關(guān)鍵操作、關(guān)鍵業(yè)務(wù)邏輯,畫(huà)在了一張大圖里,這張圖,我們后來(lái)用了一年多。
 
其實(shí),當(dāng)時(shí)年中旬的餓了么,產(chǎn)研規(guī)模已經(jīng)達(dá)到幾百人左右,新 CTO ,雪峰老師是年初加入餓了么,整個(gè)基礎(chǔ)設(shè)施的起步是 2015 年下半年,整個(gè)體系的飛速搭建是在 2016 年。
       
可以說(shuō)是正處于相當(dāng)混亂,又高速發(fā)展的時(shí)期。我們稱(chēng)那個(gè)時(shí)間是一邊開(kāi)著跑車(chē)一邊換輪胎。
 

Zeus 解耦

 
和訂單真正密切相關(guān)的第一個(gè) Super 任務(wù),大概是從 6 月左右開(kāi)始 --- Zeus 解耦,HC老師是 Python 框架的負(fù)責(zé)人,也是個(gè)人最佩服和敬仰的技術(shù)專(zhuān)家之一,在美國(guó)舉行 Qcon 上,作為首席架構(gòu)師介紹過(guò)當(dāng)時(shí)餓了么整體技術(shù)架構(gòu)。剛才在太古時(shí)期已經(jīng)說(shuō)到, Zeus 是一個(gè)巨型單體應(yīng)用,為了今后各個(gè)部分能夠快速發(fā)展,降低耦合和牽連影響等,公司啟動(dòng)了 zeus 解耦項(xiàng)目,總之就兩個(gè)字,拆分
      
經(jīng)過(guò) 1 個(gè)多月的密集會(huì)議,完成了拆分的方案。說(shuō)的似乎沒(méi)那么難,但是這場(chǎng)口水戰(zhàn)當(dāng)時(shí)打的不可開(kāi)交,拆分后不同的服務(wù)歸屬于誰(shuí)?模塊和模塊之間并沒(méi)有切分的那么干凈,A和B服務(wù)中的邊界怎么定等等一系列問(wèn)題。當(dāng)時(shí)的我還不夠格參與討論。
 
結(jié)論是, Zeus 將要拆分成下邊的幾個(gè)主服務(wù):
 
  • zeus.eos => 訂單服務(wù)
  • zeus.eus => 用戶(hù)服務(wù)
  • zeus.ers => 商家服務(wù)
  • zeus.eps => 營(yíng)銷(xiāo)服務(wù)(新產(chǎn)物)
  • zeus.sms => 短信服務(wù)
  • ...
 
第一階段
每個(gè)被拆分后的服務(wù),隨之進(jìn)行的是新的一波重構(gòu)和拆分。例如從 zeus.eos 分離出來(lái) biz.booking ,拿走了下單和購(gòu)物車(chē)部分能力;分離出來(lái) biz.ugc 拿走了訂單評(píng)價(jià)相關(guān)能力。
        千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!

拆分主要經(jīng)歷的幾個(gè)階段:
1、(7月份)共享代碼倉(cāng)庫(kù),按模塊獨(dú)立運(yùn)行。即,把 Zeus 所有代碼都打包到服務(wù)器后,按照劃分,在特定機(jī)器上只將特定模塊單獨(dú)啟動(dòng),開(kāi)放特定端口。
2、(8月份) Proxy 階段。即在原服務(wù)中,要遷出去的接口上增加一個(gè)代理,可以代理到新服務(wù)的接口,由服務(wù)注冊(cè)中心開(kāi)關(guān)能力來(lái)控制切換流量大小。
3、(8月份至9月初)腳本、模塊的完全切分改造。
4、(9月份)代碼倉(cāng)庫(kù)獨(dú)立。使用了 Git 的核彈武器 filter-branch ,將模塊中的代碼和變更歷史,完全完整的從原代碼庫(kù)中分離。而此時(shí)部署卻仍然為混布,在發(fā)布工具中,某個(gè)獨(dú)立應(yīng)用發(fā)布后實(shí)際是替換了 Zeus 這個(gè)大項(xiàng)目下的某個(gè)目錄。
5、(9月份)配置獨(dú)立。原來(lái)的配置由 saltstack 刷到服務(wù)器上,被服務(wù)器上多個(gè)應(yīng)用所共用,我們將其直接改成使用服務(wù)注冊(cè)中心的配置下發(fā)能力獲取單個(gè)應(yīng)用配置。在這個(gè)階段也基本上過(guò)渡到了軟負(fù)載。
6、(次年3月份)物理部署獨(dú)立。當(dāng)然這是解耦二期的內(nèi)容了。
 
當(dāng)然,這次拆分,還帶來(lái)了另外一個(gè)產(chǎn)物, Python 的 SOA 框架 zeus_core,zeus_core 要大概在 4 月份左右先于業(yè)務(wù)服務(wù)被拆分出來(lái)。
 
整個(gè)解耦一期,持續(xù)了大概半年時(shí)間。在期間,沒(méi)有發(fā)生因?yàn)椴鸱謱?dǎo)致的事故,也幾乎沒(méi)有什么冒煙。想想當(dāng)時(shí)沒(méi)有用什么高深的東西,工具落后,沒(méi)有專(zhuān)職測(cè)試,完全靠著一幫早期工程師和運(yùn)維同學(xué)的技術(shù)素養(yǎng)。
 
分庫(kù)分表
 
仍然是在 2015 年,大概是 9、10 月左右確定分庫(kù)分表要開(kāi)始實(shí)施,而分庫(kù)分表的方案,在我介入時(shí)已經(jīng)幾乎敲定,并由 CI 部門(mén)的 DAL 團(tuán)隊(duì)主導(dǎo)。
 
為什么要做分庫(kù)分表?
 
一是扛不住并發(fā)。 當(dāng)時(shí)我們的訂單庫(kù)的 MySQL 是采取 1 主 5 從的架構(gòu),還有 1 臺(tái)做 MHA 。DB 不太能承受住當(dāng)時(shí)的并發(fā)壓力,并且,對(duì)風(fēng)險(xiǎn)的抵抗能力非常的弱。業(yè)務(wù)如果做一些活動(dòng)沒(méi)提前告知,我們的從庫(kù)一旦掛了一個(gè),就只能來(lái)回切,嚴(yán)重的時(shí)候只能大量限流。而且,那段時(shí)間,作為技術(shù),我們也在祈禱美團(tuán)外賣(mài)別在高峰期掛,美團(tuán)外賣(mài)一旦掛了,流量就會(huì)有一部分流到餓了么,我們就開(kāi)始也緊張起來(lái)了。同樣的,那段時(shí)間,我們整站掛了,美團(tuán)外賣(mài)也不太能扛得住,大家都在經(jīng)歷相似的發(fā)展階段。
 
二是 DDL 成本太高 ,業(yè)務(wù)又處于戰(zhàn)斗高峰。當(dāng)時(shí)餓了么的單量在日均百萬(wàn)出頭。有一些業(yè)務(wù)需求,希望在訂單上新增字段,然而,我們找到 DBA 評(píng)估的時(shí)候,給的答案是,樂(lè)觀估計(jì)需要停服 3 小時(shí),悲觀估計(jì)要 5 小時(shí),并且需要 CEO 審批。顯然,這個(gè)風(fēng)險(xiǎn),技術(shù)團(tuán)隊(duì)難以接受,而業(yè)務(wù)團(tuán)隊(duì)也無(wú)法接受。那么投機(jī)取巧的方案,就是在預(yù)留的 Json 擴(kuò)展字段中不斷的塞,這種方式一定程度上緩解了很長(zhǎng)一段時(shí)間的壓力,然而,也埋下了非常多的隱患。
 
當(dāng)然,還有一些特殊的業(yè)務(wù)場(chǎng)景以及一些開(kāi)放出去顆粒度很大的接口,會(huì)產(chǎn)生一些性能極差的 SQL ,都會(huì)引爆全站。
 
Shardin 后物理結(jié)構(gòu)如下:
 
千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!
一次更新操作邏輯如下:

                        千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!
   
我們其實(shí)是做了兩維 Sharding ,兩個(gè)維度都是 120 個(gè)分片,但是可以通過(guò)三種方式路由(用戶(hù) ID、商戶(hù)ID、訂單ID),寫(xiě)入優(yōu)先保證用戶(hù)維度成功。由于資源的原因,用戶(hù)和商戶(hù)分片是交錯(cuò)混合部署的。

 (加粗部分其實(shí)是有一些坑的,這個(gè)特殊定制也是餓了么唯一,如果有興趣以后可以展開(kāi))
 
更具體分庫(kù)分表的技術(shù)細(xì)節(jié)不在這里展開(kāi),大致經(jīng)歷了幾個(gè)階段:
 
1、制定新的訂單號(hào)生成規(guī)則,并完成改造接入。
2、數(shù)據(jù)雙寫(xiě),讀舊,對(duì)比數(shù)據(jù)。
3、對(duì)不兼容的 SQL 進(jìn)行改造,比如跨分片的排序、統(tǒng)計(jì),不帶shardingkey的SQL等等。
4、數(shù)據(jù)雙寫(xiě),讀新。(與3有部分同步進(jìn)行)
5、完成數(shù)據(jù)庫(kù)切換,數(shù)據(jù)寫(xiě)新讀新。
 
這段日子,作為業(yè)務(wù)團(tuán)隊(duì),大部分時(shí)間其實(shí)花在第三部分,也曾奮斗過(guò)好幾次到凌晨3、4點(diǎn)。
 
在 2016 年的春節(jié)前夕,為了頂過(guò)業(yè)務(wù)峰值和系統(tǒng)穩(wěn)定,我們甚至把 DB 里的數(shù)據(jù)做歸檔只留最近 15 天內(nèi)的訂單
 
記得最終切換的那一天,大概在 2016 年 3 月中旬,我和幾位同學(xué)早上 5 點(diǎn)多就到了公司,天蒙蒙亮。整個(gè)餓了么開(kāi)始停服,然后阻斷寫(xiě)請(qǐng)求,完成 DB 指向的配置,核對(duì)無(wú)誤,恢復(fù)寫(xiě)請(qǐng)求,核驗(yàn)業(yè)務(wù)無(wú)誤,慢慢放開(kāi)前端流量,重新開(kāi)服。整個(gè)過(guò)程核心部分大概 10 分鐘,整個(gè)停服到完全開(kāi)放持續(xù)了半個(gè)小時(shí)。
 
到了第二天,我們才得以導(dǎo)入最近 3 個(gè)月的歷史訂單。
 
這次變更做完,我們基本擺脫了 DB 的瓶頸和痛點(diǎn)(當(dāng)然,后邊的故事告訴我們,有時(shí)候還是有點(diǎn)天真的~~~)
 
消息廣播
 
那個(gè)時(shí)期,也是在 15 年的 7 月左右,受到一些架構(gòu)文章的影響,也是因?yàn)?JN 提到了這一點(diǎn),我們決定做訂單的消息廣播,主要目的是為了進(jìn)一步解耦。
 
在調(diào)研了 RabbitMQ、NSQ、RocketMQ、Kafka、ActiveMQ 之后,我得出的最終結(jié)論,選型還是 RabbitMQ ,其實(shí)當(dāng)時(shí)我認(rèn)為,RocketMQ 更為適合,特別是順序消息的特性,在交易某些業(yè)務(wù)場(chǎng)景下能夠提供天然的支持,然而,運(yùn)維團(tuán)隊(duì)主要的運(yùn)維經(jīng)驗(yàn)是在 RabbitMQ ??蚣軋F(tuán)隊(duì)和運(yùn)維團(tuán)隊(duì)的同學(xué)很自信,自從搭建以來(lái),也沒(méi)有出過(guò)任何問(wèn)題,穩(wěn)的一匹,如果選擇 RabbitMQ ,就能夠得到運(yùn)維團(tuán)隊(duì)的天然支持,這對(duì)于我們當(dāng)時(shí)的業(yè)務(wù)團(tuán)隊(duì)來(lái)說(shuō),能夠避免很多風(fēng)險(xiǎn)。
 
于是由框架團(tuán)隊(duì)承接了對(duì) RabbitMQ 進(jìn)行一輪嚴(yán)謹(jǐn)?shù)男阅軠y(cè)試,給出部分性能指標(biāo)。這一場(chǎng)測(cè)試,最終搭建了一個(gè) 3Broker 組成的集群,單獨(dú)為訂單服務(wù),在此之前只有一個(gè) MQ 節(jié)點(diǎn),服務(wù)于 Zeus 體系的異步消息任務(wù)。
 
為了保證對(duì)交易主流程不產(chǎn)生影響,然后在 Client 端 SOA 框架進(jìn)行了一系列的容錯(cuò)改造,主要是針對(duì)連接 MQ 集群時(shí)的發(fā)送超時(shí)、斷開(kāi)等容錯(cuò),消息發(fā)送異步進(jìn)行且重試一定次數(shù)。最終全新搭建了由 3 個(gè)節(jié)點(diǎn)組成的 MQ 集群,訂單的消息最終發(fā)往這個(gè)集群。
 
期間,其實(shí)踩了一個(gè)小坑。雖然框架團(tuán)隊(duì)已經(jīng)進(jìn)行了異常情況的容錯(cuò)。但畢竟消息廣播的發(fā)送時(shí)機(jī)是和主流程狀態(tài)扭轉(zhuǎn)緊密相連的,代碼在上線(xiàn)前,當(dāng)時(shí)一向謹(jǐn)慎的我,為首次上線(xiàn)加上了一個(gè)消息發(fā)送的開(kāi)關(guān)。那是一個(gè)晚上,大概 8 點(diǎn)多,現(xiàn)在回想,當(dāng)時(shí)灰度和觀察時(shí)間是有一些短的,當(dāng)我全部發(fā)布完成后,很快,監(jiān)控上顯著看到接口開(kāi)始嚴(yán)重超時(shí)(我們當(dāng)時(shí)采用框架默認(rèn)的超時(shí)設(shè)定, 30s,其實(shí)這個(gè)配置很?chē)?yán)重),進(jìn)而產(chǎn)生了大量接口嚴(yán)重超時(shí),很明顯,有什么拖慢了接口。交易曲線(xiàn)斷崖式的下降,我立馬就被NOC 進(jìn)行了 on call ,迅速將消息發(fā)送的開(kāi)關(guān)關(guān)閉,恢復(fù)也是一瞬間的事情,然后,人肉跑到架構(gòu)團(tuán)隊(duì)前邊跪求協(xié)助排查原因(終歸還是當(dāng)時(shí)的自己太菜)。
 
當(dāng)晚,我們開(kāi)、關(guān)、開(kāi)、關(guān)、開(kāi)、關(guān)...流量從 5% 、10% 、30% 等等,不同嘗試、驗(yàn)證之后,最后得出的結(jié)論,是和當(dāng)時(shí)的 HAProxy 配置有關(guān),由于 HAProxy 提前關(guān)閉了和 RabbitMQ 集群的連接,服務(wù)的 Client 仍然拿著壞死的連接去請(qǐng)求,進(jìn)而造成了這次問(wèn)題,并且, Client 確實(shí)沒(méi)對(duì)這種超時(shí)進(jìn)行容錯(cuò)。在調(diào)整了 HAProxy 的鏈接超時(shí)配置之后,癥狀就消除了。雖然,從日志上看遺留有一些隱患。
 
此時(shí),是長(zhǎng)這樣的,每個(gè)接入的業(yè)務(wù)方需要申請(qǐng)一個(gè) Topic , Topic 之下掛多少 Queue 可以根據(jù)業(yè)務(wù)需求自己確定。
 
千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!
 
 這個(gè)物理架構(gòu)部署穩(wěn)定運(yùn)行了不到1年時(shí)間就存在不少問(wèn)題,下章會(huì)再展開(kāi)。
 
在使用上,當(dāng)時(shí)定下了這么幾條原則:
1、訂單不對(duì)外直接暴露自身狀態(tài),而是以事件的方式對(duì)外暴露。因?yàn)闋顟B(tài)是一個(gè)描述,而事件則代表了一個(gè)動(dòng)作,同時(shí)可以將訂單狀態(tài)細(xì)節(jié)和接入方解耦。
2、消息廣播僅用于廣播事件,而不用于數(shù)據(jù)同步,如消費(fèi)者需要更多的數(shù)據(jù)則反查訂單數(shù)據(jù)接口,時(shí)間戳包含事件產(chǎn)生時(shí)間和發(fā)送時(shí)間(時(shí)間是后來(lái)加上的)。即消息體包括 header 信息,僅放入用于解釋這個(gè)事件的內(nèi)容,還包括交易雙方主鍵和一些能夠用于做通用過(guò)濾或二次路由的信息。
3、費(fèi)者在消費(fèi)消息時(shí)應(yīng)當(dāng)保證自身的冪等性,同時(shí)應(yīng)當(dāng)讓自己在消費(fèi)時(shí)無(wú)狀態(tài)。如果一定要順序消費(fèi),那么自行通過(guò)Redis等方案實(shí)現(xiàn)。
4、消費(fèi)者接入時(shí), Topic 和 Queue 需要按照一定命名規(guī)范,同時(shí), Queue 的最大積壓深度為 10k ,超過(guò)則舍棄。消費(fèi)者要明確自身是否接受消息可損,同時(shí)要保證自身的消費(fèi)性能。按照當(dāng)時(shí)評(píng)估,消息堆積到達(dá)百萬(wàn)時(shí)會(huì)使得整個(gè)集群性能下降 10% 。(在全局架構(gòu)的建議下,我們還提供了以 Redis 為介質(zhì),作為鏡像存儲(chǔ)了訂單事件,不過(guò)體驗(yàn)并不夠優(yōu)雅)
 
而這套消息廣播的邏輯架構(gòu),一直持續(xù)使用到今天,在解耦上產(chǎn)生了巨大的紅利。
 

初探


15 年中旬到 16 年初,我們處在每天的單量在百萬(wàn)以上并逐步快速增長(zhǎng)這么一個(gè)階段。


OSC
 
在那個(gè)時(shí)期,也看了很多架構(gòu)文章,ESB、SOA、微服務(wù)、CQRS、EventSource 等等,我們也在積極探討訂單系統(tǒng)如何重構(gòu),以支撐更高的并發(fā)。當(dāng)時(shí)聽(tīng)的最多的,是京東的 OFC ,還特地買(mǎi)了《京東技術(shù)解密》在研讀,不過(guò)很快得出結(jié)論,幾乎無(wú)太大參考價(jià)值。主要原因是京東的 OFC ,很明顯是由零售業(yè)務(wù)的特性決定的,很多 OFC 里的概念,作為入行尚淺的我們,套到餐飲 O2O ,幾乎難以理解。但我們還是深受其影響,給小組取了一個(gè)相似的縮寫(xiě),OSC,Order Service Center 。
 
由于手頭上這套訂單已經(jīng)服役了 3 年多,公司的主要語(yǔ)言棧從人數(shù)上也由 Python 傾向到 Java ,沒(méi)多久,我們打算重寫(xiě)這套訂單體系。于是,我設(shè)計(jì)了一套架構(gòu)體系,以 osc 為應(yīng)用的域前綴。這套體系的核心理念: 訂單是為了保持交易時(shí)刻的快照,盡可能的保持自己的簡(jiǎn)潔,減少對(duì)各方的依賴(lài),減輕作為數(shù)據(jù)通道的作用。
 
我們選取的語(yǔ)言棧選型是 Java ,也就是計(jì)劃開(kāi)始轉(zhuǎn)型 Java 。(很不巧,我們真正轉(zhuǎn)型到 Java 最后發(fā)生在 2019 年).

此時(shí),正值 9 月。很巧的是,公司開(kāi)始第一次開(kāi)始設(shè)立新服務(wù)的架構(gòu)評(píng)審制度,我這個(gè)方案,大概就是參與評(píng)審的 Top1、2 小白鼠,新鮮的大錘正等著敲人。
 
其實(shí),在那之后的1年回過(guò)頭來(lái)看,還挺感謝這次架構(gòu)評(píng)審,不是因?yàn)橥ㄟ^(guò)了,而是因?yàn)楸痪芙^了。
 
說(shuō)來(lái)也好笑,那一次,依稀記得參與架構(gòu)評(píng)審的評(píng)委成員, DA 負(fù)責(zé)人、基礎(chǔ) OPS 負(fù)責(zé)人、入職沒(méi)多久的一個(gè)架構(gòu)師。
       
架構(gòu)師當(dāng)時(shí)的提問(wèn)關(guān)注點(diǎn)在這套架構(gòu)是能夠用1年還是3年,而基礎(chǔ)OPS負(fù)責(zé)人的提問(wèn),特別有意思,他問(wèn)了第一個(gè)問(wèn)題,這套系統(tǒng)是關(guān)鍵路徑嗎?我心想,這不是廢話(huà)嗎,我直接回答,最中間那部分是的。

然后第二個(gè)問(wèn)題,出了問(wèn)題,這個(gè)應(yīng)用可以降級(jí)嗎?我一想,這不也是廢話(huà)嗎,這個(gè)鏈路當(dāng)然沒(méi)法降級(jí),這是最核心最基礎(chǔ)的鏈路,公司的核心業(yè)務(wù)就是圍繞交易。(可能是雙方的理解不在一個(gè)頻道上)。

于是,他給的結(jié)論是,關(guān)鍵路徑,又是核心的訂單,沒(méi)法降級(jí),一旦出了問(wèn)題,大家都沒(méi)飯吃。于是評(píng)審結(jié)束,結(jié)論是不通過(guò)。
 

組建測(cè)試團(tuán)隊(duì)

 
交易團(tuán)隊(duì)一直沒(méi)有專(zhuān)職的測(cè)試,也就是說(shuō),所有的內(nèi)容,都是由研發(fā)自測(cè)來(lái)保證的。而公司當(dāng)時(shí)的自動(dòng)化測(cè)試非常的弱,幾乎所有的測(cè)試都是依靠手工進(jìn)行。但是,我此時(shí)覺(jué)得非常有必要拿到測(cè)試資源。我強(qiáng)烈的要求成立一個(gè)測(cè)試小組來(lái)給訂單上線(xiàn)質(zhì)量加上一層防護(hù)。
 
當(dāng)時(shí)還發(fā)生了一些有趣的事情,據(jù) JN 去了解,框架團(tuán)隊(duì)是沒(méi)有測(cè)試的,然而他們似乎沒(méi)出什么問(wèn)題,當(dāng)時(shí)他們很自豪的解釋?zhuān)夹g(shù)憑什么不應(yīng)該自己保障代碼的質(zhì)量。簡(jiǎn)直理直氣壯,無(wú)懈可擊。我覺(jué)得這個(gè)觀點(diǎn)有一些理想,研發(fā)自己可能沒(méi)那么容易發(fā)現(xiàn)自己的錯(cuò)誤,引入另外一批人從另外一個(gè)角度切入,能夠進(jìn)一步提升質(zhì)量的保障,畢竟這個(gè)系統(tǒng)是如此的重要和高風(fēng)險(xiǎn),但是我們也并不應(yīng)該建立一個(gè)只能提供“點(diǎn)點(diǎn)點(diǎn)”的測(cè)試團(tuán)隊(duì)。
 
最后,在和 JN 長(zhǎng)時(shí)間的溝通后,我們確定了當(dāng)時(shí)測(cè)試小組的定位和職責(zé): 保證代碼質(zhì)量是研發(fā)自己應(yīng)盡的責(zé)任,測(cè)試開(kāi)發(fā)在此基礎(chǔ)上,主要提供工具支持,讓測(cè)試成本降低,同時(shí)在精力允許的情況,提供一定程度的測(cè)試保障。
 
于是,在 2016 年 2、3 月左右,交易團(tuán)隊(duì)來(lái)了第一位測(cè)試,差不多在 4 月的時(shí)候,測(cè)試 HC 達(dá)到了 4 人,整個(gè)測(cè)試小組由我來(lái)負(fù)責(zé)。
 

第一件事情,搭建自動(dòng)化集成測(cè)試。


技術(shù)棧上的選擇,采用了 RobotFramework ,主要原因是整個(gè)團(tuán)隊(duì)當(dāng)時(shí)仍然以 Python 為主要語(yǔ)言,測(cè)試開(kāi)發(fā)同學(xué)實(shí)際上 Python 和 Java 也都能寫(xiě);另外一點(diǎn)是  RobotFramwork 的關(guān)鍵字驅(qū)動(dòng),有一套自己的規(guī)范,和系統(tǒng)相關(guān)的lib可以被提煉出來(lái),即使做語(yǔ)言棧轉(zhuǎn)型時(shí),成本也不會(huì)很高。
 
除了測(cè)試的流程規(guī)范和標(biāo)準(zhǔn)外,開(kāi)始想搭建一個(gè)平臺(tái),用于管理測(cè)試用例、執(zhí)行情況和執(zhí)行報(bào)告。
 
這套體系我命名為 WeBot :
  • 采用 RobotFramwork 來(lái)作為測(cè)試用例執(zhí)行的基礎(chǔ)
  • Jenkins 來(lái)實(shí)際調(diào)配在何處執(zhí)行,并且滿(mǎn)足執(zhí)行計(jì)劃的管理
  • 基于 Django 搭建了一個(gè)簡(jiǎn)單的管理界面,用來(lái)管理用例和測(cè)試報(bào)告,并使得每一個(gè)測(cè)試用例可以被作為一個(gè)單元隨意組裝,如果對(duì) Java 很熟悉的同學(xué),這里做一個(gè)近似的類(lèi)比,這里每一個(gè)用例都可以當(dāng)成一個(gè) SPI 。
  • 另外引入了 Docker 來(lái)部署 slave 的環(huán)境,用的很淺,雖然當(dāng)時(shí)餓了么在生產(chǎn)還沒(méi)使用 Docker (餓了么生產(chǎn)上的容器化應(yīng)該在 17 年左右)。
 
想想自己當(dāng)時(shí)在測(cè)試環(huán)境玩的還是蠻歡樂(lè)的,很喜歡折騰。
 
大致的思路如:
 
千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!

測(cè)試單元: Bussiness Library 其實(shí)是對(duì) SOA 服務(wù)接口到 RobotFramwork 中的一層封裝,每一個(gè)測(cè)試單元可以調(diào)用一個(gè)或多個(gè)接口完成一次原子的業(yè)務(wù)活動(dòng)。

校驗(yàn)組件: 提供了對(duì)返回值,或者額外配置對(duì)Redis、數(shù)據(jù)庫(kù)數(shù)據(jù)的校驗(yàn)。

集成測(cè)試: 多個(gè)測(cè)試單元串行編排起來(lái)就完成了一個(gè)集成測(cè)試用例。其中每個(gè)測(cè)試單元執(zhí)行后,請(qǐng)求的入?yún)⒑统霾?,在集成測(cè)試用例的運(yùn)行域內(nèi)任何地方都是可以獲取到的。

回歸測(cè)試: 選取多個(gè)集成測(cè)試,可以當(dāng)成一個(gè)方案,配置執(zhí)行。
 
這樣就實(shí)現(xiàn)了多層級(jí)不同粒度的復(fù)用。根據(jù)集成測(cè)試和回歸測(cè)試的方案搭配,后臺(tái)會(huì)編譯生成對(duì)應(yīng)的  Robot 文件。
 
這個(gè)項(xiàng)目,最后其實(shí)失敗了。最主要的原因,測(cè)試開(kāi)發(fā)的同學(xué)在開(kāi)發(fā)上能力還不足,而界面上需要比較多的前端開(kāi)發(fā)工作,一開(kāi)始我直接套用了 Django 的擴(kuò)展管理界面 xadmin ,進(jìn)行了簡(jiǎn)單的擴(kuò)展,然而當(dāng)時(shí)的精力,不允許自己花太多精力在上邊,內(nèi)置的前端組件在體驗(yàn)上有一些硬傷,反而導(dǎo)致效率不高。直到 5 月份,基本放棄了二次開(kāi)發(fā)。
 
但這次嘗試也帶來(lái)了另外的一些成果。我們相當(dāng)于舍棄了使用系統(tǒng)管理用例,而 Jenkins + RobotFramwork 的組合被保留了下來(lái)。我們把寫(xiě)好的一些集成測(cè)試用例托管在 Git 上,研發(fā)會(huì)把自己開(kāi)發(fā)好的分支部署在指定環(huán)境,每天凌晨拉取執(zhí)行,研發(fā)會(huì)在早上根據(jù)自動(dòng)化測(cè)試報(bào)告來(lái)看最近一次要發(fā)布的內(nèi)容是否有問(wèn)題。同時(shí),也允許研發(fā)手動(dòng)執(zhí)行,文武和曉東兩位同學(xué)在這塊貢獻(xiàn)了非常多的精力。
 
這個(gè)自動(dòng)化集成回歸的建立,為后續(xù)幾次訂單系統(tǒng)的拆分和小范圍重構(gòu)提供了重要的保障。讓研發(fā)膽子更大,步子能夠邁得更長(zhǎng)了。研發(fā)自己會(huì)非常積極的使用這套工具,嘗到了很多顯而易見(jiàn)的甜頭。
 

第二件事情,搭建性能測(cè)試。


背景:
記得在 15 年剛剛接觸訂單的時(shí)候,有幸拜訪(fǎng)了還沒(méi)來(lái)餓了么,但后來(lái)成為餓了么全局架構(gòu)負(fù)責(zé)人的 XL 老師,談及如何做好訂單系統(tǒng),重點(diǎn)提及的一點(diǎn),也是壓測(cè)。

當(dāng)時(shí)有一些問(wèn)題和性能、容量有一些關(guān)系,我們沒(méi)有什么提前預(yù)知的能力。比如,在我們完成 sharding 前有一次商戶(hù)端上線(xiàn)了一次訂單列表改版,因?yàn)槭褂昧爽F(xiàn)有的一個(gè)通用接口(這個(gè)接口粒度很粗,條件組合自由度很強(qiáng)),我們都沒(méi)能預(yù)先評(píng)估,這個(gè)查詢(xún)走了一個(gè)性能極差的索引。當(dāng)時(shí)午高峰接近,一個(gè)幾 k QPS 的查詢(xún)接口,從庫(kù)突然( 15 年我們的監(jiān)控告警體系還沒(méi)有那么完備)就被打垮了,從庫(kù)切一個(gè)掛一個(gè),不得不采取接口無(wú)差別限流 50% 才緩過(guò)來(lái),整個(gè)持續(xù)了接近半個(gè)小時(shí)。最后追溯到近期變更,商戶(hù)端回滾了這次變更才真的恢復(fù)。而事后排查,造成此次事故的慢 SQL, QPS 大概幾百左右。
 
整個(gè)公司的性能測(cè)試組建,早于我這邊的規(guī)劃,但是當(dāng)時(shí)公司的性能測(cè)試是為了 517 外賣(mài)節(jié)服務(wù)的,有一波專(zhuān)門(mén)的測(cè)試同學(xué),這是餓了么第一次造節(jié),這件事的籌備和實(shí)施其實(shí)花了很長(zhǎng)時(shí)間。

在壓測(cè)的時(shí)候需要不斷的解決問(wèn)題,重復(fù)再壓測(cè),這件事使得當(dāng)時(shí)很多同學(xué)見(jiàn)到了近鐵城市廣場(chǎng)每一個(gè)小時(shí)的樣子,回憶那段時(shí)光,我記得最晚的一次,大概是 5 月 6 號(hào),我們到樓下已經(jīng)是凌晨 5 點(diǎn)半,我到家的時(shí)候兩旁的路燈剛剛關(guān)。
 
上邊是一點(diǎn)題外話(huà),雖然全鏈路壓測(cè)一定會(huì)帶上我們,但是我們也有一些全鏈路壓不到的地方,還有一些接口或邏輯需要單獨(dú)進(jìn)行,需要隨時(shí)進(jìn)行。
 
搭建:
技術(shù)選型上選擇了 Locust ,因?yàn)?Python 的 SOA 框架及其組件,可以帶來(lái)極大的便利。此前在做公司級(jí)的全鏈路壓測(cè)時(shí),是基于 JMeter 的, JMeter 并不是很容易和 Java 的 SOA 框架進(jìn)行集成,需要有一個(gè)前端 HaProxy 來(lái)做流量的分流,不能直接使用軟負(fù)載,這在當(dāng)時(shí)造成了一定的不便性。另外一個(gè)原因, Locust 的設(shè)計(jì)理念,可以使一些性能測(cè)試的用例更為貼近業(yè)務(wù)實(shí)際場(chǎng)景,只觀測(cè) QPS 指標(biāo),有時(shí)候會(huì)有一些失真。
 
有了全鏈路性能測(cè)試團(tuán)隊(duì)在前邊趟坑,其實(shí)我自己性能測(cè)試能力的搭建很快就完成了,整個(gè)搭建過(guò)程花費(fèi)了 1 個(gè)多月, 8、9 月基本可以對(duì)域內(nèi)服務(wù)自行組織性能測(cè)試。性能測(cè)試人員包括研發(fā)的學(xué)習(xí),需要一點(diǎn)過(guò)程。很快,我們這個(gè)小組的性能測(cè)試就鋪開(kāi)到整個(gè)部門(mén)內(nèi)使用,包括之后和金融團(tuán)隊(duì)合并之后。
 
這次搭建使得我們?cè)趯?duì)外提供接口時(shí),對(duì)自己服務(wù)負(fù)載和性能上限有一定的預(yù)期,規(guī)避了一些有性能隱患的接口上線(xiàn),特別是面向商戶(hù)端復(fù)雜查詢(xún)條件;也能夠模擬高并發(fā)場(chǎng)景,在我們一些重構(gòu)的階段,提前發(fā)現(xiàn)了一些并發(fā)鎖和調(diào)用鏈路依賴(lài)問(wèn)題。
 

第三件事情,隨機(jī)故障演練。


1.0版本:
一開(kāi)始的雛形其實(shí)很簡(jiǎn)單,大致的思路是:
 
1、 在測(cè)試環(huán)境單拉出一個(gè)專(zhuān)門(mén)的環(huán)境,有單獨(dú)的監(jiān)控和 DB 。
2、構(gòu)造一個(gè) Client ,模擬用戶(hù)行為造數(shù)。(我們自動(dòng)化集成測(cè)試積累的經(jīng)驗(yàn)就排上用場(chǎng)了。
3、提供了一個(gè)工具來(lái)構(gòu)建被依賴(lài)服務(wù)的 Mock Server ,解決長(zhǎng)鏈路服務(wù)依賴(lài)問(wèn)題。Mock Server 可以根據(jù)輸入返回一些設(shè)定好的輸出。
4、另外,框架團(tuán)隊(duì)幫忙做了一些手腳,發(fā)了一個(gè)特殊版本,使得我們可以對(duì)流量打標(biāo)??梢愿鶕?jù) Client 對(duì)流量的標(biāo)記,來(lái)讓 Mock Server 模擬阻塞、超時(shí)等一些異常行為,反饋到我們的被測(cè) server 上。
 
這是一個(gè)很簡(jiǎn)單的雛形,而訂單經(jīng)過(guò)我們的幾次治理,對(duì)外依賴(lài)已經(jīng)很少,所以不到 2、3 天就完全成型。但僅僅是玩具而已,并不具備足夠的參考意義。因?yàn)椴l(fā)沒(méi)有做的很高, Mock Server 能夠做的事情也有限。
 
2.0版本:
JN 召集了一些同學(xué),參照 Netflix 的 Choas Monkey 為原型,造了一個(gè)輪子,我們稱(chēng)之為 Kennel 。
 
控制中心設(shè)計(jì)圖如下:
千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!
 
在專(zhuān)項(xiàng)同學(xué)和運(yùn)維同學(xué)的幫助下,Kennel 在 2016 年的 10 月左右初步可用。這個(gè)工具提供了諸如: 模擬網(wǎng)絡(luò)丟包;接口異常注入;摘除集群中的某節(jié)點(diǎn);暴力干掉服務(wù)進(jìn)程等等。
 
這東西大家之前都沒(méi)嘗試過(guò),我們也不知道能夠測(cè)出什么來(lái),我在11月的時(shí)候想做第一波嘗試,我嘗試制定了 5 個(gè)需要驗(yàn)收的場(chǎng)景:
1、超長(zhǎng)分布式事務(wù)
2、某個(gè)接口異常引起整個(gè)服務(wù)雪崩
3、集群中某個(gè)節(jié)點(diǎn)重啟或者機(jī)器重啟,調(diào)用方反應(yīng)明顯
4、集群某個(gè)節(jié)點(diǎn)CPU負(fù)載變高,負(fù)載不均
5、服務(wù)是單點(diǎn)的,集群行為不一致
 
根據(jù)這幾個(gè)場(chǎng)景,在測(cè)試同學(xué)中挑選一個(gè)人牽頭實(shí)施。不同服務(wù)的測(cè)試報(bào)告略有差異,其中一份的部分截圖如下:

千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!
千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!
千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!
 
通過(guò)對(duì)交易主要的幾個(gè)服務(wù)測(cè)試一輪之后,我們確實(shí)發(fā)現(xiàn)了一些隱患:
 
  • 一些情況下部署的集群和服務(wù)注冊(cè)中心機(jī)器數(shù)量可能不一致,即服務(wù)節(jié)點(diǎn)被暴力干掉后,服務(wù)注冊(cè)中心不能主動(dòng)發(fā)現(xiàn)和踢出。這是一個(gè)比較大的隱患。
  • 每個(gè)集群都存在負(fù)載不均的現(xiàn)象,個(gè)別機(jī)器可能 CPU 利用率會(huì)偏高。(和負(fù)載均衡策略有關(guān))
  • 進(jìn)行“毀滅打擊”自恢復(fù)時(shí),某幾個(gè)節(jié)點(diǎn)的 CPU 利用率會(huì)顯著高于其他節(jié)點(diǎn),幾個(gè)小時(shí)之后才會(huì)逐漸均勻。(和負(fù)載均衡策略有關(guān))
  • 單節(jié)點(diǎn) CPU 負(fù)載較高時(shí),負(fù)載均衡不會(huì)將流量路由到其它節(jié)點(diǎn),即使這部分請(qǐng)求性能遠(yuǎn)差于其它節(jié)點(diǎn),甚至出現(xiàn)很多超時(shí)。(和負(fù)載均衡、熔斷的實(shí)現(xiàn)機(jī)制有關(guān),Python 的 SOA 是在服務(wù)端做的熔斷,而客戶(hù)端沒(méi)有)
  • 大量服務(wù)的超時(shí)設(shè)置配置有誤,框架支持配置軟超時(shí)和硬超時(shí),軟超時(shí)只告警不阻斷,然而默認(rèn)的硬超時(shí)長(zhǎng)達(dá) 20s 之久,很多服務(wù)只配置了軟超時(shí)甚至沒(méi)有配置,這其實(shí)是一個(gè)低級(jí)錯(cuò)誤埋下的嚴(yán)重隱患,可能會(huì)沒(méi)法避免一些雪崩。
  • 個(gè)別場(chǎng)景下超時(shí)配置失效,通過(guò)對(duì)調(diào)用鏈路的埋點(diǎn),以及和框架團(tuán)隊(duì)復(fù)現(xiàn),最后鎖定是一些使用消息隊(duì)列發(fā)送消息的場(chǎng)景,Python 框架是利用了Gevent 來(lái)實(shí)現(xiàn)高并發(fā)的支持,框架沒(méi)能抓住這個(gè)超時(shí)。
  • ...
 
這個(gè)項(xiàng)目,幾個(gè)道理顯而易見(jiàn),我們做了很多設(shè)計(jì)和防范,都必須結(jié)合故障演練來(lái)進(jìn)行驗(yàn)收,無(wú)論是低級(jí)錯(cuò)誤還是設(shè)計(jì)不足,能夠一定程度提前發(fā)現(xiàn)。

當(dāng)然我們也造成了一些失誤,一條信心滿(mǎn)滿(mǎn)的補(bǔ)償鏈路(平時(shí)不work),自己攻擊的時(shí)候,它失效了,后來(lái)發(fā)現(xiàn)是某次變更埋下的隱患。自己親手造的鍋,含著淚也要往身上背,但我反而更覺(jué)得故障演練是更值得去做的,誰(shuí)能保證真正的故障來(lái)臨時(shí),不是一個(gè)更嚴(yán)重的事故。
 
除了系統(tǒng)利好外,人員也拿到了很多收益,比如測(cè)試和研發(fā)同學(xué)經(jīng)過(guò)這個(gè)項(xiàng)目的實(shí)時(shí),對(duì)我們的 trace 和 log 系統(tǒng)在使用上爐火純青,對(duì)我們 SOA 框架的運(yùn)作了解也更為透徹,這里的很多隱患和根因,就是測(cè)試同學(xué)刨根挖底找到的。 高水準(zhǔn)的 QA 同學(xué)很重要,提升 QA 同學(xué)的水平也同樣重要。
 
當(dāng)然,除了測(cè)試團(tuán)隊(duì)的工作外,單元測(cè)試我們也沒(méi)有落下,在 16 年長(zhǎng)時(shí)間保持 80%~90% 的一個(gè)代碼行覆蓋率。

伴隨體量上漲的一系列問(wèn)題

 

Redis使用的改進(jìn)

 
使用姿勢(shì)的治理:
 
2016 年年初主要瓶頸在數(shù)據(jù)庫(kù),在上文其實(shí)已經(jīng)提到了分庫(kù)分表的事,可以稍微喘口氣,到了 6 月,大家最擔(dān)憂(yōu)的,變成了 Redis 。當(dāng)時(shí) Zabbix 只能監(jiān)控到機(jī)器的運(yùn)行情況, Zabbix 其實(shí)也在逐步下線(xiàn)中, SRE 團(tuán)隊(duì)搭建了一套時(shí)效更高的機(jī)器指標(biāo)收集體系,直接讀取了 Linux 的一些數(shù)據(jù),然而,整個(gè) Redis 運(yùn)行情況仍然完全是黑盒。
 
餓了么在 twemproxy 和 codis 上也踩了不少坑, redis-cluster 在業(yè)界還沒(méi)被大規(guī)模使用,于是自研了一套 Redis proxy: corvus ,還提供了強(qiáng)大指標(biāo)上報(bào),可以監(jiān)控到 redis 的內(nèi)存、鏈接、 hit 率、key 數(shù)量、傳輸數(shù)據(jù)量等等。正好在這個(gè)時(shí)間點(diǎn)推出,用以取代 twemproxy ,這使得 Redis 的治理迎來(lái)轉(zhuǎn)機(jī)。
 
我們配合進(jìn)行了這次遷移,還真是不遷不知道,一遷嚇一跳。
 
當(dāng)時(shí)我們使用 Reids 主要有三個(gè)用途,一是緩存,類(lèi)似表和接口緯度;二是分布式鎖,部分場(chǎng)景用來(lái)防并發(fā)寫(xiě);三是餐廳流水號(hào)的生成。代碼已經(jīng)是好幾年前的前人寫(xiě)的。
 
老的使用姿勢(shì),把表級(jí)緩存和接口緩存,配置在一個(gè)集群中;其余配置在另外一個(gè)集群,但是在使用上,框架包裝了兩種 Client ,有不同的容錯(cuò)機(jī)制(即是否強(qiáng)依賴(lài)或可擊穿)。
 
大家都知道外賣(mài)交易有個(gè)特點(diǎn),一筆訂單在短時(shí)間內(nèi),交易階段的推進(jìn)會(huì)更快,因此訂單緩存的更新更頻繁,我們?cè)诙虝夯叶闰?yàn)證 Redis 集群的可用性之后,就進(jìn)行了全面切換(當(dāng)時(shí)的具體切換方案細(xì)節(jié)記不太清了,現(xiàn)在回想起來(lái)其實(shí)可以有更穩(wěn)妥的方案)。
 
參照原緩存的集群是 55G , OPS 準(zhǔn)備了一個(gè) 100G 的集群。在切換后 10min 左右,集群內(nèi)存就占滿(mǎn)了。
 
千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!
 
我們得出一個(gè)驚人的結(jié)論...舊集群的 55G ,之前就一直是超的(巧了,配合我們遷移的OPS也叫超哥)。
 
從監(jiān)控指標(biāo)上看,keys 增長(zhǎng)很快而ttl下降也很快,我們很快鎖定了兩個(gè)接口, query_order 和 count_order ,當(dāng)時(shí)這兩個(gè)接口高峰期前者大概是 7k QPS ,后者是10k QPS ,這兩個(gè)接口之前的rt上看一點(diǎn)問(wèn)題也沒(méi)有,平均也就 10ms 。
 
還得從我們的業(yè)務(wù)場(chǎng)景說(shuō)起,這兩個(gè)接口的主要作用是查詢(xún)一段時(shí)間內(nèi)某家餐廳的訂單,為了保證商家能夠盡快的看到新訂單,商戶(hù)端是采取了輪詢(xún)刷新的機(jī)制,而這個(gè)問(wèn)題主要出在查詢(xún)參數(shù)上。這兩個(gè)接口使用了接口級(jí)緩存,所謂的接口級(jí)緩存,就是把入?yún)⑸蓚€(gè) Hash 作為 key ,把返回值作為 value , cache 起來(lái), ttl 為秒級(jí),咋一看沒(méi)什么問(wèn)題。如果查詢(xún)參數(shù)的時(shí)間戳,截止時(shí)間是當(dāng)天最后一秒的話(huà),確實(shí)是的??吹竭@我相信很多人已經(jīng)猜到,截止時(shí)間戳傳入的其實(shí)是當(dāng)前時(shí)刻,這是一個(gè)滑動(dòng)的時(shí)間,也就引發(fā)了 cache 接近 100% miss 的同時(shí),高頻的塞入了新的數(shù)據(jù)。
 
 (因?yàn)樾屡f集群的內(nèi)存回收策略不一樣,新集群在這種情況下,頻繁 GC 會(huì)引發(fā)性能指標(biāo)抖動(dòng)劇烈)
 
這兩個(gè) cache ,其實(shí)沒(méi)任何用處...回滾過(guò)了一天后,經(jīng)過(guò)灰度,全面去掉了這兩個(gè)接口的 cache ,我們又進(jìn)行了一次切換,順帶將接口級(jí)緩存和表級(jí)緩存拆分到兩個(gè)集群。

接著,我們又發(fā)現(xiàn)了一些有趣的事情...
 
先來(lái)看看,我們業(yè)務(wù)單量峰值的大致曲線(xiàn),對(duì)外賣(mài)行業(yè)來(lái)說(shuō),一天有兩個(gè)峰值,中午和傍晚,中午要顯著高于傍晚。
 
千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!
 
       切換后那天的下午大概 3 點(diǎn)多,內(nèi)存再次爆了... ,內(nèi)存占用曲線(xiàn)近似下圖:
千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!
緊急擴(kuò)容后,我們一直觀察到了晚上,最后的曲線(xiàn)變成了下圖,從 hit 率上看,也有一定提升(具體數(shù)據(jù)已不可考,在 88%~95% 之間,后來(lái)達(dá)到 98% 以上)。
 
千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!
 
為什么和業(yè)務(wù)峰值不太一樣...
 
其實(shí)還是要結(jié)合業(yè)務(wù)來(lái)說(shuō),很簡(jiǎn)單,商戶(hù)端當(dāng)時(shí)的輪詢(xún)有多個(gè)場(chǎng)景,最長(zhǎng)是查詢(xún)最近 3 天內(nèi)的訂單,還有一個(gè)頁(yè)面單獨(dú)查詢(xún)當(dāng)天訂單。
 
后端在輪詢(xún)時(shí)查了比前端每頁(yè)需要的更多條目,并且,并不是每個(gè)商戶(hù)當(dāng)天訂單一開(kāi)始就是大于一頁(yè)的,因此,隨著當(dāng)天時(shí)間的推移,出現(xiàn)了上邊的現(xiàn)象。
 
為什么以前的性能指標(biāo)又沒(méi)看出什么問(wèn)題呢?一是和舊 Redis 集群的內(nèi)存回收策略選取有關(guān),二是 QPS 的量很高,如果只看平均響應(yīng)時(shí)間,差的指標(biāo)被平均了, hit 率也被平均拉高了。
 
嗯,解決了這個(gè)問(wèn)題之后,又又發(fā)現(xiàn)了新的問(wèn)題...
 
大概1、2點(diǎn)這個(gè)夜深人靜的時(shí)候,被 oncall 叫起來(lái),監(jiān)控發(fā)現(xiàn)內(nèi)存使用急劇飆升。
 
我們鎖定到一個(gè)調(diào)用量不太正常的接口上,又是 query_order。前段日子,清結(jié)算剛剛改造,就是在這種夜深人靜的時(shí)候跑賬,當(dāng)時(shí)我們的賬期比較長(zhǎng)(這個(gè)是由于訂單可退天數(shù)的問(wèn)題,下文還有地方會(huì)展開(kāi)),這時(shí)候會(huì)拉取大量歷史訂單,導(dǎo)致占用了大量?jī)?nèi)存,而我們的表級(jí)緩存時(shí)效是 12h ,如果不做清理,對(duì)早高峰可能會(huì)產(chǎn)生一定的影響。后來(lái)我們次日就提供了一個(gè)不走緩存的接口,單獨(dú)給到清結(jié)算。
 
這里核心的問(wèn)題在于, 我們服務(wù)化也就不到 1 年的時(shí)間,服務(wù)的治理還不能做到很精細(xì),服務(wù)開(kāi)放出去的接口,暴露在內(nèi)網(wǎng)中,誰(shuí)都可以來(lái)調(diào)用,我們的接口協(xié)議也是公開(kāi)的,任何人都很容易知道查閱到接口,并且,在公司的老人路子都比較野(不需要對(duì)接,有啥要啥,沒(méi)有就自己加)。Git 倉(cāng)庫(kù)代碼合并權(quán)限和發(fā)布權(quán)限早在 15 年底就回收管控了,但那一刻 SOA 化還未完全,接口授權(quán)直到很后邊才支持。
 
Redis 的使用還是需要建立在深刻理解業(yè)務(wù)場(chǎng)景基礎(chǔ)上,并且關(guān)注各類(lèi)指標(biāo)。
 
緩存機(jī)制的改進(jìn) 
我們當(dāng)時(shí)的緩存機(jī)制是這樣的:
 
千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!
這個(gè)架構(gòu)設(shè)計(jì)的優(yōu)點(diǎn):
1、有一條獨(dú)立的鏈路來(lái)做緩存的更新,對(duì)原有服務(wù)入侵性較小
2、組件可復(fù)用性較高
3、有 MQ 削峰,同時(shí)還有一級(jí) Redis,做了聚合,進(jìn)一步減小并發(fā)
 
在很多場(chǎng)景,是一套蠻優(yōu)秀的架構(gòu)。
 
缺點(diǎn): 
1、用到了兩級(jí)隊(duì)列,鏈路較長(zhǎng)
2、實(shí)時(shí)性較差
 
驅(qū)動(dòng)我們改造的原因,也源自一次小事故。

商戶(hù)訂單列表的查詢(xún)其實(shí)根據(jù)的是訂單狀態(tài)來(lái)查,獲取到的訂單應(yīng)當(dāng)是支付好了的。然而有一部分錯(cuò)誤的判斷邏輯,放在了當(dāng)時(shí)商戶(hù)端接單后端,這個(gè)邏輯會(huì)判斷訂單上的流水號(hào)是否是0(默認(rèn)值),如果是0推斷出訂單還未支付,就將訂單過(guò)濾掉。
 
在那次事故中,緩存更新組件跪了(并且沒(méi)有人知道...雖然這個(gè)架構(gòu)是框架的某些同學(xué)早期設(shè)計(jì)的,但太穩(wěn)定了以至于都被遺忘...)。由于緩存更新的不夠及時(shí),拿到了過(guò)時(shí)的數(shù)據(jù),表象就是,商戶(hù)看不到部分新訂單,看到的時(shí)候,已經(jīng)被超時(shí)未接單自動(dòng)取消的邏輯取消了,真是精彩的組合...
 
后邊改造成下邊的樣子:
 
千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!
 
相比起來(lái),這個(gè)架構(gòu)鏈路就減少了很多,而且實(shí)時(shí)性得到了保障。但是為了不阻塞流程,進(jìn)行了一定的容錯(cuò),這就必須增加一條監(jiān)控補(bǔ)償鏈路。這次改進(jìn)之后,我們立馬去除了對(duì) ZeroMQ 在代碼和配置上的依賴(lài)。
 

消息使用的改進(jìn)

 
分庫(kù)分表做完后,我們對(duì) MQ 沒(méi)有什么信心,在接下來(lái)的幾個(gè)月,MQ 接連出了幾次異常...真的是墨菲定律,遺憾的是我們只是感覺(jué)它要出事情而不知道它哪里會(huì)出事情。
 
錯(cuò)誤的姿勢(shì)
在之前的章節(jié),我提到過(guò)曾經(jīng)搭建了一套訂單消息廣播機(jī)制,基于這套消息為契機(jī),商戶(hù)端針對(duì)高頻輪詢(xún)做了一個(gè)技術(shù)優(yōu)化,希望通過(guò)長(zhǎng)連接,推拉結(jié)合,減小輪詢(xún)的壓力。簡(jiǎn)單介紹一下這套方案,商戶(hù)端有一個(gè)后端服務(wù),接收訂單的消息廣播,如果有新訂單(即剛剛扭轉(zhuǎn)到完成支付商家可見(jiàn)的訂單),會(huì)通過(guò)與端上的長(zhǎng)連接推送觸達(dá)到端上,接著端上會(huì)觸發(fā)一次主動(dòng)刷新,并發(fā)出觸達(dá)聲音提醒商戶(hù)。原先的輪詢(xún)則增加時(shí)間間隔,降低頻次。
 
千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!
那么問(wèn)題在哪? 有部分時(shí)候,藍(lán)色這條線(xiàn),整體花費(fèi)的時(shí)間居然比紅色這條線(xiàn)更少,也就是說(shuō),一部分比例的請(qǐng)求兜到外網(wǎng)溜一圈比內(nèi)網(wǎng)數(shù)據(jù)庫(kù)的主從同步還快。

商戶(hù)端提出要輪主庫(kù),禽獸啊,顯然,這個(gè)頻次,想是不用想的,不可能答應(yīng),畢竟之前輪詢(xún)從庫(kù)還打掛過(guò)。由消費(fèi)者在本地 hold 一段時(shí)間再消費(fèi),也不太友好。畢竟有時(shí)候,快不一定是好事情,那么我們能不能讓它慢一點(diǎn)出來(lái)?
 
于是,binding 的拓?fù)浔晃覀兏某闪诉@樣,前段粉紅的這個(gè) Queue ,使用了 RabbitMQ 死進(jìn)隊(duì)列的特性(即消息設(shè)置一個(gè)過(guò)期時(shí)間,等過(guò)期時(shí)間到了就可以從隊(duì)列中舍棄或挪到另外的地方):
千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!
眼前的問(wèn)題解決了,但也埋了坑,對(duì) RabbitMQ 和架構(gòu)設(shè)計(jì)稍有經(jīng)驗(yàn)的同學(xué),應(yīng)該很快意識(shí)到這里犯了什么錯(cuò)誤。binding 關(guān)系這類(lèi) Meta 信息每一個(gè) Broker 都會(huì)存儲(chǔ),用于路由。然而,消息的持久化卻是在 Queue 中,而 queue 只會(huì)存在一個(gè)節(jié)點(diǎn),本來(lái)是集群,在這個(gè)時(shí)候,拓?fù)渲锌壳暗囊徊糠肿兂闪藛吸c(diǎn)。
 
回到我一開(kāi)始提到的 MQ 集群事故,因?yàn)橐恍┰驙窟B,我們這個(gè) MQ 集群某些節(jié)點(diǎn)跪了,很不幸,包含這個(gè)粉紅粉紅的 Queue 。于此同時(shí),暴露了另外一個(gè)問(wèn)題,這個(gè)拓?fù)浣Y(jié)構(gòu),不能自動(dòng)化運(yùn)維,得依靠一定的人工維護(hù),重建新的節(jié)點(diǎn), meta 信息需要從舊節(jié)點(diǎn)導(dǎo)出導(dǎo)入,但是會(huì)產(chǎn)生一定的沖突。并且,早期我們的 Topic 和 Queue 的聲明沒(méi)有什么經(jīng)驗(yàn),沒(méi)有根據(jù)消費(fèi)者實(shí)際的消費(fèi)情況來(lái)分配 Queue ,使得部分節(jié)點(diǎn)過(guò)熱。權(quán)衡自動(dòng)運(yùn)維和相對(duì)的均衡之下,后邊的做法,實(shí)際是隨機(jī)選擇了一個(gè)節(jié)點(diǎn)來(lái)聲明 Queue 。

之后我們做了兩個(gè)改進(jìn),一是拓?fù)浣Y(jié)構(gòu)支持在服務(wù)的配置文件中聲明,隨服務(wù)啟動(dòng)時(shí)自動(dòng)到 MQ 中聲明;二是由商戶(hù)端后端服務(wù),接到新單消息來(lái)輪詢(xún)時(shí),對(duì)新單by單單獨(dú)請(qǐng)求一次(有 cache,如果 miss 會(huì)路由到主庫(kù))。
 
于是,消息的拓?fù)浣Y(jié)構(gòu)變成了下邊這樣:
 
千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!
消息集群拆分
仍然是上邊這個(gè)故事的上下文,我們回到影響這次事故的原因。根據(jù)我們對(duì) RabbitMQ 集群的性能測(cè)試,這個(gè)吞吐應(yīng)該能夠承受,然而 CPU 負(fù)載非常的高,還影響了生產(chǎn)者發(fā)送消息(觸發(fā)了 RabbitMQ 的自保護(hù)機(jī)制),甚至掛掉。
 
經(jīng)過(guò)架構(gòu)師的努力下,最后追溯到,這次事故的原因,在于商戶(hù)端使用的公共 SOA 框架中,消息隊(duì)列的客戶(hù)端,是部門(mén)自己獨(dú)立封裝的,這個(gè)客戶(hù)端,沒(méi)有很好理解 RabbitMQ 的一些 Client 參數(shù)(例如 get 和 fetch 模式, fetch 下的 prefetch_count參數(shù)等),其實(shí)這個(gè)參數(shù)需要一定的計(jì)算才能得到合理值,否則,即使機(jī)器還有 CPU 可用,消費(fèi)能力也上不去。

和訂單的關(guān)系又是什么?答案是 混布。這個(gè)集群通過(guò) vhost 將不同業(yè)務(wù)的消息廣播隔開(kāi),因此上邊部署了訂單、運(yùn)單、商戶(hù)端轉(zhuǎn)接的消息等。
 
在事故發(fā)生當(dāng)天,運(yùn)營(yíng)技術(shù)部老大一聲令下,無(wú)論怎么騰挪機(jī)器,當(dāng)天都必須搭建出一個(gè)獨(dú)立消息廣播集群給到訂單,運(yùn)營(yíng)技術(shù)部和我們,聯(lián)合所有的消費(fèi)方,當(dāng)天晚上,即搭建了一個(gè)7節(jié)點(diǎn)的集群,將訂單的消息廣播從中單獨(dú)拆出來(lái)。
 
(一年后,這個(gè)集群也到了瓶頸,而且無(wú)法通過(guò)擴(kuò)容解決,主要原因,一是消費(fèi)方?jīng)]有使用RabbitMQ的特性來(lái)監(jiān)聽(tīng)消息,而是本地過(guò)濾,導(dǎo)致白白耗費(fèi)一部分處理資源;二是隨著集群規(guī)模的上升,連接數(shù)達(dá)到了瓶頸。后者我們?cè)谏a(chǎn)者額外發(fā)了一份消息到新搭建的一個(gè)集群,得到了一定的緩解。真正解決,還是在餓了么在 RabbitMQ 栽了這么多跟頭,使用 Go 自研的 MaxQ 取代 RabbitMQ 之后)。
 
PS: 如果時(shí)光倒流,當(dāng)初的改進(jìn)項(xiàng)里,會(huì)提前加一個(gè)第三點(diǎn),針對(duì)使用`*`這個(gè)通配符來(lái)訂閱消息的,都要求訂閱方根據(jù)真實(shí)需要更改。這里腐化的原因,主要還是把控和治理的力度不夠,標(biāo)準(zhǔn)和最佳實(shí)踐建議在最初的說(shuō)明文檔就有,后續(xù)也提供了一些可供調(diào)整參數(shù)的計(jì)算公式,不能完全指望所有消費(fèi)者都是老實(shí)人,也不完全由技術(shù)運(yùn)營(yíng)來(lái)把控,服務(wù)提供方是需要。
 

虛擬商品交易以及創(chuàng)新

 

早餐:

2015 年下旬到 2016 年上旬,餓了么的早餐業(yè)務(wù),雖然單量占比不高,但對(duì)當(dāng)時(shí)技術(shù)架構(gòu)沖擊感,是比較大的。
 
一開(kāi)始外賣(mài)和早餐的交互是這樣的:

千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!
 
我猜這時(shí)候,一定會(huì)有小朋友有一堆問(wèn)號(hào)...
我解釋一下背景:
1、早餐獨(dú)立于餐飲完全搭建了一套新的體系(用戶(hù)、店鋪、訂單、配送等等)。
2、因?yàn)橹Ц稕](méi)法獨(dú)立搞,而支付在2016年初之前,是耦合在用戶(hù)系統(tǒng)里的,并且,這套支付就是純粹為外賣(mài)定制的。
 
于是,作為「創(chuàng)新」部門(mén)的「創(chuàng)新業(yè)務(wù)」,為了快速試錯(cuò),完全自己搭建了一套完整的電商雛形,而為了使用支付,硬湊著“借”用了外賣(mài)的交易鏈路。這個(gè)方案是早餐的研發(fā)同學(xué)和支付的研發(fā)同學(xué)確定并實(shí)施的,訂單無(wú)感知的當(dāng)了一把工具人。
 
當(dāng)初我知道的時(shí)候,就已經(jīng)長(zhǎng)這樣了。我是什么時(shí)候知道的,出鍋的時(shí)候,很真實(shí)。當(dāng)時(shí) PPE 和 PROD 沒(méi)有完全隔離,一次錯(cuò)誤的操作導(dǎo)致 PROD 的異步任務(wù)被拉取到 PPE ,再經(jīng)過(guò)一次轉(zhuǎn)移,最后沒(méi)有 worker 消費(fèi)導(dǎo)致訂單被取消。
 

餓配送會(huì)員卡

在 2016 年初,業(yè)務(wù)方提過(guò)來(lái)一個(gè)需求,希望餓了么配送會(huì)員卡的售賣(mài)能夠線(xiàn)上化,此前是做了實(shí)體卡依靠騎手線(xiàn)下推銷(xiāo)的方式。正好,經(jīng)過(guò)之前的架構(gòu)評(píng)審,我們也需要一個(gè)流量較小的業(yè)務(wù)模式,來(lái)實(shí)踐我們新的架構(gòu)設(shè)想,于是,就有了我們這套虛擬商品售賣(mài)的訂單系統(tǒng)。
 
我們抽象了一套最簡(jiǎn)單的狀態(tài)模型:
 
千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!
最核心的觀點(diǎn):
 1、天下所有的交易,萬(wàn)變不離其宗,主要的節(jié)點(diǎn)是較為穩(wěn)定的。
2、C 端購(gòu)買(mǎi)行為較為簡(jiǎn)單,而 B 端的交付則可能千變?nèi)f化。
3、越是核心的系統(tǒng),越應(yīng)該保持簡(jiǎn)單。
 
千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!
上下游交互如上,商品的管理、營(yíng)銷(xiāo)、導(dǎo)購(gòu)等,都交給業(yè)務(wù)團(tuán)隊(duì)自己,交易系統(tǒng)最核心的職責(zé)是提供一條通路和承載交易的數(shù)據(jù)。
 
在數(shù)據(jù)上的設(shè)計(jì),買(mǎi)賣(mài)雙方、標(biāo)的物、進(jìn)行階段,這三個(gè)是當(dāng)時(shí)我們認(rèn)為較為必要的,當(dāng)然,現(xiàn)在我可以給出更為標(biāo)準(zhǔn)的模型,但是,當(dāng)時(shí),我們真沒(méi)想那么多。
 
 所以,交易主表拆成了兩。

 一張基礎(chǔ)表,包含主要買(mǎi)方ID、買(mǎi)方ID、狀態(tài)碼、業(yè)務(wù)類(lèi)型、支付金額。業(yè)務(wù)類(lèi)型是用來(lái)區(qū)分不同買(mǎi)賣(mài)方體系的。

另一張成為擴(kuò)展表,包含標(biāo)的物列表、營(yíng)銷(xiāo)信息列表、收貨手機(jī)號(hào)等等,屬于明細(xì),允許業(yè)務(wù)方有一定的自由空間。
 
(PS: 事后來(lái)看,標(biāo)的物、營(yíng)銷(xiāo)信息等等,雖然是可供上游自己把控的,但是需要對(duì)范式從代碼層面進(jìn)行約束,否則治理會(huì)比較麻煩,業(yè)務(wù)方真是什么都敢塞...)
 
拆兩張表,背后的原因,一是訂單一旦生成,快照的職責(zé)就幾乎完成了,剩下最關(guān)鍵的是狀態(tài)維護(hù),高頻操作也集中在狀態(tài)上,那么讓每條記錄足夠的小有助于保障核心流程;二是參照餐飲訂單的經(jīng)驗(yàn), 2/3 的存儲(chǔ)空間是用在了明細(xì)上,特別是幾個(gè) Json 字段。
 
整個(gè)虛擬訂單系統(tǒng)搭建好之后,很多平臺(tái)售賣(mài)性質(zhì)的業(yè)務(wù)都通過(guò)這套系統(tǒng)接入,對(duì)我們自身來(lái)說(shuō),接入成本開(kāi)發(fā)+測(cè)試只需要 2~3 天以?xún)?nèi),而整個(gè)業(yè)務(wù)上線(xiàn)一般一個(gè)星期以?xún)?nèi)就可以,我們很開(kāi)心,前臺(tái)業(yè)務(wù)團(tuán)隊(duì)也很開(kāi)心。因?yàn)闆](méi)有大規(guī)模查詢(xún)的場(chǎng)景,很長(zhǎng)一段時(shí)間,穩(wěn)定支持每日幾十萬(wàn)的成單,幾十核的資源綽綽有余。
 
這其實(shí)是一個(gè)簡(jiǎn)單的平臺(tái)化系統(tǒng)的雛形了。
 

其它

圍繞交易,我們其實(shí)還衍生出一些業(yè)務(wù),廣義上,當(dāng)時(shí)是訂單團(tuán)隊(duì)來(lái)負(fù)責(zé),也是組織架構(gòu)影響導(dǎo)致,
 
例如「準(zhǔn)時(shí)達(dá)」這個(gè)IP,技術(shù)側(cè)是我團(tuán)隊(duì)主own從無(wú)到有實(shí)現(xiàn)的,同時(shí)又衍生出一塊 「交易賠付中心」,用來(lái)收口一筆交易過(guò)程中所有的賠付(包括紅包、代金券、現(xiàn)金、積分等),;
 
為了提升用戶(hù)交易體驗(yàn),我們發(fā)起了一個(gè)「交易觸達(dá)中心」(后演化為公司通用的觸達(dá)中心),收口了交易過(guò)程中對(duì)用戶(hù)的短信、push、電話(huà)等等觸達(dá)方式,特別是提升了極端case的觸達(dá)率,同時(shí),減少對(duì)用戶(hù)的反復(fù)騷擾。
 

服務(wù)和業(yè)務(wù)治理

 
上邊說(shuō)的大都是一些技術(shù)細(xì)節(jié)上的提升,下邊兩件事,則是應(yīng)用架構(gòu)上的重大演化,也奠定了之后應(yīng)用架構(gòu)的走向。
 

逆向中的售中和售后

2016  年中旬,業(yè)務(wù)背景,為了提升用戶(hù)在不滿(mǎn)場(chǎng)景下的體驗(yàn)(在我們的白板上密密麻麻貼了幾十個(gè)case),同時(shí)為了縮短結(jié)算賬期(因?yàn)槟嫦蛴行r(shí)間長(zhǎng)達(dá)七天,結(jié)算強(qiáng)依賴(lài)了這個(gè)時(shí)間)。
 
在 JN 的發(fā)起下,我們從原來(lái)的訂單中,單獨(dú)把逆向拆出來(lái),并且將原來(lái)的訂單組拆分成兩個(gè)團(tuán)隊(duì),我推薦了其中一位同學(xué)成為新團(tuán)隊(duì)的 Team Leader 。
 
對(duì)于正向來(lái)說(shuō),最核心的職責(zé)是保障交易的順暢,因此它重點(diǎn)追求的是高性能、高并發(fā)和穩(wěn)定性,越是清晰簡(jiǎn)單越好,主次清楚,依賴(lài)干凈,越容易快速定位問(wèn)題,快速恢復(fù)。
 
逆向的并發(fā)遠(yuǎn)小于正向,只有 1% 的訂單才會(huì)需要走到逆向,然而,業(yè)務(wù)邏輯的分支和層次關(guān)系復(fù)雜度,則遠(yuǎn)大于正向,需要更強(qiáng)的業(yè)務(wù)抽象。雖然穩(wěn)定和性能對(duì)逆向同樣很重要,但是相對(duì)沒(méi)那么高。
 
因?yàn)楹诵膯?wèn)題域不同,服務(wù)要求級(jí)別不同,拆分是順理成章的事情。
 
實(shí)際拆分過(guò)程,還是蠻痛苦的,大家都是在探索,我和逆向組,包括和老板,我們口水戰(zhàn)打了無(wú)數(shù)次。
 
當(dāng)時(shí)的最終形態(tài)如下(也還是有問(wèn)題的,在后邊的幾年我負(fù)責(zé)逆向后,把售中和售后合并了):

千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!
第一步,是增加一個(gè)訂單狀態(tài),用以表示訂單完成(約等于收貨,因?yàn)槭肇浐笠话懔ⅠR就完成了,但二者概念上還是有一些差別)。光增加這個(gè)狀態(tài),推動(dòng)上下游,包括APP的升級(jí),花費(fèi)了近3個(gè)月。

第二步,搭建一套退單,訂單完成狀態(tài)灰度完成后,以這個(gè)狀態(tài)作為訂單生命周期的完結(jié)點(diǎn),后續(xù)由退單負(fù)責(zé)。這樣清結(jié)算的入賬和扣款也就相互獨(dú)立了。

第三步,將訂單中涉及到售中的邏輯也一并切流到售中服務(wù)。(關(guān)于售中、售后的演化,后邊還有機(jī)會(huì)再展開(kāi))
 
我們當(dāng)時(shí)踏入的其中一個(gè)坑,是沒(méi)有把狀態(tài)和上層事件剝離的比較干凈,最終體現(xiàn)在業(yè)務(wù)邊界和分布式事務(wù)上有很多問(wèn)題。
 
后來(lái)吵過(guò)幾次之后,訂單系統(tǒng)的主干邏輯其實(shí)已經(jīng)被剝離的比較簡(jiǎn)單了,主要工作就是定義了狀態(tài)之間的關(guān)系,比如 A->C,B->C,A->B,這里的A、B、C和能否扭轉(zhuǎn)都是訂單定義的,這層的業(yè)務(wù)含義很輕,重點(diǎn)在 *->C 我們認(rèn)為是一個(gè)場(chǎng)景,上層來(lái)負(fù)責(zé)。

舉個(gè)例子, C 這個(gè)狀態(tài)是訂單無(wú)效,除開(kāi)完結(jié)狀態(tài)的訂單,任何狀態(tài)都有一定條件可變到無(wú)效,滿(mǎn)足什么樣的條件是由業(yè)務(wù)形態(tài)決定,適合放在售中服務(wù)中,他來(lái)決定要不要觸發(fā)訂單去扭轉(zhuǎn)狀態(tài)。類(lèi)似的還有訂單收貨。
 
這個(gè)時(shí)候已經(jīng)有了狀態(tài)機(jī)的神在(重構(gòu)成狀態(tài)機(jī)的實(shí)現(xiàn)方式,放到17年初再說(shuō))
 
特別要說(shuō)明的是紅色的那條線(xiàn),確實(shí)是這種時(shí)效要求較高的交易場(chǎng)景下一個(gè)折中的設(shè)計(jì),這條線(xiàn)最主要的任務(wù),純粹就是打標(biāo),在訂單上打一個(gè)標(biāo)表示是否有售后。我們參考了當(dāng)時(shí)的電商(淘寶、京東),從端上的頁(yè)面就完成垂直拆開(kāi),對(duì)系統(tǒng)設(shè)計(jì)來(lái)說(shuō),要簡(jiǎn)單的多,而我們沒(méi)辦法這么做,這個(gè)是由業(yè)務(wù)形態(tài)決定的,商家在極短時(shí)間內(nèi)要完成接單,同時(shí)還要時(shí)刻關(guān)注異常case,很多頁(yè)面在權(quán)衡下,要照顧用戶(hù)體驗(yàn)。也就是說(shuō),雖然系統(tǒng)拆開(kāi)了,但是在最上層的業(yè)務(wù)仍然不能拆開(kāi),甚至,內(nèi)部也有很多聲音,我們只是希望退款,為什么要我識(shí)別、區(qū)分并對(duì)接兩套系統(tǒng)。因此,一部分?jǐn)?shù)據(jù)是回寫(xiě)到了訂單上。
 
在這個(gè)階段,最受用的兩句話(huà):
 
1、對(duì)事不對(duì)人: 無(wú)論怎么吵,大家都是想把事情做的更好,底線(xiàn)是不要上升到人;(沒(méi)有什么是一杯下午茶解決不了的)。
2、堅(jiān)持讓一件事情變成更有益的: 誰(shuí)也不是圣賢,無(wú)論當(dāng)初的決定是什么,沒(méi)有絕對(duì)的說(shuō)服對(duì)方,拍板后就執(zhí)行,發(fā)現(xiàn)問(wèn)題就解決,而不是抱怨之前的決策。(與之相對(duì)的是,及時(shí)止損,二者并不沖突,但同樣需要決斷)。
 

物流對(duì)接

8月初計(jì)劃把 MQ 業(yè)務(wù)邏輯交接給我,因?yàn)樵O(shè)計(jì)理念不同,語(yǔ)言棧也不同,第一件事情便是著手重構(gòu)。
 
在這里先談?wù)剝蓚€(gè)“過(guò)時(shí)的”架構(gòu)設(shè)計(jì)。
 
ToC & ToB & ToD:


在2016年初,有一個(gè)老的名詞,現(xiàn)在絕大部分人都不知道的東西: BOD。

這是早起餓了么自配送的形態(tài),這套業(yè)務(wù)體現(xiàn),把訂單、店鋪、配送、結(jié)算等在業(yè)務(wù)上全耦合在一團(tuán)。餓了么自己的大物流體系從 2015 年中旬開(kāi)始搭建,到了這個(gè)時(shí)間,順應(yīng)著要做一個(gè)大工程, BOD 解耦。
 
這次解耦,誕生了服務(wù)包、ToB單、ToD單。
 
稍稍解釋一下業(yè)務(wù)背景,那時(shí)候的訴求,平臺(tái)將一些服務(wù)打包售賣(mài)給商戶(hù),和商戶(hù)簽約,這里售賣(mài)的服務(wù)中就包括了配送服務(wù)。那么,商戶(hù)使用配送與否,就影響到了商戶(hù)的傭金和應(yīng)收,然而,這個(gè)行業(yè)的特色創(chuàng)新,就是在商戶(hù)接單的時(shí)候,告訴商戶(hù),交易完成,你確切能夠收入的錢(qián)是多少,相當(dāng)于預(yù)先讓商戶(hù)看到一個(gè)大概率正確(不考慮售中的異常)的賬單,還得告訴商家,最終以賬單為準(zhǔn)。
 
這其實(shí)是分賬和分潤(rùn)的一些邏輯,就把清結(jié)算域的業(yè)務(wù)引入到交易鏈路上,清結(jié)算是常年做非實(shí)時(shí)業(yè)務(wù)的,那么計(jì)算商戶(hù)預(yù)計(jì)收入這件事,撕了幾天之后,自然就落到到了訂單團(tuán)隊(duì)上。另外一個(gè)背景,當(dāng)時(shí)有很多攜程系過(guò)來(lái)的同學(xué),攜程的業(yè)務(wù)形態(tài)是用戶(hù)向平臺(tái)下單,平臺(tái)再到供應(yīng)商去下單,于是,ToC、ToB、ToD的概念,就這么被引入了。
 
我接到的任務(wù),就是要做一套 ToB 單。當(dāng)時(shí)覺(jué)得這個(gè)形態(tài)不對(duì),餓了么的交易和攜程的交易是不一樣的。我向主管表示反對(duì)這個(gè)方案,但是,畢竟畢業(yè)半年沒(méi)多少沉淀,我拿不出來(lái)多少清晰有力的理由,也有一些其他人掙扎過(guò),總之,3月初正式上線(xiàn)灰度。
 
千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!
這個(gè)圖可以看出來(lái)幾個(gè)顯而易見(jiàn)的問(wèn)題:
1、交易被拆成了幾段,而用戶(hù)、商戶(hù)實(shí)際都需要感知到每一段。并且每個(gè)階段對(duì)時(shí)效、一致性都有一定的要求。
2、平臺(tái)和物流只通過(guò)紅色的先來(lái)交互,這個(gè)通道很重
3、公式線(xiàn)下同步...

ToD
 上邊的架構(gòu)實(shí)施后,到了 7 月份,ToD 這部分,變成了平臺(tái)和物流唯一的通道,太重了,業(yè)務(wù)還沒(méi)發(fā)展到那個(gè)階段,弊大于利。商戶(hù)端配送組的同學(xué)不開(kāi)心,物流的同學(xué)不開(kāi)心,訂單的同學(xué)也不開(kāi)心。
 
正好,訂單在做增加完結(jié)狀態(tài)這個(gè)事。我們認(rèn)為,訂單需要管控的生命周期,應(yīng)該延伸到配送,并且配送屬于子生命周期,是交易的一部分。于是,7 月底, ToD 也交給了我,又到了喜聞樂(lè)見(jiàn)的重構(gòu)環(huán)節(jié)。
 
作為商戶(hù)端技術(shù)體系的外部人員來(lái)看,當(dāng)時(shí) ToD 的設(shè)計(jì)非常的反人類(lèi)。
千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!
 
我們真正接手的時(shí)候發(fā)現(xiàn),當(dāng)時(shí)商戶(hù)端的應(yīng)用架構(gòu)大概是這樣的:

            千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!

有這么一個(gè)基礎(chǔ)設(shè)施公共層,這一層封裝了對(duì) DB、Redis 等公共操作。也就是說(shuō),同一個(gè)領(lǐng)域的業(yè)務(wù)邏輯和數(shù)據(jù),是根據(jù)這個(gè)體系的分層原則分在了不同層級(jí)的服務(wù)中,一個(gè)域內(nèi)的業(yè)務(wù)層要操作它自己的數(shù)據(jù),也需要通過(guò)接口進(jìn)行。它可能有一定道理在(包括 2020 年我在面試一些候選人的時(shí)候發(fā)現(xiàn),也有一些公司是這種做法),但是,交接出來(lái)的時(shí)候,痛苦!復(fù)雜的耦合,相當(dāng)于要從一個(gè)錯(cuò)綜復(fù)雜的體系里剝出一條比較干凈獨(dú)立的線(xiàn)。

那后來(lái),我們改成下邊的樣子:
 
千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!
 
1、ToB 和 ToD 被合并成為了一層,放在了 osc.blink 這個(gè)服務(wù)里,并且消滅這兩個(gè)概念,作為訂單的擴(kuò)展數(shù)據(jù),而不是從交易中切出來(lái)的一段。
2、平臺(tái)和物流如果有數(shù)據(jù)交互,不一定需要通過(guò)這個(gè)對(duì)接層,這條鏈路最好只承載實(shí)時(shí)鏈路上配送所必須的數(shù)據(jù)。物流 Apollo 可以自己到平臺(tái)其它地方取其需要的數(shù)據(jù)。(這里其實(shí)有一些問(wèn)題沒(méi)解,osc.blink 和 Apollo 在兩方的定位并不完全一致,Apollo 作為運(yùn)單中心收攏了和平臺(tái)對(duì)接的所有數(shù)據(jù))
3、節(jié)點(diǎn)與節(jié)點(diǎn)之間的交互盡可能簡(jiǎn)單,節(jié)點(diǎn)自身保證自身的健壯性。原先推單是通過(guò)消息進(jìn)行,現(xiàn)在改成了 RPC 進(jìn)行,推的一方可以主動(dòng)重推(有一個(gè)憑證保證冪等),拉的一方有補(bǔ)償拉取鏈路。

 (圖示的3.1,是由于當(dāng)時(shí)外賣(mài)平臺(tái)和物流平臺(tái),機(jī)房部署在不同城市,多次跨機(jī)房請(qǐng)求影響巨大,所以鏈路上由這個(gè)服務(wù)進(jìn)行了一次封裝)。

到了8月底,呼單部分就完成上線(xiàn)。9月份開(kāi)始把數(shù)據(jù)進(jìn)行重構(gòu)。
 
 

小結(jié)


 

到了 2016 年底,我們的交易體系整體長(zhǎng)這樣:


千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!
 
當(dāng)時(shí)一些好的習(xí)慣和意識(shí),挺重要:
 
1、理清權(quán)力和職責(zé):代碼倉(cāng)庫(kù)權(quán)限的回收,發(fā)布權(quán)限的回收,數(shù)據(jù)庫(kù)和消息隊(duì)列連接串管控等等。

2、保持潔癖:
a. 及時(shí)清理無(wú)用邏輯(例如,我每隔一兩個(gè)月就會(huì)組織清理一批沒(méi)有流量的接口,也會(huì)對(duì)流量增長(zhǎng)不正常的接口排查,下游有時(shí)候會(huì)怎么方便怎么來(lái)).
b. 及時(shí)清理無(wú)用的配置,不用了立馬干掉,否則交接幾次之后估計(jì)就沒(méi)人敢動(dòng)了.
c. 及時(shí)治理異常和解決錯(cuò)誤日志,這將大大的減小你告警的噪音和排查問(wèn)題的干擾項(xiàng)。

3、理想追求極致但要腳踏實(shí)地。

4、堅(jiān)持測(cè)試的標(biāo)準(zhǔn)和執(zhí)行的機(jī)制。
a. 堅(jiān)持自動(dòng)化建設(shè)
b. 堅(jiān)持性能測(cè)試
c. 堅(jiān)持故障演練

5、不斷的請(qǐng)教、交流和思維沖撞。

6、Keep Simple, Keep Easy.

7、對(duì)事不對(duì)人。
 
架構(gòu)的演進(jìn),最好是被業(yè)務(wù)驅(qū)動(dòng),有所前瞻,而不是事故驅(qū)動(dòng)?;剡^(guò)頭發(fā)現(xiàn),我們有一半的演進(jìn),其實(shí)是伴隨在事故之后的。值得慶幸的是,那個(gè)時(shí)候技術(shù)可自由支配的時(shí)間更多一些。
 
如果你閱讀到這里,有很多共鳴和感觸,但是又說(shuō)不出來(lái),那么你確實(shí)把自己的經(jīng)歷整理出一些腦圖了。
 
在實(shí)習(xí)的半年,每個(gè)月都會(huì)感覺(jué)日新月異,在畢業(yè)的最初 1 年半里,總覺(jué)得 3 個(gè)月前的自己弱爆了,最初的這 2 年,是我在餓了么所經(jīng)歷的最為寶貴的時(shí)間之一。
 
上篇內(nèi)容就到這里,如果有所收獲,可以關(guān)注公眾號(hào),等待下篇的內(nèi)容。

作者信息:
楊凡,花名挽晴,餓了么高級(jí)架構(gòu)師,2014 年加入餓了么,2018 年隨餓了么被阿里巴巴收購(gòu)一同加入阿里巴巴,4 年團(tuán)隊(duì)管理經(jīng)驗(yàn),4 年主要從事餓了么交易系統(tǒng)建設(shè),也曾負(fù)責(zé)過(guò)餓了么賬號(hào)、評(píng)價(jià)、IM、履約交付等系統(tǒng)。

特別推薦一個(gè)分享架構(gòu)+算法的優(yōu)質(zhì)內(nèi)容,還沒(méi)關(guān)注的小伙伴,可以長(zhǎng)按關(guān)注一下:

千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!

長(zhǎng)按訂閱更多精彩▼

千萬(wàn)級(jí)餓了么交易系統(tǒng)架構(gòu) 5 年演化史!

如有收獲,點(diǎn)個(gè)在看,誠(chéng)摯感謝

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專(zhuān)欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車(chē)的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國(guó)汽車(chē)技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車(chē)工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車(chē)。 SODA V工具的開(kāi)發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車(chē) 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來(lái)越多用戶(hù)希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來(lái)越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開(kāi)幕式在貴陽(yáng)舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱(chēng),數(shù)字世界的話(huà)語(yǔ)權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營(yíng)業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營(yíng)商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國(guó)電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱(chēng)"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉