夠了,不要一上來就把微服務說的神乎其神
作者:Adam Drake
編輯:薛命燈
資深架構師 Adam Drake 在他的部落格上分享了他對微服務的看法,他從自己的經驗出發,結合 Martin Fowler 對微服務的見解,幫助想要採用微服務的公司重新審視微服務。以下內容已獲得作者翻譯授權。
關於微服務的優勢和劣勢已經有過太多的討論,不過我仍然看到很多成長型初創公司對它進行著“盲目崇拜”。冒著“重複發明輪子”的風險(Martin Fowler 已經寫過“Microservice Premium”的文章),我想把我的一些想法寫下來,在必要的時候可以發給客戶,也希望能夠幫助人們避免犯下我之前見過的那些錯誤。在進行架構或技術選型時,將網路上找到的一些所謂的最佳實踐文章作為指南,一旦做出了錯誤的決定,就要付出慘重的代價。如果能夠幫助哪怕一個公司避免犯下這種錯誤,那麼寫這篇文章都是值得的。
如今微服務是個熱門技術,微服務架構一直以來都存在(面向服務架構也算是吧?),但對於我所見過的大部分公司來說,微服務不僅浪費了他們的時間,分散了他們的注意力,而且讓事情變得更糟糕。
這聽起來似乎很奇怪,因為大部分關於微服務的文章都會肯定微服務的各種好處,比如解耦系統、更好的伸縮性、移除開發團隊之間的依賴,等等。如果你的公司有 Uber、Airbnb、Facebook 或 Twitter 那樣的規模,那麼就不存在什麼問題。我曾經幫助一些大型組織轉型到微服務架構,包括搭建訊息系統和採用一些能夠提升伸縮性的技術。不過,對於成長型初創公司來說,很少需要這些技術和微服務。
Russ Miles 在他的《讓微服務失效的八種方式》這篇文章中表達了他的首要觀點,而在我看來,這些場景卻到處可見。成長型初創公司總是想模仿那些大公司的最佳實踐,用它們來彌補自身的不足。但是,最佳實踐是要視情況而定的。有些東西對於 Facebook 來說是最佳實踐,但對於只有不到百人的初創公司來說,它們就不一定也是最佳實踐。
如果你的公司比那些大公司小一些,你仍然能夠在一定程度上從微服務架構中獲得好處。但是,對於成長型初創公司來說,大規模地遷移到微服務是一種過錯,而且對技術人來說是不公平的。
為什麼選擇微服務?
一般來說,成長型初創公司採用微服務架構最主要的目的是要減少或者消除開發團隊間的依賴,或者提升系統處理大流量負載的能力(比如伸縮性)。開發人員經常抱怨的問題和常見的症狀包括合併衝突、由未完整實現的功能引起的部署錯誤以及伸縮性問題。接下來讓我們逐個說明這些問題。
依賴
在初創公司的早期階段,開發團隊規模不大,使用的技術也很簡單。人們在一起工作,不會出現混亂,要實現一些功能也比較快。一切看起來都很美好。
隨著公司的不斷髮展,開發團隊也在壯大,程式碼庫也在增長,然後就出現了多個團隊在同一個程式碼庫上工作的情況。這些團隊的大部分成員都是公司早期的員工。因為初創公司的早期員工一般都是初級開發人員,他們並沒有意識到一個問題,那就是在團隊規模增長和程式碼庫增長的同時,溝通成本也會隨之提升。對於缺乏經驗的技術人員來說,他們傾向於通過技術問題來解決人的問題,並希望通過微服務來減少開發團隊之間的依賴和耦合。
實際上,他們真正需要做的是通過有效的溝通來解決人的問題。當一個初創公司有多個開發團隊時,團隊之間需要協調,團隊成員需要知道每個人都在做什麼,他們需要協作。在這樣規模的企業裡,軟體開發其實具有了社交的性質。如果團隊之間缺乏溝通或者缺乏資訊分享,不管用不用微服務,一樣會存在依賴問題,而就算使用了微服務,也仍然存在負面的技術問題。
將程式碼模組化作為解決這個問題的技術方案,確實能夠緩解軟體開發固有的團隊依賴問題,但團隊間的溝通仍然要隨著團隊規模的增長而不斷改進。
切記不要混淆了解耦和分散式二者的含義。由模組和介面組成的單體可以幫助你達到解耦的目的,而且你也應該這麼做。你沒有必要把應用程式拆分成分散式的多個獨立服務,在模組間定義清晰的介面也能達到解耦的目的。
部分功能實現
微服務裡需要用到功能標誌(feature flag),微服務開發人員需要熟悉這種技術。特別是在進行快速開發(下面會深入討論)的時候,你可能需要部署一些功能,這些功能在某些平臺上還沒有實現,或者前端已經完全實現,但後端還沒有,等等。隨著公司的發展,部署和運維繫統變得越來越自動化和複雜,功能標誌變得越來越重要。
水平伸縮
通過部署同一個服務的多個例項來獲得系統的伸縮性,這是微服務的優點之一。不過,大多數過早採用微服務的公司卻在這些微服務背後使用了同一個儲存系統。
也就是說,這些服務具備了伸縮性,但整個應用並不具備伸縮性。如果你正打算使用這樣的伸縮方式,那為什麼不直接在負載均衡器後面部署多個單體例項呢?你可以以更簡單的方式達到相同的目的。再者,水平伸縮應該被作為殺手鐗來使用。你首先要關注的應該是如何提升應用程式的效能。一些簡單的優化常常能帶來數百倍的效能提升,這裡也包括如何正確地使用其他服務。例如,我在一篇博文裡提到的 Redis 效能診斷(文末有連結)。
我們為微服務做好準備了嗎?
在討論架構選型時,人們經常會忽略這個問題,但其他卻是最重要的。高階技術人員在瞭解了開發人員或業務人員的抱怨或痛點之後,開始在網上找尋找解決方案,他們總是宣稱能解決這些問題。但在這些信誓旦旦的觀點背後,有很多需要注意的地方。微服務有利也有弊。如果你的企業足夠成熟,並且具有一定的技術積累,那麼採用微服務所面臨的挑戰會小很多,並且能夠帶來更多正面好處。那麼怎樣才算已經為微服務做好準備了呢?Martin Fowler 在多年前表達了他對微服務先決條件的看法,但是從我的經驗來看,大多數成長型初創公司完全忽略了他的觀點。Martin 的觀點是一個很好的切入點,讓我們來逐個說明。
我敢說,大部分成長型初創公司幾乎連一個先決條件都無法滿足,更不用說滿足所有的條件了。如果你的技術團隊不具備快速配置、部署和監控能力,那麼在遷移到微服務前必須先獲得這些能力。接下來讓我們更詳細地討論這些先決條件。
1. 快速配置
如果你的開發團隊裡只有少數幾個人可以配置新服務、虛擬環境或其它配套設施,那說明你們還沒有為微服務做好準備。你的每個團隊裡都應該要有幾個這樣的人,他們具備了配置基礎設施和部署服務的能力,而且不需要求助於外部。要注意,光是有一個 DevOps 團隊並不意味著你在實施 DevOps,開發人員應該參與管理與應用程式相關的元件,包括基礎設施。
類似的,如果你沒有靈活的基礎設施(易於伸縮並且可以由團隊裡的不同人員來管理)來支撐當前的架構,那麼在遷移到微服務前必須先解決這個問題。你當然可以在裸機上執行微服務,以更低的成本獲得出眾的效能,但在服務的運維和部署方面也必須具備靈活性。
2. 基本的監控
如果你不曾對你的單體應用進行過效能監控,那麼在遷移到微服務時,你的日子會很難過。你需要熟悉系統級別的度量指標(比如 CPU 和記憶體)、應用級別的度量指標(比如端點的請求延遲或端點的錯誤)和業務級別的度量指標(比如每秒事務數或每秒收益),這樣才可以更好地理解系統的效能。在效能方面,微服務生態系統比單體系統要複雜得多,就更不用提診斷問題的複雜性了。你可以搭建一個監控系統(如 Prometheus),在將單體應用拆分成微服務之前對應用做一些增強,以便進行監控。
3. 快速部署
如果你的單體系統沒有一個很好的持續整合流程和部署系統,那麼要整合和部署好你的微服務幾乎是件不可能的事。想象一下這樣的場景:10 個團隊和 100 個服務,它們都需要進行手動測試和部署,然後再將這些工作與測試和部署一個單體所需要的工作進行對比。100 個服務會出現多少種問題?而單體系統呢?這些先決條件很好地說明了微服務的複雜性。
Phil Calcado 在 Fowler 的先決條件清單裡添加了一些東西,不過我認為它們更像是重要的擴充套件,而不是真正的先決條件。
如果我們具備了這些先決條件呢?
就算具備了這些條件,仍然需要注意微服務的負面因素,確保微服務能夠為你的業務帶來真正的幫助。事實上,很多技術人員對微服務中存在的分散式計算謬論視而不見,但為了確保能夠成功,這些問題是必須要考慮到的。對於大部分成長型初創公司來說,基於各種原因,他們應該避免使用微服務。
1. 運營成本的增加
快速部署這一先決條件已經涵蓋了一部分成本,除此之外,對微服務進行容器化(可能使用 Docker)和使用容器編排系統(比如 Kubernetes)也需要耗費很多成本。Docker 和 Kubernetes 都是很優秀的技術,但是對於大部分成長型初創公司來說,它們都是一種負擔。我見過初創公司使用 rsync 作為部署和編排工具,我也見過很多的初創公司陷入運維工具的複雜性泥潭裡,他們因此浪費了很多時間,而這些時間本來可以用於為使用者開發更多的功能。
2. 你的應用會被拖慢
如果你的單體系統裡包含了多個模組,並且在模組間定義了良好的 API,那麼 API 之間的互動就幾乎沒有什麼額外開銷。但對於微服務來說就不是這麼一回事了,因為它們一般執行在不同的機器上,它們之間需要通過網路進行互動。這樣會在一定程度上拖慢整個系統。如果一個請求需要多個服務進行同步的互動,那麼情況會變得更加糟糕。我曾經工作過的一個公司,他們需要呼叫將近 10 個服務才能處理完某些請求。處理請求的每一個步驟都需要額外的網路開銷和延遲,但實際上,他們可以把這些服務放在單個軟體包裡,按照不同的模組來區分,或者把它們設計成非同步的。這樣可以為他們節省大量的基礎設施成本。
3. 本地開發變得更加困難
如果你有一個單體應用,後端只有一個數據庫,那麼在開發過程中,在本地執行這個應用是很容易的。如果你有 100 個服務,並使用了多個數據儲存系統,而且它們之間互相依賴,那麼本地開發就會變成一個噩夢。即使是 Docker 也無法把你從這種複雜性泥潭中拯救出來。雖然事情原本可以簡單一些,不過仍然需要處理依賴問題。理論上說,微服務不存在這些問題,因為微服務被認為是相互獨立的。不過,對於成長型初創公司來說,就不是這麼一回事了。技術人員一般需要在本地執行所有(或者幾乎所有)的服務才能進行新功能的開發和測試。這種複雜性是對資源的巨大浪費。
4. 難以伸縮
對單體系統進行伸縮的最簡單方式是在負載均衡器後面部署單體系統的多個例項。在流量增長的情況下,這是一種非常簡單的伸縮方式,而且從運維角度來講,它的複雜性是最低的。你的系統在編排平臺(如 Elastic Beanstalk)上執行的時間越長越好,你和你的團隊就可以集中精力構建客戶需要的東西,而不是忙於解決部署管道問題。使用合適的 CI/CD 系統可以緩解這個問題,但在微服務生態系統裡,事情要複雜得多,而且這些複雜性所造成的麻煩已經超過了它們所能帶來的好處。
然後呢?
如果你剛好處在一個成長型初創公司裡,需要對架構做一些調整,而微服務似乎不能解決你的問題,這個時候應該怎麼辦?
Fowler 提出的先決條件可以說是技術領域的能力成熟度模型,Fowler 在他的文章裡對成熟度模型進行過介紹。如果這種成熟度模型對於公司來說是說得通的,那麼我們可以按照 Fowler 提出的先決條件,並使用其他的一些中間步驟為向微服務遷移做好準備。下面的內容引用自 Fowler 的文章。
關鍵你要認識到,成熟度模型的評估結果並不代表你的當前水平,它們只是在告訴你需要做哪些工作才能朝著改進的目標前進。你當前的水平只是一種中間工作,用於確定下一步該獲得什麼樣的技能。
那麼,我們該做出怎樣的改進,以及如何達成這些目標?我們需要經過一些簡單的步驟,其中前面兩步就可以解決很多在向微服務遷移過程中會出現的問題,而且不會帶來相關的複雜性。
- 清理應用程式。確保應用程式具有良好的自動化測試套件,並使用了最新版本的軟體包、框架和程式語言。
- 重構應用程式,把它拆分成多個模組,為模組定義清晰的 API。不要讓外部程式碼直接觸及模組內部,所有的互動應該通過模組提供的 API 來進行。
- 從應用程式中選擇一個模組,並把它拆分成獨立的應用程式,部署在相同的主機上。你可以從中獲得一些好處,而不會帶來太多的運維麻煩。不過,你仍然需要解決這兩個應用之間的互動問題,雖然它們都部署在同一個主機上。不過你可以無視微服務架構裡固有的網路分割槽問題和分散式系統的可用性問題。
- 把獨立出來的模組移動到不同的主機上。現在,你需要處理跨網路互動問題,不過這樣可以讓這兩個系統之間的耦合降得更低。
- 如果有可能,可以重構資料儲存系統,讓另一個主機上的模組負責自己的資料儲存。
在我所見過的公司裡,如果他們能夠完成前面兩個步驟就算萬事大吉了。如果他們能夠完成前面兩個步驟,那麼剩下的步驟一般不會像他們最初想象的那麼重要了。如果你決定在這個過程的某個點上停下來,而系統仍然具有可維護性和比剛開始時更好的狀態,那麼就再好不過了。
我的總結
我不能說這些想法都是獨一無二的,也不能說是我所獨有的。我只是從其他遭遇了相同問題的人那裡收集想法,並連同觀察到的現象在這裡作了一次總結。還有其他很多比我更有經驗的人也寫過這方面的文章,他們剖析地更加深入,比如 Sander Mak 寫的有關模組和微服務的文章。不管怎樣,對於正在考慮對他們的未來架構做出調整的公司來說,這些經驗都是非常重要的。認真地思考每一個問題,確保微服務對你們的組織來說是一個正確的選擇。
最起碼在完成了上述的前面兩個步驟之後,再慎重考慮一下微服務對於你的組織來說是否是正確的方向。你之前的很多問題可能會迎刃而解。
文中部分內容超連結:
- http://www.russmiles.com/essais/8-ways-to-lose-at-microservices-adoption
- https://aadrake.com/posts/2017-05-15-redis-performance-triage-handbook.html
- https://en.wikipedia.org/wiki/Capability_Maturity_Model
- https://www.oreilly.com/ideas/modules-vs-microservices
文章來自微信公眾號:聊聊架構