微服務,夠了

資深架構師Adam Drake在他的博客上分享了他對微服務的看法,他從自己的經驗出發,結合Martin Fowler對微服務的見解,幫助想要採用微服務的公司重新審視微服務。以下內容已獲得作者翻譯授權,查看英文原文 Enough with the microservices

簡介

關於微服務的優勢和劣勢已經有過太多的討論,不過我仍然看到很多成長型初創公司對它進行着盲目崇拜。冒着“重複發明輪子”的風險(Martin Fowler已經寫過“Microservice Premium”的文章),我想把我的一些想法寫下來,在必要的時候可以發給客戶,也希望能夠幫助人們避免犯下我之前見過的那些錯誤。在進行架構或技術選型時,將網絡上找到的一些所謂的最佳實踐文章作爲指南,一旦做出了錯誤的決定,就要付出慘重的代價。如果能夠幫助哪怕一個公司避免犯下這種錯誤,那麼寫這篇文章都是值得的。

如今微服務是個熱門技術,微服務架構一直以來都存在(面向服務架構也算是吧?),但對於我所見過的大部分公司來說,微服務不僅浪費了他們的時間,分散了他們的注意力,而且讓事情變得更糟糕


Russ Miles在他的《讓微服務失效的八種方式》這篇文章中表達了他的首要觀點,而在我看來,這些場景卻到處可見。成長型初創公司總是想模仿那些大公司的最佳實踐,用它們來彌補自身的不足。但是,最佳實踐是要視情況而定的。有些東西對於Facebook來說是最佳實踐,但對於只有不到百人的初創公司來說,它們就不一定也是最佳實踐。這聽起來似乎很奇怪,因爲大部分關於微服務的文章都會肯定微服務的各種好處,比如解耦系統、更好的伸縮性、消除開發團隊之間的依賴,等等。如果你的公司有Uber、Airbnb、Facebook或Twitter那樣的規模,那麼就沒有什麼問題。我曾經幫助一些大型組織轉型到微服務架構,包括搭建消息系統和採用一些能夠提升伸縮性的技術。不過,對於成長型初創公司來說,很少需要這些技術和服務。

如果你的公司比那些大公司小一些,在一定程度上你仍然能夠從微服務架構中獲益。但是,對於成長型初創公司來說,大規模地遷移到微服務是一種過錯,而且對技術人來說是不公平的。

爲什麼選擇微服務?

一般來說,成長型初創公司採用微服務架構最主要的目的爲了減少或消除開發團隊之間的依賴,或者提升系統處理大流量負載的能力(比如伸縮性)。開發人員經常抱怨的問題和常見的症狀包括合併衝突、由未完整實現的功能引起的部署錯誤以及伸縮性問題。接下來讓我們逐個說明這些問題。

依賴

在初創公司的早期階段,開發團隊規模不大,使用的技術也很簡單。人們在一起工作,不會出現混亂,要實現一些功能也比較快。一切看起來都很美好。

隨着公司的不斷髮展,開發團隊也在壯大,代碼庫也在增長,然後就出現了多個團隊在同一個代碼庫上工作的情況。這些團隊的大部分成員都是公司早期的員工。因爲初創公司的早期員工一般都是初級開發人員,他們並沒有意識到一個問題,那就是在團隊規模增長和代碼庫增長的同時,溝通效率也需要隨之提升。對於缺乏經驗的技術人員來說,他們傾向於通過技術問題來解決人的問題,並希望通過微服務來減少開發團隊之間的依賴和耦合。

實際上,他們真正需要做的是通過有效的溝通來解決人的問題。當一個初創公司有多個開發團隊時,團隊之間需要協調,團隊成員需要知道每個人都在做什麼,他們需要協作。在這樣規模的企業裏,軟件開發其實具備了社交的性質。如果團隊之間缺乏溝通或者缺乏信息分享,不管用不用微服務,一樣存在依賴問題,而就算使用了微服務,也仍然存在負面的技術問題。

將代碼模塊化作爲解決這個問題的技術方案,確實能夠緩解軟件開發固有的團隊依賴問題,但團隊間的溝通仍然要隨着團隊規模的增長而不斷改進。

不要混淆了解耦分佈式二者的含義。由模塊和接口組成的單體可以幫助你達到解耦的目的,而且你也應該這麼做。你沒有必要把應用程序拆分成分佈式的多個獨立服務,在模塊間定義清晰的接口同樣能達到解耦的目的。

部分功能實現

微服務裏需要用到功能標誌(feature flag),微服務開發人員需要熟悉這種技術。特別是在進行快速開發(下面會深入討論)的時候,你可能需要部署一些功能,這些功能在某些平臺上還沒有實現,或者前端已經完全實現,但後端還沒有。隨着公司的發展,部署和運維繫統變得越來越自動化和複雜,功能標誌也變得越來越重要。

水平伸縮

