背景介紹
S公司是一家數(shù)據(jù)服務(wù)公司,有 20 000 多名客戶使用公司的軟件,公司使用 API 收集和清理客戶的數(shù)據(jù)。S 公司提供的產(chǎn)品如下圖所示。


微服務(wù)是當(dāng)今主流的架構(gòu)模式之一。S 公司的系統(tǒng)進(jìn)行了一次微服務(wù)改造,并取得了不錯(cuò)的效果。
重構(gòu)后的規(guī)模:400 private repos;70 different services(workers)。
取得的收益:
visibility(可見(jiàn)性)。在微服務(wù)架構(gòu)中,非常方便對(duì)每個(gè)服務(wù)進(jìn)行監(jiān)控(sysdig、htop、iftop 等)。
微服務(wù)大大降低了配置和構(gòu)建部署成本。
消除了在現(xiàn)有服務(wù)中附加不同功能的誘惑。
產(chǎn)生了很多對(duì)外依賴很少的服務(wù):僅僅需要從隊(duì)列里讀取和處理數(shù)據(jù),然后發(fā)送結(jié)果即可。非常適合小團(tuán)隊(duì)協(xié)同工作。
定位問(wèn)題變得容易。可以對(duì)每一個(gè) microworker 進(jìn)行 Datadog 式的監(jiān)控,如下圖所示

比如,類似于內(nèi)存泄漏的問(wèn)題,可以很容易將問(wèn)題范圍縮小到 50~100 行代碼內(nèi)。
簡(jiǎn)單地講,微服務(wù)是一種面向服務(wù)的軟件體系結(jié)構(gòu),其中服務(wù)端的應(yīng)用程序是通過(guò)組合許多單一用途、低占用空間的網(wǎng)絡(luò)服務(wù)而成的。其優(yōu)點(diǎn)是改進(jìn)的模塊化減少了測(cè)試負(fù)擔(dān),可以更好地進(jìn)行功能組合,環(huán)境隔離和開(kāi)發(fā)團(tuán)隊(duì)具備自主權(quán)。經(jīng)常與之拿來(lái)對(duì)比的是單體架構(gòu),在單體架構(gòu)中,大量的功能存在于單個(gè)服務(wù)中,作為單個(gè)單元進(jìn)行測(cè)試、部署和擴(kuò)展。
另外,操作復(fù)雜度和負(fù)載都很高的產(chǎn)品一般都會(huì)選擇微服務(wù)架構(gòu),它使基礎(chǔ)結(jié)構(gòu)更加靈活、可擴(kuò)展性強(qiáng),并且更易于監(jiān)控。
但不幸的事情發(fā)生了,當(dāng)重構(gòu)完成兩年以后,團(tuán)隊(duì)沒(méi)有更快地交付,而是陷入了“爆炸性”的復(fù)雜性中,架構(gòu)的優(yōu)點(diǎn)變成了負(fù)擔(dān)。隨著速度的下降,失敗率激增,團(tuán)隊(duì)也變得不堪重負(fù)。
系統(tǒng)處理流程概述
S公司的客戶數(shù)據(jù)基礎(chǔ)設(shè)施每秒可接收數(shù)十萬(wàn)個(gè)事件,并將它們轉(zhuǎn)發(fā)給合作伙伴的 API,即服務(wù)端 destination。目前,有超過(guò)一百種類型的 destination,如 Google Analytics, Optimizely,或自定義 Webhook。
幾年前,架構(gòu)相對(duì)簡(jiǎn)單,一個(gè)API 即可接收事件并將其轉(zhuǎn)發(fā)到分布式消息隊(duì)列。事件是由 Web 或移動(dòng)應(yīng)用程序生成的 JSON 對(duì)象,其中包含有關(guān)用戶及其操作的信息。
一旦請(qǐng)求失敗,有時(shí)會(huì)嘗試在稍后的時(shí)間再次發(fā)送該事件。有些失敗可以安全重試,有些則不行。可重試錯(cuò)誤是指那些 destination 不做任何更改就可以接受的錯(cuò)誤,如 HTTP 500、速率限制和超時(shí)。不可重試錯(cuò)誤是指可以確信 destination 永遠(yuǎn)不會(huì)接受的請(qǐng)求,如具有無(wú)效憑證或缺少必需字段的請(qǐng)求。
此時(shí),單個(gè)隊(duì)列既包含最新的事件,也包含跨越所有destination 的可能有多次重試的事件,這會(huì)導(dǎo)致“隊(duì)頭阻塞”。也就是說(shuō),在這種特殊情況下,如果一個(gè) destination 變慢或下降,則重試將會(huì)導(dǎo)致隊(duì)列擁擠,從而導(dǎo)致所有 destination 的延遲。
假設(shè)destinationX 遇到了一個(gè)臨時(shí)問(wèn)題,每個(gè)請(qǐng)求都由于超時(shí)而出錯(cuò)。現(xiàn)在,這不僅會(huì)創(chuàng)建大量尚未到達(dá) destinationX 的積壓請(qǐng)求,而且還會(huì)將每個(gè)失敗事件放回隊(duì)列中進(jìn)行重試,如下圖所示。雖然系統(tǒng)將自動(dòng)伸縮以響應(yīng)增加的負(fù)載,但隊(duì)列深度的突然增加將超過(guò)系統(tǒng)的伸縮能力,從而導(dǎo)致最新事件的延遲。

