為什么我們要放棄遷移到微服務(wù)?
最近我們開發(fā)團隊在開發(fā)計劃中有一個小停頓,技術(shù)部門認(rèn)為現(xiàn)在是將應(yīng)用從單體架構(gòu)遷移到微服務(wù)的最佳時機。
圖片來自 Pexels
經(jīng)過一個月的準(zhǔn)備和調(diào)查,我們?nèi)∠诉w移,仍然使用單體模式。對我們而言,微服務(wù)不僅幫不上忙,反而會影響到開發(fā)計劃。
我們了解微服務(wù)大約是在一年前,但是很驚訝地發(fā)現(xiàn)它并不適合我們。本篇文章把我們的經(jīng)歷寫出來,可能會對大家有借鑒意義。
發(fā)現(xiàn)問題以及早期妥協(xié)
我們嚴(yán)重依賴第三方
我們的應(yīng)用是整合外部現(xiàn)有產(chǎn)品和業(yè)務(wù)規(guī)則給用戶展現(xiàn)一個友好界面的 UI。客戶是一家 UWP App,后臺有相應(yīng)服務(wù)在第三方域和我們之間交換數(shù)據(jù)。
對第三方的依賴嚴(yán)重影響我們選擇微服務(wù)。例如,應(yīng)用經(jīng)常要在不同域之間轉(zhuǎn)換功能,使得第三方域看起來像是完全不同的一個域,如果在我們之間有一個單一服務(wù)情況還不算太糟。
然而,整個域交換在分解成多個微服務(wù)過程中就看起來很怪異了。
是我們的微服務(wù)跟第三方的分解不同嗎?我們復(fù)制了所有服務(wù)的前后端需求嗎?還是我們分解了自己的微服務(wù),仍然需要一個微服務(wù)從第三方獲取信息?所有這些問題看起來都跟微服務(wù)指南相背離。
我們和第三方合作很緊密,經(jīng)常一起協(xié)作發(fā)布版本。微服務(wù)的好處在于每個團隊都可以不受影響?yīng)毩l(fā)行服務(wù),而跨公司合作則失去了這種好處。
微服務(wù)另外一個好處在于,每個團隊只需要完整設(shè)計自己的業(yè)務(wù)問題。而我們,作為一個和第三方完全獨立的公司團隊,這種重構(gòu)看起來不可行。
我們不能有效分離微服務(wù)
我們實在找不出應(yīng)該從哪個單體應(yīng)用下手。于是我們在域模型之間連線,以決定要創(chuàng)建哪些微服務(wù)。
然而,一旦這么開始做,發(fā)現(xiàn)很多共享業(yè)務(wù)邏輯影響著微服務(wù)域的劃分。如果將微服務(wù)劃分的更細小,只能帶來更多的耦合關(guān)系,到處都需要消息總線,消息可能會出現(xiàn)大爆炸。
原因在于我們的單體式應(yīng)用是為一個業(yè)務(wù)邏輯服務(wù)的。我們?yōu)橛脩舴奖銊?chuàng)建了跨域和組的很多工作流,本質(zhì)上,UI 在過去四年中就是將各種東西整合到了一起。
另外,我們還誤解了微服務(wù)如何被隔離,以及低估了服務(wù)之間正確邊界選擇的重要性。
唯一能做到的就是為了實現(xiàn)一個標(biāo)準(zhǔn)功能,從而需要將所有相關(guān)微服務(wù)同時升級,由此要求每個微服務(wù)都不能被某個單獨團隊擁有。
共享微服務(wù)
我們大約有 12 個開發(fā)人員分布在兩個功能團隊和一個支持團隊。工作波動性很大,沒有專職負(fù)責(zé)團隊。所有團隊同時接觸同一批代碼很正常,不能將某個微服務(wù)指定給一個團隊。
考慮架構(gòu)時候一定要記得 Conway's Law,其意思是軟件架構(gòu)會模仿組織和團隊架構(gòu)增長。
微服務(wù)架構(gòu)對于不同團隊負(fù)責(zé)不同業(yè)務(wù)邏輯是比較有效的,然而,共享代碼功能的工作模式最好采用單體式架構(gòu)。
平臺并沒有準(zhǔn)備好
各種問題意味著至少六個月內(nèi),需要在 IIS 內(nèi)并行運行微服務(wù)和單體式應(yīng)用。
我們不會訪問與微服務(wù)相關(guān)的工具,如容器、Kubernetes、服務(wù)總線、API 網(wǎng)管等,也就意味著與其他微服務(wù)之間通訊有很大障礙。
因此,我們決定每個微服務(wù)都復(fù)制與存儲層轉(zhuǎn)換相關(guān)讀服務(wù)的共享邏輯。因為不能正常拆分服務(wù),也就意味著必須承擔(dān)大量復(fù)制工作量。
例如,對于某個復(fù)雜而且基礎(chǔ)的業(yè)務(wù)邏輯,必須復(fù)制黏貼并維護至少 4 個計劃中的微服務(wù)。
沒有未來清晰圖景
開發(fā)團隊只有六個月內(nèi)粗略的構(gòu)想,而且業(yè)務(wù)更改也很頻繁(業(yè)務(wù)更改需求也是司空見慣的事情),這些讓微服務(wù)化更加充滿不確定性,因為即使在短期也無法預(yù)知會出現(xiàn)什么鏈接。
微服務(wù)之間的復(fù)雜性會增加嗎?花了幾個月分離的服務(wù)會回到過去嗎?盡管我們今年早些時候做過一些 PoC,但是因為業(yè)務(wù)需求的更改不得不放棄。
架構(gòu)緊耦合
我們只有一個很窄的時間窗口,剛好能把單體式應(yīng)用分解成列出來的微服務(wù),而且沒有冗余時間應(yīng)付可能出現(xiàn)的改變,也沒有 Plan B,我們被自己給卡住了。
因為我們在計劃階段就發(fā)現(xiàn)了很多問題和挑戰(zhàn),更別說實施階段了,開發(fā)團隊非常有壓力。
缺乏經(jīng)驗
考慮到風(fēng)險和時間壓力,而且架構(gòu)師和實施專家也都沒有任何特殊經(jīng)驗,加上沒有標(biāo)準(zhǔn)工具能用,我們只能靠自己來實施,這些都更加惡化了情況。
和其他微服務(wù)大拿溝通后,他們也都發(fā)出了很多警告,并給出了不少以前并沒有的架構(gòu)建議,指出了我們在域模型之間畫線的順序。
到目前為止,由于時間緊任務(wù)重,我們的計劃包括了很多不同于標(biāo)準(zhǔn)微服務(wù)的妥協(xié)方案。
因為缺乏專家,這看起來更像一條不歸之路,開發(fā)團隊越來越焦慮。
再次拷問我們的目標(biāo)
是否解決了痛點
一旦前方布滿了困難,就失去了目標(biāo),我們暫停下來,意識到我們并沒有搞明白為什么要這樣做。
我們沒有列出痛點,而且不清楚這樣做是否可以解決痛點。更甚,微服務(wù)有可能會給我們帶來新的問題。
我們開始反思,我們從中會獲得什么好處?能解決什么問題?我們召開了更多會議討論它,但一直沒有明確的答案。
最后,我們發(fā)現(xiàn)在討論微服務(wù)過程中我們忽略了一個很重要的痛點,而且沒有足夠的時間來解決這個問題,也就是我們不能考慮微服務(wù)或者其他東西了。
潛在收益是什么
這時候我們開始反思微服務(wù)一般意義上會帶來什么好處?
自治
微服務(wù)使得團隊可以控制全棧提供一個功能。這種區(qū)隔會減少不同團隊之間協(xié)同的次數(shù),互相不影響對方的工作。
使團隊更加專注
單體式應(yīng)用中,團隊可以專注于所有任務(wù)。而采用微服務(wù)后,則是某項業(yè)務(wù)流程的專家。
他們會理解自己區(qū)域內(nèi)的業(yè)務(wù)規(guī)則和需求,知道軟件棧如何搭建,當(dāng)發(fā)生改變時會非常有信心完成。
易于擴展
有了微服務(wù),可以根據(jù)性能需求擴展服務(wù)規(guī)模。而在單體式應(yīng)用中,盡管也可以水平擴展服務(wù),但是不能獨立擴展單體應(yīng)用的模塊。
微服務(wù)粒度可以增加或者減少服務(wù)能力。也許當(dāng)發(fā)現(xiàn)性能問題時,可以參與其他工作,或者稍微喘口氣。
易于回滾
每個功能只需要修改單一微服務(wù),而且可以不影響其他團隊工作進行回滾。另外微服務(wù)還可以減少單一錯誤對整個系統(tǒng)的影響。
易于迭代
如果有一個擴充系統(tǒng)的,每個發(fā)布都很花時間并且有風(fēng)險,那么就需要大量工作處理每次發(fā)行時的問題。
微服務(wù)可以減少溝通時間和成本,團隊可以各自確定合適時間。
采用最佳技術(shù)
團隊依賴微服務(wù)可以選擇最佳技術(shù)方案。而單體式卻很難升級。
易于升級
大型應(yīng)用升級都不是一件容易的事情。尤其是要在多個團隊之間協(xié)作。相互隔離的微服務(wù)可以每次只升級一個服務(wù),更容易控制風(fēng)險。
風(fēng)險保護
微服務(wù)可以將頻繁變化和很少變化的服務(wù)分隔開,減少意外發(fā)生的風(fēng)險。
粒度減小
小型化服務(wù)更易于理解,而且可以保持設(shè)計一致。對比來看,單體式應(yīng)用會因為不同團隊的意見不統(tǒng)一帶來不一致性。
優(yōu)勢匯總
微服務(wù)有很多優(yōu)勢。但是我們能從中獲得什么?
最終,對于無法改變或者妥協(xié)的架構(gòu)只能放棄這些優(yōu)勢。我們失去了微服務(wù)帶來的隔離性。與第三方的合作減弱了從服務(wù)不相關(guān)性中帶來的好處。
權(quán)衡
大炮打蚊子
微服務(wù)也并不都是優(yōu)點,有一長串需要考慮的問題。
例如,日志,監(jiān)控,異常處理,容錯,回滾,通訊,消息格式,容器,服務(wù)發(fā)現(xiàn),備份,遙測,報警,跟蹤,工具,共享,文檔,擴展,時區(qū),階段,API 版本,網(wǎng)絡(luò)延遲,健康檢查,負(fù)載均衡,CDC測試,等等。
如果沒有微服務(wù)平臺,我們要自己面對所有上面列出來的問題。因為有痛點才要轉(zhuǎn)向微服務(wù),但是不但沒法從中獲益,還要面對一堆轉(zhuǎn)向微服務(wù)帶來的問題,我們是何苦來呢?
微服務(wù)只是名義
下圖是我們現(xiàn)在單體式應(yīng)用和微服務(wù)的對比。架構(gòu)上來看,新架構(gòu)很像單體式,各個組件還是緊耦合,也許我們只是用了微服務(wù)的標(biāo)簽。
單體式一定很差嗎
好像“單體式”就意味著落后,“微服務(wù)”就意味著先進。但是從開發(fā)團隊來看,我們的單體式應(yīng)用運行良好,基本沒有什么問題。
我們有很好的 CI/CD 配置,易于配置和回滾。分支和測試策略確保問題都被提前解決。
似曾相識
此時,似乎有些熟悉的感覺。在我以前從業(yè)經(jīng)驗也有過類似的經(jīng)驗,但從沒稱為微服務(wù),盡管并不完全符合微服務(wù)的規(guī)則,但是的確解決了問題并帶來類似的好處。
5 個人的小團隊帶領(lǐng) 200 人的開發(fā)者。這是一個巨大的 C# 應(yīng)用,大概 5% 的工作通過單體式共享,其他的共享兩節(jié)點服務(wù)。
我們也不喜歡單體式,工作起來很慢,編譯,測試,架構(gòu)改變的很快經(jīng)??吹讲煌耐鲁霈F(xiàn)。
有時候因為一個從未聽過的同事辭職了從而延遲了整個項目,技術(shù)更新因為要和整個公司同步需要幾個月,Pull 申請需要整個系統(tǒng)批復(fù)需又要幾個星期。
同時,有兩個服務(wù)規(guī)模很小,我們可以很好地控制、部署、架構(gòu)它。一旦有性能問題,會懷疑實例數(shù)量直到解決底層問題,很少麻煩其他團隊。
我們團隊主導(dǎo)了前后端開發(fā)語言的選擇??傊覀兒軐W⒂谝粋€很窄的業(yè)務(wù)邏輯,每個人都成為了專家。
技術(shù)之外
越了解微服務(wù),越發(fā)現(xiàn)其不太適合我們。我們是不是太為了技術(shù)而技術(shù)了?
還有很多其他問題沒考慮:
- 有沒有專注業(yè)務(wù)邏輯的團隊?
- 是否能夠清晰劃分域和微服務(wù)?
- 工作在所有團隊之間是否平衡?
- 團隊負(fù)載是否均衡?
- 單體式困難是否被分解到其他工作中?
復(fù)盤
遷移到微服務(wù)是個大爆炸,大家都停下功能開始想如何分解單體引用,即使前提還不具備。
后來證明這不是個好方法,有可能還是個倒退。首先創(chuàng)建所有微服務(wù),然后設(shè)置架構(gòu)并忽略了團隊的架構(gòu)。
假如我們開始時考慮團隊和業(yè)務(wù)邏輯結(jié)合,等架構(gòu)成熟,并等微服務(wù)自然顯現(xiàn),會更好。而且新的業(yè)務(wù)需求出現(xiàn),可以直接被放在一個新服務(wù)中。
強制上微服務(wù),也意味著需要選擇每個微服務(wù)的大小。一些文章建議每個微服務(wù)至少按照一個團隊的支撐設(shè)計。其他的則建議越小越好,一旦需要更改,很短時間內(nèi)就可以完成。
領(lǐng)導(dǎo)層決定按照工作域劃分,如果需要再細分。這個決定導(dǎo)致上面出現(xiàn)的問題。后來復(fù)盤時發(fā)現(xiàn)如果等待微服務(wù)自然出現(xiàn),其大小其實最合適。
取消
隨著預(yù)定時間一天天臨近,我們團隊持續(xù)發(fā)現(xiàn)越來越多的問題。上線四天后,仍然看不到預(yù)期的效果。于是召開了一次會議,最終取消了微服務(wù)計劃。
取代行動
微服務(wù)的熱度消散,也就意味著我們沒有做好必要的調(diào)研。拋棄了微服務(wù)后,我們也有精力去調(diào)研其他方案。
最后,我們決定在單體應(yīng)用內(nèi)部劃分成不同項目,更好地劃分了架構(gòu)和耦合關(guān)系。
另外這種架構(gòu)使得域模型更加清晰,使得未來考慮微服務(wù)更容易。
結(jié)論
領(lǐng)導(dǎo)層上微服務(wù)的倉促決定并沒考慮清楚挑戰(zhàn)和目前狀態(tài)。評估后,發(fā)現(xiàn)微服務(wù)并不適合我們,需要更多的妥協(xié)。
這些折衷方案把微服務(wù)帶來的好處都抵消掉了。微服務(wù)并不考慮非技術(shù)因素,例如團隊結(jié)構(gòu)和工作量。
幾個月調(diào)研后,我們拋棄了微服務(wù)嘗試,決定對已有的單體式應(yīng)用做一些更適合的微調(diào)。