通過部署同一個微服務的多個實例來獲得伸縮性,這是微服務的優點之一。不過,大多數過早採用微服務的公司在這些微服務背後使用了同一個存儲系統。也就是說,這些服務具備了伸縮性,但整個應用並不具備伸縮性。如果你正打算使用這樣的伸縮方式,那爲什麼不直接在負載均衡器後面部署多個單體實例呢?你可以用更簡單的方式達到相同的目的。再者,水平伸縮應該被作爲殺手鐗來使用。你首先要關注的應該是如何提升應用程序的性能。一些簡單的優化常常能帶來數百倍的性能提升,這裏也包括如何正確地使用其他服務。例如,我在一篇博文裏提到的Redis性能診斷

我們爲微服務做好準備了嗎?

在討論架構選型時,人們經常會忽略這個問題,但其實卻是最重要的。高級技術人員在瞭解了開發人員或業務人員的抱怨或痛點之後,在網上找尋找解決方案,他們總是宣稱能解決這些問題。但在這些信誓旦旦的觀點背後,有很多需要注意的地方。微服務有利也有弊。如果你的企業足夠成熟,並且具有一定的技術積累,那麼採用微服務所面臨的挑戰會小很多,並且能夠帶來更多正面好處。那麼怎樣纔算已經爲微服務做好準備了呢?Martin Fowler在多年前表達了他對微服務先決條件的看法,但是從我的經驗來看,大多數成長型初創公司完全忽略了他的觀點。Martin的觀點是一個很好的切入點,讓我們來逐個說明。

我敢說,大部分成長型初創公司幾乎連一個先決條件都無法滿足,更不用說滿足所有的條件了。如果你的技術團隊不具備快速配置、部署和監控能力,那麼在遷移到微服務前必須先獲得這些能力。接下來讓我們更詳細地討論這些先決條件。

快速配置

如果你的開發團隊裏只有少數幾個人可以配置新服務、虛擬環境或其他配套設施,那說明你們還沒有爲微服務做好準備。你的每個團隊裏都應該要有幾個這樣的人,他們具備了配置基礎設施和部署服務的能力,而且不需要求助於外部。要注意,光是有一個DevOps團隊並不意味着你在實施DevOps。開發人員應該參與管理與應用程序相關的組件,包括基礎設施。

類似的,如果你沒有靈活的基礎設施(易於伸縮並且可以由團隊裏的不同人員來管理)來支撐當前的架構,那麼在遷移到微服務前必須先解決這個問題。你當然可以在裸機上運行微服務,以更低的成本獲得出衆的性能,但在服務的運維和部署方面也必須具備靈活性。

基本的監控

如果你不曾對你的單體應用進行過性能監控,那麼在遷移到微服務時,你的日子會很難過。你需要熟悉系統級別的度量指標(比如CPU和內存)、應用級別的度量指標(比如端點的請求延遲或端點的錯誤)和業務級別的度量指標(比如每秒事務數或每秒收益),這樣纔可以更好地理解系統的性能。在性能方面,微服務生態系統比單體系統要複雜得多,就更不用提診斷問題的複雜性了。你可以搭建一個監控系統(如Prometheus),在將單體應用拆分成微服務之前對應用做一些增強,以便進行監控。

快速部署

如果你的單體系統沒有一個很好的持續集成流程和部署系統,那麼要集成和部署好你的微服務幾乎是件不可能的事。想象一下這樣的場景:10個團隊和100個服務,它們都需要進行手動測試和部署,然後再將這些工作與測試和部署一個單體所需要的工作進行對比。100個服務會出現多少種問題?而單體系統呢?這些先決條件很好地說明了微服務的複雜性。

Phil Calcado在Fowler的先決條件清單裏添加了一些東西,不過我認爲它們更像是重要的擴展,而不是真正的先決條件。

如果我們具備了這些先決條件呢?

就算具備了這些條件,仍然需要注意微服務的負面因素,確保微服務能夠爲你的業務帶來真正的價值。事實上,很多技術人員對微服務中存在的分佈式計算謬論視而不見,但爲了確保能夠成功,這些問題是必須要考慮到的。對於大部分成長型初創公司來說,基於各種原因,他們應該避免使用微服務。

運營成本的增加

快速部署這一先決條件已經涵蓋了一部分成本,除此之外,對微服務進行容器化(可能使用Docker)和使用容器編排系統(比如Kubernetes)也需要耗費很多成本。Docker和Kubernetes都是很優秀的技術,但是對於大部分成長型初創公司來說,它們都是一種負擔。我見過初創公司使用rsync作爲部署和編排工具,我也見過很多的初創公司陷入運維工具的複雜性泥潭裏,他們因此浪費了很多時間,而這些時間本來可以用於爲用戶開發更多的功能。

你的應用會被拖慢

如果你的單體系統裏包含了多個模塊,並且在模塊間定義了良好的API,那麼API之間的交互就幾乎沒有什麼額外開銷。但對於微服務來說就不是這麼一回事了,因爲它們一般運行在不同的機器上,它們之間需要通過網絡進行交互。這樣會在一定程度上拖慢整個系統。如果一個請求需要多個服務進行同步交互,那麼情況會變得更加糟糕。我曾經工作過的一個公司,他們需要調用將近10個服務才能處理完某些請求。處理請求的每一個步驟都需要額外的網絡開銷和延遲,但實際上,他們可以把這些服務放在單個軟件包裏,按照不同的模塊來區分,或者把它們設計成異步的。這樣可以爲他們節省大量的基礎設施成本。

