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

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


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

來自:阿里巴巴中間件

文  |  挽晴


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


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

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

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

那我們就從2012年的太古時期講起。
 

太古



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

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

萌芽



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

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

訂單組的成立

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

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

Zeus 解耦

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

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

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

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

初探


15 年中旬到 16 年初,我們處在每天的單量在百萬以上并逐步快速增長這么一個階段。


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

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

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

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

組建測試團隊

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

第一件事情,搭建自動化集成測試。


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

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

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

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

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

第二件事情,搭建性能測試。


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

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

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

第三件事情,隨機故障演練


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

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

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

伴隨體量上漲的一系列問題

 

Redis使用的改進

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

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

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

消息使用的改進

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

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

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

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

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

 

早餐:

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

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

餓配送會員卡

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

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

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

其它

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

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

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

逆向中的售中和售后

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

千萬級餓了么交易系統(tǒng)架構(gòu) 5 年演化史!
第一步,是增加一個訂單狀態(tài),用以表示訂單完成(約等于收貨,因為收貨后一般立馬就完成了,但二者概念上還是有一些差別)。光增加這個狀態(tài),推動上下游,包括APP的升級,花費了近3個月。

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

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

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

物流對接

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


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

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

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

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

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

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

 (圖示的3.1,是由于當(dāng)時外賣平臺和物流平臺,機房部署在不同城市,多次跨機房請求影響巨大,所以鏈路上由這個服務(wù)進行了一次封裝)。

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

小結(jié)


 

到了 2016 年底,我們的交易體系整體長這樣:


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

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

3、理想追求極致但要腳踏實地。

4、堅持測試的標準和執(zhí)行的機制。
a. 堅持自動化建設(shè)
b. 堅持性能測試
c. 堅持故障演練

5、不斷的請教、交流和思維沖撞。

6、Keep Simple, Keep Easy.

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

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

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

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

長按訂閱更多精彩▼

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

如有收獲,點個在看,誠摯感謝

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

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

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

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

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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