如何構建微服務架構

貓飯先生發表於2017-10-10

【編者的話】“微服務”的概念興起於四五年前,近幾年尤其火熱,各大廠都在進行微服務化改造和微服務建設。最近一年來我們也參與了微服務化的改造大軍,這裡寫下一些做微服務系統設計和開發時的切身感受。

【3 天燒腦式基於Docker的CI/CD實戰訓練營 | 北京站】本次培訓圍繞基於Docker的CI/CD實戰展開,具體內容包括:持續整合與持續交付(CI/CD)概覽;持續整合系統介紹;客戶端與服務端的 CI/CD 實踐;開發流程中引入 CI、CD;Gitlab 和 CI、CD 工具;Gitlab CI、Drone 的使用以及實踐經驗分享等。

微服務架構

說起微服務,不得不提那篇經典的文章,來自Martin Flower的《Microservices》,建議多讀幾遍。Martin Flower是敏捷開發方法創始人之一,《重構》《企業應用架構模式》作者,ThoughtWorks公司的首席科學家。微服務雖然不是在這篇文章中首次提出,但是它將發展多年的微服務架構進行了重要性總結,推動了微服務的流行。

Martin在文章中從多個方面詳細闡述了微服務的概念,首先作者對比單體應用的架構,一個單體應用處理請求的所有邏輯都執行在一個單獨的程式中,這種情況下,你可以在筆記本上開發、測試、部署都很簡單,還可以通過負載均衡進行橫向擴充套件,最後交付給運維團隊。單體應用非常成功,但是越來越多的人感覺不妥,尤其是在雲中部署的時候,任何微小的變更都要整體重新構建和部署,擴充套件的時候也需要整體擴充套件,不能進行部分擴充套件。這時候微服務架構風格就出現了,它提出將應用程式構建為一套服務,每個服務都可以獨立部署和擴充套件,執行在獨立的程式中,服務之間通過RPC呼叫進行通訊。微服務的應用致力於鬆耦合和高內聚:採用單獨的業務邏輯封裝,接受請求、處理業務邏輯、返回響應,而且喜歡簡單的REST風格,而不喜歡複雜的協議,最終實現敏捷開發。

如何構建微服務架構
單體架構和微服務架構圖

微服務不是什麼框架,也不是什麼系統,只是一種架構風格。我們所使用的DSF(華為)、DUBBO(阿里)、Spring Clould(Pivotal)等框架,在介紹中都稱是分散式服務框架。這些分散式服務框架都是微服務架構必不可少的基礎能力,微服務一定是分散式的。分散式服務的概念比較模糊,只是解決了網站的高併發問題,很多細節問題是微服務幫其明確的。微服務更加強調敏捷和健壯,強調服務的粒度,一個服務只需完成一個單一的、獨立的功能,多個微服務組合完成相對複雜的業務系統,以滿足需求。而且微服務注重藉助於各種中介軟體進行業務解耦和提高效能,以及提高服務的容錯性。

從上面的介紹中我們可以看出,微服務架構有很多優點。例如,服務的拆分將複雜問題簡單化;每個服務由專門的團隊開發,開發者可以自由選擇實現技術,提供API服務;每個微服務都可獨立部署,加快了部署速度;每個服務可獨立擴充套件以滿足需求。微服務不是免費的午餐,微服務架構也有缺點。微服務概念強調了服務的大小,但是服務變小不是最終目的,微服務的目的是有效地拆分應用,實現敏捷開發和部署;微服務應用都是分散式系統,需要通過RPC程式間通訊完成服務呼叫,這樣大大增加了系統的複雜性,因此必須寫程式碼處理由於網路或者服務不可用等導致的呼叫失敗問題,雖然一般框架都支援相關配置,但是在這種情況下微服務顯得相對複雜些;在微服務架構的應用中,建議不同的服務使用不同的資料庫,這種情況下,一個交易一般會呼叫多個服務,同時需要修改多個資料庫,由於CAP理論和其它的一些因素,並不能實時地保證資料的一致性,因此不得不使用最終一致性的方法,從而對開發者提出了更高的要求和挑戰;測試一個微服務架構的應用也是很複雜的任務,首先需要啟動和它相關的所有服務(至少需要這些服務的stubs)。最後還是要強調一下,不要低估了微服務架構帶來的複雜性,下面介紹一下我們如何應對這樣的複雜性。