本地開發變得更加困難

如果你有一個單體應用,後端只有一個數據庫,那麼在開發過程中,在本地運行這個應用是很容易的。如果你有100個服務,並使用了多個數據存儲系統,而且它們之間互相依賴,那麼本地開發就會變成一個噩夢。即使是Docker也無法把你從這種複雜性泥潭中拯救出來。雖然事情原本可以簡單一些,不過仍然需要處理依賴問題。理論上說,微服務不存在這些問題,因爲微服務被認爲是相互獨立的。不過,對於成長型初創公司來說,就不是這麼一回事了。技術人員一般需要在本地運行所有(或者幾乎所有)的服務才能進行新功能的開發和測試。這種複雜性是對資源的巨大浪費。

難以伸縮

對單體系統進行伸縮的最簡單方式是在負載均衡器後面部署單體系統的多個實例。在流量增長的情況下,這是一種非常簡單的伸縮方式,而且從運維角度來講,它的複雜性是最低的。你的系統在編排平臺(如Elastic Beanstalk)上運行的時間越長越好,你和你的團隊就可以集中精力開發客戶需要的東西,而不是忙於解決部署管道問題。使用合適的CI/CD系統可以緩解這個問題,但在微服務生態系統裏,事情要複雜得多,而且這些複雜性所造成的麻煩已經超過了它們所能帶來的好處。

然後呢?

如果你剛好身處一個成長型初創公司裏,需要對架構做一些調整,而微服務似乎不能解決你的問題,這個時候應該怎麼辦?

Fowler提出的先決條件可以說是技術領域的能力成熟度模型,Fowler在他的文章裏對成熟度模型進行過介紹。如果這種成熟度模型對於公司來說是說得通的,那麼我們可以按照Fowler提出的先決條件,並使用其他的一些中間步驟爲向微服務遷移做好準備。下面的內容引用自Fowler的文章。

關鍵是你要認識到,成熟度模型的評估結果並不代表你的當前水平,它們只是在告訴你需要做哪些工作才能朝着改進的目標前進。你當前的水平只是一種中間工作,用於確定下一步該獲得什麼樣的技能。

那麼,我們該做出怎樣的改進,以及如何達成這些目標?我們需要經過一些簡單的步驟,其中前面兩步就可以解決很多在向微服務遷移過程中會出現的問題,而且不會帶來相關的複雜性。

  1. 清理應用程序。確保應用程序具有良好的自動化測試套件,並使用了最新版本的軟件包、框架和編程語言。
  2. 重構應用程序,把它拆分成多個模塊,爲模塊定義清晰的API。不要讓外部代碼直接觸及模塊內部,所有的交互都應該通過模塊提供的API來進行。
  3. 從應用程序中選擇一個模塊,並把它拆分成獨立的應用程序,部署在相同的主機上。你可以從中獲得一些好處,而不會帶來太多的運維麻煩。不過,你仍然需要解決這兩個應用之間的交互問題,雖然它們都部署在同一個主機上。不過你可以無視微服務架構裏固有的網絡分區問題和分佈式系統的可用性問題。
  4. 把獨立出來的模塊移動到不同的主機上。現在,你需要處理跨網絡交互問題,不過這樣可以讓這兩個系統之間的耦合降得更低。
  5. 如果有可能,可以重構數據存儲系統,讓另一個主機上的模塊負責自己的數據存儲。

在我所見過的公司裏,如果他們能夠完成前面兩個步驟就算萬事大吉了。如果他們能夠完成前面兩個步驟,那麼剩下的步驟一般不會像他們最初想象的那麼重要了。如果你決定在這個過程的某個點上停下來,而系統仍然具有可維護性和比剛開始時更好的狀態,那麼就再好不過了。

結尾

我不能說這些想法都是獨一無二的,也不能說是我所獨有的。我只是從其他遭遇了相同問題的人那裏收集想法,並連同觀察到的現象在這裏作了一次總結。還有其他很多比我更有經驗的人也寫過這方面的文章,他們剖析地更加深入,比如Sander Mak寫的有關模塊和微服務的文章。不管怎樣,對於正在考慮對他們的未來架構做出調整的公司來說,這些經驗都是非常重要的。認真地思考每一個問題,確保微服務對你們的組織來說是一個正確的選擇。

最起碼在完成了上述的前面兩個步驟之後,再慎重考慮一下微服務對於你的組織來說是否是正確的方向。你之前的很多問題可能會迎刃而解。


感謝郭蕾對本文的審校。

給InfoQ中文站投稿或者參與內容翻譯工作,請郵件至[email protected]。也歡迎大家通過新浪微博(@InfoQ@丁曉昀),微信(微信號:InfoQChina)關注我們。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章