為了解決“隊(duì)頭阻塞”問(wèn)題,該團(tuán)隊(duì)為每個(gè) destination 都創(chuàng)建了單獨(dú)的服務(wù)和隊(duì)列。這個(gè)新的體系結(jié)構(gòu)包括一個(gè)額外的路由器進(jìn)程,該進(jìn)程接收入站事件并將事件的副本分發(fā)到每個(gè)選定的 destination 中,如下圖所示。現(xiàn)在,如果一個(gè) destination 出現(xiàn)問(wèn)題,則只有它的隊(duì)列會(huì)阻塞,其他 destination 不會(huì)受到影響。這種微服務(wù)風(fēng)格的體系結(jié)構(gòu)將 destination 彼此隔離,這在 destination 經(jīng)常發(fā)生問(wèn)題時(shí),至關(guān)重要。

產(chǎn)生的問(wèn)題
共享庫(kù)多版本問(wèn)題。隨后,系統(tǒng)又增加了 50 多個(gè)新的 destination,這就意味著有 50個(gè)新的 repo。為了減輕開(kāi)發(fā)和維護(hù)這些代碼庫(kù)的負(fù)擔(dān),團(tuán)隊(duì)創(chuàng)建了共享庫(kù),來(lái)處理公共轉(zhuǎn)換和功能(如 HTTP 請(qǐng)求處理)。然而,一個(gè)新的問(wèn)題出現(xiàn)了。對(duì)這些共享庫(kù)的測(cè)試和部署更改會(huì)影響所有的 destination,此時(shí)必須測(cè)試和部署幾十個(gè)服務(wù)。在時(shí)間緊迫的情況下,工程師只會(huì)在單個(gè)目標(biāo)的代碼庫(kù)中包含這些庫(kù)的更新版本。這樣一來(lái),隨著時(shí)間的推移,這些共享庫(kù)的版本開(kāi)始在不同的目標(biāo)代碼庫(kù)中出現(xiàn)不同的分支版本,原本擁有的在每個(gè)目標(biāo)代碼庫(kù)之間減少自定義的優(yōu)勢(shì)開(kāi)始不復(fù)存在。最終,它們都使用了這些共享庫(kù)的不同版本。本可以構(gòu)建一些工具來(lái)自動(dòng)進(jìn)行更改,但此時(shí),不僅開(kāi)發(fā)人員的工作效率受到了影響,還遇到微服務(wù)架構(gòu)的其他問(wèn)題。
負(fù)載模式問(wèn)題。每個(gè)服務(wù)都有不同的負(fù)載模式,其中一些服務(wù)每天處理少量事件,而另一些服務(wù)每秒處理數(shù)千個(gè)事件。對(duì)于處理少量事件的 destination,當(dāng)出現(xiàn)意外的負(fù)載峰值時(shí),操作員將不得不手動(dòng)擴(kuò)展服務(wù),以滿足需求。
伸縮調(diào)優(yōu)問(wèn)題。雖然確實(shí)實(shí)現(xiàn)了自動(dòng)伸縮,但每個(gè)服務(wù)都有不同的 CPU 和內(nèi)存資源組合,使得自動(dòng)伸縮配置的調(diào)優(yōu)更像是藝術(shù)而不是科學(xué)。destination 的數(shù)量繼續(xù)快速增加,團(tuán)隊(duì)平均每個(gè)月增加三個(gè) destination,這意味著有了更多的 repo、隊(duì)列和服務(wù)。
管理開(kāi)銷。當(dāng)服務(wù)個(gè)數(shù)超過(guò) 140 個(gè)時(shí),對(duì)團(tuán)隊(duì)來(lái)說(shuō)管理所有服務(wù)是一筆巨大的開(kāi)銷。團(tuán)隊(duì)每天睡不好覺(jué),最常見(jiàn)的場(chǎng)景就是線上工程師處理負(fù)載峰值。
退回到單體
最終,團(tuán)隊(duì)決定拋棄這些微服務(wù)和repo,并重新將服務(wù)并到一起。然而,退回到單體服務(wù)非常困難。如果所有 destination 都有一個(gè)單獨(dú)的隊(duì)列,那么所有工程師都必須檢查每個(gè)隊(duì)列的工作,這會(huì)給 destination 服務(wù)增加一層復(fù)雜性。為了解決這個(gè)問(wèn)題,系統(tǒng)新增了一種“離心機(jī)(Centrifuge)”組件,并將所有 destination 進(jìn)行了合并,如下圖所示。