微服務架構給我們帶來方便的同時,也引入一定的系統複雜性,最近幾年隨著基礎設施自動化技術的發展,比如雲平臺、容器技術,再加上自動化測試與持續整合,減少了構建、釋出、運維微服務的複雜性。因此我們的微服務團隊應該依賴基礎設施自動化技術構建軟體,下圖說明這種構建的流程:

如何構建微服務架構
基本的構建流程

在構建微服務軟體時使用的分散式服務框架也擁有很多特性,這些特性提供了很多複雜問題的解決方案,比如非同步呼叫、超時失敗策略、故障隔離、健康檢查、流量控制以及自定義路由等,這些特性大大減輕了開發者處理邏輯的負擔,還有一些高效能、高可用的保障都增加了我們構建微服務的信心。而且經過多年的微服務實踐者的摸索,也總結出了很多輔助運維繫統,比如統一日誌收集(ELK)、服務管理、服務監控和服務治理等,大大減輕了運維的工作,而且能讓我們對複雜的微服務系統做到心中有數。

總之,微服務架構入坑容易,坑了呆得舒服難。各種基礎設施和配套系統必須跟得上,還得有一個具有微服務特性的團隊,才能真正駕馭得了微服務。

兩個值得深入的話題:

  1. 微服務與持續整合
  2. 微服務與測試

微服務團隊

上一節中說到更適合構建微服務應用的微服務團隊,什麼時微服務團隊?說一個比較現實的問題,當我們做服務拆分的時候,通常管理都會集中在技術層面,涉及到UI團隊、服務端業務邏輯團隊、資料庫團隊、測試團隊和運維團隊。當採用這種標準對團隊進行劃分時,即使時小小的變更都將導致跨團隊專案協作,從而消耗時間和預算審批。這就是 Conway's Law :

設計一個系統的任何組織(廣義上)都會產生這樣一種設計,其結構是組織交流結構的複製。 ——Melvyn Conway, 1967

Melvyn Conway 的意思是設計一個系統的團隊結構將決定了一個軟體系統的結構,將人員劃分為 UI 團隊,中介軟體團隊,DBA 團隊,那麼相應地,軟體系統也就會自然地被劃分為 UI 介面,中介軟體系統,資料庫。而一個高效的微服務團隊會針對這種情況進行改善,微服務團隊需要是一個全棧的團隊,跨職能的團隊,應該包含整個專案週期的所有技能,前後端開發、UI設計、測試、構建部署、上線與運維都是必須技能。

微服務團隊除了熟練的業務邏輯開發之外,還需要有DevOps能力、服務的快速構建、良好的團隊文化:

  • 首先DevOps能力是保證持續交付和應對複雜運維問題的動力之源,運維人員不懂開發人員的服務設計和呼叫流程,開發人員不懂產品環境和整體配置結構,開發和運維的融合才能更好的應對微服務架構。正如下面這句話所說的,“你構建,你運維”。

You build it, you own it. – Amazon CTO

因此,需要打造DevOps文化,將運維作為需求提前注入到開發流程中。

如何構建微服務架構
DevOps流程

  • 其次保持服務的持續演進,使服務能夠快速、低成本地被拆分和合並,以快速響應業務的變化。

  • 同時要保持團隊和架構的對齊,微服務看似是技術層面的變革,但它對團隊結構和組織文化有很強的要求和影響,識別和構建匹配架構的團隊是解決問題的加速器。微服務團隊的另一個關鍵點是持續改進、持續學習和反饋,只有保持這樣一個文化氛圍,微服務架構才能持續發展下去,保持新鮮的生命力,進而實現我們的初衷。

專業的微服務團隊,為微服務保駕護航。

專案的微服務設計實踐

我們在各種基礎設施不健全的情況下,匆忙進入了微服務改造的深淵。原來的單體應用按照功能拆分成了下面的結構:

如何構建微服務架構
微服務下的系統分層

拆分之後,模組突然變多了。將內部業務邏輯和原子功能拆分出來,實現了部分功能的複用,並且對外的介面按型別區分到不同的模組中去了,方便了使用,但是增加了管理的難度。

從上面我們對微服務架構的瞭解,一個微服務系統會有大量的服務組成,服務之間的層次關係依然是存在的,但是呼叫順序上的要求會有所降低,只要滿足嚴格的上層呼叫下層即可,在某些簡單的業務功能上允許跨層呼叫。

如何構建微服務架構
架構對比圖

上圖是三種架構(集中式、分散式、微服務架構)的形象化展示。可以看到微服務的一個狀態,乍看起來有些混亂,如果還按照以前的管理方式維護專案的話,工作量會成指數級增長,人力所無法勝任的工作。

這時候就需要依賴一些分散式服務框架,並建立一些輔助運維繫統:

  1. 首先服務之間遠端呼叫,服務的註冊和發現機制必須有好的分散式服務框架(類似dubbo)支援,方便做服務之間的呼叫配置管理。
  2. 部署的服務程式增加以後,對應的日誌檔案也會增多,這時再使用ssh到伺服器上檢視日誌,這個工作量也是可想而知的。我們對業務日誌做了規範化約束,然後使用ELK技術棧搭建了日誌集中化管理系統。
  3. 為了能夠對線上的所有服務有個全域性的把控,我們根據註冊中心的資料搭建了服務的管理系統,展示各個維度的統計資料,例如,每個主機上多少個應用,每個應用提供了多少個服務,同時又消費了多少個服務等等。還有一些針對服務的約束規則的告警資訊,例如某個應用消費的服務,卻沒有服務提供者等。還有一個整體的應用之間的依賴關係圖。
  4. 為了優化系統結構和排查問題,搭建了服務的監控系統,這個是依賴分散式服務框架列印的預統計日誌的,通過這些日誌分析得到一個整體的服務監控功能,例如每個服務的響應時間、錯誤率等。還有呼叫鏈跟蹤功能,排查問題時使用它來跟蹤請求資訊。
  5. 服務治理功能用來及時地對線上專案做一些調整,例如限流、降級、負載均衡、超時、路由、隔離容錯等。

有了以上這些系統輔助,我們的微服務化改造之路平坦了許多。

在進行微服務化改造的時候,介面的設計上遇到了一個問題,服務之間的呼叫是通過私有協議進行的RPC呼叫,這種呼叫中如何描述服務響應成功之外的其他異常情況?

  1. 採用Exception類,定義各種異常類來描述異常情況。
  2. 採用錯誤碼+錯誤描述。

其中方案1採用的Exception類,是我們平時java中定義程式內部介面最常見的方法,這種方法的優點是,能夠使呼叫介面的一方的程式碼更簡潔,通過try...catch來處理,如果不需要處理的話,在方法上宣告直接向上丟擲即可。缺點是跨語言開發解析難度大增,而且日誌中異常堆疊很多。方案2採用錯誤碼的形式恰恰相反,缺點就是每次呼叫都要判讀是否呼叫成功,增加程式碼中的if語句。

針對這個問題,希望各位實踐分散式服務或者微服務的大神給出你們的實踐方案,瘋狂討論起來。

結束語

上述三節中講述了我們在構建微服務的經歷和一些感想,給其他準備實施微服務和正在實施微服務的團隊以參考,寫這篇文章的另一個目的就是希望能起到拋磚引玉的作用,希望能和其他團隊進一步交流。

如何構建微服務架構
搞微服務

原文連結:如何構建微服務架構(作者:rabbitGYK)

相關文章