同時(shí),還需要將所有repo 進(jìn)行合并。一旦所有 destination 的代碼存在于一個(gè) repo 中,它們就可以合并為一個(gè)服務(wù)。這樣,開(kāi)發(fā)人員的生產(chǎn)率大大提高了,不再需要部署 140 多個(gè)服務(wù)來(lái)改變一個(gè)共享庫(kù),一個(gè)工程師在幾分鐘內(nèi)就可以部署這項(xiàng)服務(wù),這一變化也有利于運(yùn)維。由于所有 destination 都位于一個(gè)服務(wù)中,因此很好地混合了 CPU 和內(nèi)存密集型 destination,這使得利用擴(kuò)展服務(wù)來(lái)滿足需求變得非常容易。由于大型工作池可以吸收負(fù)載峰值,因此團(tuán)隊(duì)不必再為處理少量負(fù)載的 destination 進(jìn)行分頁(yè)。
一些犧牲
雖然已取得了巨大的改進(jìn),然而其中也有些“犧牲”。
故障隔離困難。由于所有東西都在一個(gè)單體中運(yùn)行,如果一個(gè) destination 中引入了導(dǎo)致服務(wù)崩潰的 bug,那么所有 destination 的服務(wù)都會(huì)崩潰。雖然已經(jīng)有全面的自動(dòng)化測(cè)試,但是測(cè)試無(wú)法完全保障。后續(xù)演進(jìn)的方向是設(shè)計(jì)一種更健壯的方法,以防止單個(gè) destination導(dǎo)致整個(gè)服務(wù)癱瘓,同時(shí)仍將所有 destination 保持在一個(gè)單體中。
緩存(內(nèi)存中)效率變低。以前,由于每個(gè) destination 都有一個(gè)服務(wù),低流量 destination只有少數(shù)進(jìn)程,這意味著它們控制平面數(shù)據(jù)的內(nèi)存緩存將保持熱度。現(xiàn)在,由于緩存分散在3000 多個(gè)進(jìn)程中,因此命中率大大降低。最后,考慮到實(shí)際的運(yùn)營(yíng)收益,接受了效率的損失。
更新一個(gè)依賴項(xiàng)的版本可能會(huì)破壞多個(gè)destination。雖然解決了之前多版本依賴的問(wèn)題,但如果想使用庫(kù)的最新版本,則必須更新其他 destination。目前,通過(guò)全面的自動(dòng)化測(cè)試套件,可以快速看到新老依賴版本的不同。
總結(jié)
引入微服務(wù)架構(gòu),并通過(guò)將destination 彼此隔離解決了管道中的性能問(wèn)題。然而,當(dāng)需要批量更新時(shí),由于缺乏適當(dāng)?shù)墓ぞ邅?lái)測(cè)試和部署微服務(wù),因此結(jié)果反而使開(kāi)發(fā)人員的生產(chǎn)力迅速下降。
在進(jìn)行架構(gòu)選擇時(shí),并不存在絕對(duì)的好壞,是一個(gè)權(quán)衡的過(guò)程,需要從多個(gè)維度考慮。
新的架構(gòu)是否能帶來(lái)新的復(fù)雜性,帶來(lái)的復(fù)雜性是否能被充分評(píng)估,以及如何應(yīng)對(duì),如上文提到的“共享多版本的問(wèn)題”。
新架構(gòu)下系統(tǒng)的運(yùn)維成本是否增加,如果增加能否接受,如上文提到的“負(fù)載模式問(wèn)題”。
在“享受”新架構(gòu)帶來(lái)的好處的同時(shí),能否真正掌控新架構(gòu),如上文提到的“伸縮調(diào)優(yōu)問(wèn)題”。
新的架構(gòu)是否帶來(lái)管理開(kāi)銷,成本能否接受,如上文提到的“管理開(kāi)銷”問(wèn)題。
架構(gòu)設(shè)計(jì)的誤區(qū)
盲目追求模式和原則的滿足。并不是說(shuō)模式和原則不重要,但它們不應(yīng)該成為架構(gòu)設(shè)計(jì)追求的唯一目標(biāo)。盲目追求不必要的模式和原則的滿足,往往會(huì)給系統(tǒng)帶來(lái)不必要的復(fù)雜性,使其難以理解。
追趕潮流。新的架構(gòu)形態(tài)層出不窮,令人眼花繚亂,學(xué)習(xí)到一種新的、“炫酷”的架構(gòu)設(shè)計(jì)很容易有直接拿來(lái)應(yīng)用的沖動(dòng)。這樣做的后果往往是會(huì)與實(shí)際解決的問(wèn)題脫節(jié),為系統(tǒng)帶來(lái)不必要的負(fù)擔(dān),甚至根本沒(méi)有解決任何問(wèn)題。
面面俱到,沒(méi)有重點(diǎn)。決定不要什么比要什么更難。你會(huì)看到當(dāng)某些架構(gòu)設(shè)計(jì)文檔的模板時(shí),高可用性、擴(kuò)展性、可測(cè)試性……什么都想要,不做取舍。不同系統(tǒng)的側(cè)重點(diǎn)不同,這樣做的后果往往是顧此失彼,關(guān)鍵問(wèn)題沒(méi)有得到解決。
忽視架構(gòu)腐化。架構(gòu)設(shè)計(jì)在整個(gè)軟件生命周期內(nèi),都需要守護(hù)及持續(xù)演進(jìn),否則架構(gòu)及整個(gè)系統(tǒng)都難以擺脫逐步惡化,直至消亡或重寫(xiě)的命運(yùn)。
審核編輯 :李倩
-
應(yīng)用程序
+關(guān)注
關(guān)注
38文章
3344瀏覽量
60257 -
架構(gòu)設(shè)計(jì)
+關(guān)注
關(guān)注
0文章
33瀏覽量
7227 -
微服務(wù)
+關(guān)注
關(guān)注
0文章
150瀏覽量
8103
原文標(biāo)題:S 公司的微服務(wù)“失敗”之旅
文章出處:【微信號(hào):magedu-Linux,微信公眾號(hào):馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
光伏四可裝置軟件系統(tǒng)架構(gòu):微服務(wù)化設(shè)計(jì)與容器化部署方案
基于OpenTelemetry的全鏈路追蹤微服務(wù)可觀測(cè)性實(shí)踐
Istio服務(wù)網(wǎng)格的核心原理與部署實(shí)戰(zhàn)
探索RH6016S:高性能低功耗精密運(yùn)算放大器的太空之旅
華納云VPS容器服務(wù)網(wǎng)格流量管理:實(shí)現(xiàn)微服務(wù)高效路由
基于RFID與微服務(wù)架構(gòu)的智能倉(cāng)庫(kù)管理系統(tǒng):實(shí)現(xiàn)倉(cāng)儲(chǔ)數(shù)據(jù)的全鏈路精準(zhǔn)采集與管控
如何基于Nginx構(gòu)建微服務(wù)網(wǎng)關(guān)
Jtti海外VPS微服務(wù)架構(gòu)下的日志采集與分析優(yōu)化方案
電商API的微服務(wù)架構(gòu)優(yōu)化策略
華納云服務(wù)器角色服務(wù)器失敗的原因和解決辦法
蔡司“微服務(wù)”——全能在線售后管家,24小時(shí)守護(hù)您的設(shè)備!
S公司的微服務(wù)“失敗”之旅
評(píng)論