最近幾年微服務很火,大家都在建設微服務,如果不懂點微服務相關的技術,都不好意思跟同行打招呼了。

圖檔來自 Pexels
我也見過身邊很多人在微服務踩過很多坑,我從 2016 年開始接觸微服務,有多家大型企業的微服務分布式系統的架構經驗,不過微服務和涉及的分布式計算非常的複雜,絕非是一篇文章就可以講清楚的。
本文隻是從最簡單的概念的基本使用帶你入門,如果後續還有興趣的話,可以查閱相關的文獻和技術書籍去深入學習。
本文的微服務分享以下三部分,總體大綱如下:
- 微服務的概念和原則(理論)
- Spring Cloud 如何低成本的實作微服務(實作)
- Spring Cloud 大型項目的架構方案(真實案例)
微服務的概念和原則
什麼是微服務?
簡單舉例:看軍事新聞的同學應該都知道,一艘航空母艦作戰能力雖然很強,但是弱點太明顯,就是防禦能力太差,單艘的航空母艦很少單獨行動,通常航空母艦戰鬥群才是主要軍事力量。
你可以把單艘航母了解為的單體應用(防禦差,機動性不好),把航母戰鬥群(排程複雜,維護費用高)了解為微服務。
簡單講特征就是:
- 單體應用:簡單,脆弱(某個子產品出問題,整個系統不可用),戰鬥力弱,維護成本低。
- 微服務架構:複雜,健壯(某個子產品出問題,不會影響系統整體的可用性),戰鬥力強,維護成本高。
單體作戰的應用(圖)
微服務戰鬥群(圖)
大部分的開發者經曆和開發過單體應用,無論是傳統的 SSM,還是現在的 Spring Boot/Rails,它們都是單體應用。
那麼長期陪伴我們的單體應用有什麼弊端?我們是面臨了什麼問題,導緻我們要抛棄單體應用轉向微服務架構?
個人總結主要問題如下:
- 部署成本高(無論是修改 1 行代碼,還是 10 行代碼,都要全量部署替換)。
- 改動影響大,風險高,測試成本高(不論代碼改動多小,成本都相同)。
- 因為成本高,風險高,是以導緻部署頻率低(無法滿足快速傳遞客戶需求)。
當然還有例如無法滿足快速擴容,彈性伸縮,無法适應雲環境特性等問題,但我們不一一詳談了,以上的問題,都是微服務架構要解決的問題,至于具體是怎麼解決的,我們先放到後面再聊。
曆代應用程式的架構變遷(圖)
解決什麼問題,又引入了什麼問題?
我們先看看微服務能帶給我們什麼?微服務架構的特點:
-
針對特定服務釋出,影響小,風險小,成本低
-
頻繁釋出版本,快速傳遞需求
-
低成本擴容,彈性伸縮,适應雲環境
我們知道一個樸素的理念,沒有任何事物是完美的,任何東西都有兩面性,有得必有失。
那麼在選擇微服務在解決了快速響應和彈性伸縮的問題同時,它又給我們帶來了什麼問題?
個人總結如下:
- 分布式系統的複雜性
- 部署,測試和監控的成本問題
- 分布式事務和 CAP 的相關問題
系統應用由原來的單體變成幾十到幾百個不同的工程,會所産生例如包括服務間的依賴,服務如何拆封,内部接口規範,資料傳遞等等問題。
尤其是服務拆分,需要團隊熟悉業務流程,懂得取舍,要保證拆分的粒度服務既符合“高内聚,低耦合”的基本原則,還要兼顧業務的發展以及公司的願景,要還要說服團隊成員為之努力,并且積極投入,在多方中間取得平衡。
對于分布式系統,部署,測試和監控都需要大量的中間件來支撐,而且中間件本身也要維護。
原先單體應用很簡單的事務問題 ,轉到分布式環境就變得很複雜,分布式事務是采用簡單的重試+補償機制,還是采用二階段送出協定等強一緻性方法來解決,就要取決對業務場景的熟悉加上反複的權衡了。
相同問題還包括對 CAP 模型的權衡,總之微服務對團隊整體的技術棧水準整體要求更高。
微服務有哪些應該遵循哪些原則?
古人雲:兵馬未動,糧草先行。建設微服務是需要建立長遠規劃,不是像寫 CMS 那樣建好資料庫表,然後就開始幹活,這樣十有八九是會失敗的。
我們要進行微服務改造前,架構師要提前做好規劃,我們把這裡分為三步,前期階段,設計階段,技術階段。
前期階段,大緻要做好如下事情:
- 和多方充分溝通,確定能符合客戶群組織的需求,并且得到認同。
- 和團隊溝通,讓隊友(開發/測試/運維)了解,并且積極投入。
- 和業務部門溝通,指定版本計劃和上線時間。
設計階段,參考 Sam Newman 的著作《微服務設計》,單微服務必須要滿足以下的條件,才符合微服務的基本要求:
- 标準的 REST 風格接口(基于 HTTP 和 JSON 格式)。
- 獨立部署,避免共享資料庫(避免因為資料庫而影響整個分布式系統)。
- 業務上的高内聚,減少依賴(從設計上要避免服務過大或者太小)。
龐大的分布式系統,需要強大基礎設施來支撐,微服務涉及哪些基礎設施?
- CI/CD 和自動化(分布式系統幾乎不可能通過人工手動釋出。)
- 虛拟化技術(要保證微服務運作環境隔離,目前行業主流的是使用 Docker 容器)。
- 日志聚合,全鍊路監控(高度可觀察和分析診斷問題)。
說了那麼多,那什麼樣的情況下,你的團隊不适合建設微服務?(請勿對号入座)
- 開發團隊不具備自主性,所在組織對開發團隊限制非常多(具體請參考 康威定律)。
- 團隊不熟悉業務,無法識别出服務的邊界,進行合理的拆分(請參考 DDD 領域驅動設計)。
微服務設計其實是很早就有的設計思想,因為随着虛拟化技術的崛起,微服務可以低成本的實作,是以也開始流行和興起。
微服務的内涵很深,其中就包括,自動化,去中心化,獨立性等等,其中細節無法用一篇文章概述清楚,我們在做技術選型或者方案的時候,盡可能多去了解技術的本身和起源再結合我們業務的特點,進行更好的選擇。
如何低成本的實作微服務?
Spring Cloud 為什麼是國内最流行的微服務架構,它提供哪些開箱即用的元件 ?
概覽如下:
- Srping Boot 服務應用
- Spring Cloud Config 配置中心
- Spring Cloud Eureka 服務發現
- Spring Cloud Hystrix 熔斷保護
- Spring Cloud Zuul 服務網關
- Spring Cloud OAuth 2 服務保護
- Spring Cloud Stream 消息驅動
- 分布式全鍊路跟蹤
- 部署微服務
①Spring Boot 微服務基石
Spring Boot 是建構微服務的理想架構,主要得益于 Spring Boot 可以打包成為單個可執行JAR檔案傳遞服務,Spring Actuator 公開服務健康資訊都是微服務的基石,為什麼這麼說 ?
我們先看看建構微服務的四個重要原則:
- 服務應該是獨立和可獨立部署的
- 應該從中央(配置中心)讀取配置
- 對用戶端是透明的
- 傳達健康資訊
微服務有優點和缺點,并非所有應用都适合用微服務架構,架構師需要能做到以下要求:
- 分解業務問題:描述業務問題,注意動詞,尋找資料内聚。
- 建立服務粒度:講大服務重構到更小的服務,重點關注服務如何互相互動,服務職責随時間改變。
- 定義服務接口:擁抱 REST 的理念,使用 URI 來傳達意圖,使用 HTTP 狀态碼傳達結果。
糟糕的微服務有哪些特征?
- 過于粗粒度:服務承擔過多的職責,服務跨大量表來管理資料,測試用例太多。
- 過于細粒度:服務彼此依賴嚴重,服務内部沒有邏輯。
何時不應該使用微服務 ?
- 不願投入(需要高度成熟的運維,伸縮,複雜性問題)。
- 管理/監控散亂的伺服器也需要很高的成本。
- 小型應用不适合,太昂貴。
- 資料事務(分布式系統處理事務非常困難)
②Spring Cloud Config 配置服務
Spring Config 是 Spring 提供的配置中心輕量級實作,基于 Git 存儲,國内使用者大多推薦使用 Alibaba 開源的 Nacos(內建配置中心和注冊中心)都是非常不錯的配置中心的實作。
微服務程式對于配置中心的幾點管理原則:
- 應用程式的配置和部署的實際代碼分離(配置中心和應用程式分離)。
- 集中(将配置中心集中在少量的存儲庫中)。
- 穩定(配置中心要保證高可用)。
Spring Config 這款配置中心提供的核心功能:
- 配置伺服器允許使用環境特定值。
- 使用 Spring Profile 區分環境值。
- 可以使用基于檔案或基于 Git 存儲屬性。
- 允許對稱加密和非對稱加密。
③Spring Cloud Eureka 服務發現
服務發現是微服務架構中非常重要的概念,也稱注冊中心,類似我們生活中的房地産中介的角色,買房賣房都要通過它。
是以它是所有微服務上線/下線的必經之路,也掌握微服務的生殺大權,注冊中心會根據 CAP 政策和對微服務的健康檢查,作出對具體服務剔除,下線,恢複上線等操作。
主要還有以下幾個核心功能:
- 快速對環境中服務數量水準伸縮(功能和 K8s 有些重合,不過也可以設定具體服務的運作時數量)。
- 抽象服務的實體位置(微服務通常運作在 Docker 容器内,沒有固定 IP,隻能通過注冊中心才能找到它)。
- 提高程式的彈性,自動伸縮。
- 資訊共享, 健康檢測。
微服務架構裡面要實作注冊中心,需要達到哪些基本要求?
- 高可用,注冊資訊共享(注冊中心叢集部署),不可能因為注冊中心挂了,導緻所有叢集不可用。
- 負載均衡(對服務間的動态請求負載均衡),合理在服務間配置設定流量。
- 有彈性(用戶端緩存服務資訊)。
- 容錯,健康檢查(檢測壞掉的服務自動移除,無需人工幹預)。
Spring Eureka 提供的服務發現實作,具備哪些特點 ?
- 服務發現抽象服務的實體位置。
- 無感覺添加和移除服務。
- 為服務間調用提供負載均衡。
- 使用 3 種不同機制來調用服務:DiscoveryClient,支援 Ribbon 的 RestTemplate,Netflix 的 FeignClient。
④Spring Cloud Hystrix 熔斷保護
為什麼微服務進行熔斷 ?當一個服務出現問題:
- 通常都是從小部分開始,到耗盡線程徹底崩潰。
- 服務間調用會長時間阻塞。
- 服務未關閉就會一直被調用,導緻連鎖效應。
- 一個性能不佳的服務可以迅速拖垮整個應用。
為什麼熔斷很重要 ?
- 每個節點(調用服務和資料庫)實作斷路器,可以避免服務崩潰的連鎖效應。
- 實作隻有出問題的服務受影響,其餘的服務功能都是完整的(影響範圍降到最小)。
- 熔斷是伺服器的靈活的基礎。
斷路器提供的關鍵能力:
- 快速失敗。
- 功能降級(替代方案)。
- 無縫恢複(斷路器定期檢查,自動恢複服務)。
Netflix 研發和出品的 Hystrix 實作是一款成熟穩定的熔斷實作,在 Netflix 在生産實踐和運作多年,非常可靠,後面加入 Spring Cloud 體系,成為 Spring Cloud 微服務生态鍊的一員,使用起來也非常的簡單和友善。
Hystrix 支援四種斷路模式:
- 用戶端負載均衡模式(檢測服務出錯,移除服務)。
- 斷路器模式(出現逾時抛出異常強行失敗,超過門檻值強行失敗所有調用)。
- 後備模式(不是抛出異常而是執行替代方案,例如排隊,稍後再試等)。
- 艙壁模式(把遠端調用的資源配置設定到獨立的線程池中,調用出問題隻是線程池飽和并停止請求)。
跳閘會導緻的三種結果:
- 服務 B 立即知道服務 C 有問題,不必等待,立即失敗。
- 服務 B 執行服務 C 的替代代碼來采取行動(後備模式)。
- 服務 C 可以在跳閘後,檢查問題,快速恢複。
熔斷的幾個處理原則:
- 設計分布式應用必須考慮彈性。
- 服務的徹底故障是很容易檢測和處理,隻是需要時間,斷路器給了這個時間視窗。
- 一個服務性能不佳,可能導緻叢集崩潰,因為互相調用會阻塞線程,耗盡資源。
- Hystrix 支援兩種隔離模型,即 THREAD 和 SEMAPHORE。
⑤Spring Cloud Zuul 網關
網關是整個微服務分布式叢集的入口,對于使用者來說,使用者無需知道你每個服務的位址,隻需要記住網關位址就可以了。
這樣了解可能比較抽象,舉一個生活的例子,微服務叢集是一個大型公司,公司内部有很多個不同的職能部門(對應每個微服務)。
但是你如果要通路具體的職能部門,你必須先到前台登記,再由前台帶你到你想通路的具體職能部門去辦理實際的業務(智能路由)。
微服務對于網關實作的規範:
- 一個獨立負責所有服務調用過濾和路由的服務。
- 服務和用戶端的中間人,簡化用戶端開發。
網關通常要做哪些事情:
- 靜态路由,從注冊中心擷取每個微服務的具體位置。
- 動态路由(根據參數特點,調用特定服務,少量使用者體驗新功能,通常用于灰階釋出)。
- 驗證和授權:驗證訪客的身份資訊(統一驗證,服務隻需要關注業務邏輯)。
- 資料收集和日志(收集調用次數和響應時間等)。
Zuul 網關的具體運作參考圖:
Spring Cloud Zuul 是初期版本的 API 網關實作,提供以下功能:
- 結合 Spring Cloud Eureka 将服務發現的注冊位址加入到 Zuul 路由。
- Zuul 可以給所有服務輕松的添加/API 之類的字首路由位址。
- 在全局上定制 Zuul 的 Spring Cloud Hystrix 和 Spring Cloud Ribbon (排程政策)的逾時。
- 實作動态路由,不同版本進行 A/B 測試。
- 檢查參數合法性等,例如 JWT,時間戳等等。
⑥Spring Cloud OAuth 2 服務保護
Oauth 2 是用于保證請求的合法和正确性,為了讓微服務本身更加專注于業務,是以 OAuth 2 類似配置中心被單獨抽離出來作為基礎元件的統一認證中心來使用。
OAuth 2 的作用類似我們生活中的警察局的角色,當我們需要去正規機構辦理業務的時候,我們需要提供有效的身份證(合法的身份認證标示)。
如果沒有你就需要去警察局(OAuth)申請一張在有效期内的身份證(Token),然後帶着這張身份證我們才能去購買機票,酒店等其他社會服務(微服務)。
社會服務機構在拿到你提供的身份證(Token)後,也會向警察局(OAuth)聯網發送資訊,來驗證你的身份證的合法性(Token 合法性校驗),身份認證不通過就會被拒絕服務,合法的身份才能進行業務的辦理。
關于 OAuth 的工作流程,可以結合下圖來了解:
微服務對于 OAuth2 規範的 4 種類型授權:密碼/用戶端憑據/授權碼/隐式。
Spring Cloud OAuth 2 為我們提供哪些便利?
- 安全架構,提供令牌生成,驗證等邏輯。
- 開箱即用,和其他服務無縫內建。
- 行業标準,輕松與雲服務商內建。
OAuth 2:/auth/oauth/token 的傳回資訊:
- access_token(OAuth 2 令牌,每次調用出示)。
- token_type(令牌類型,常用 bearer token)。
- refresh_token(續約令牌)。
- expires_in(過期描述,預設 12H)。
- scope(令牌有效作用域)。
OAuth 2 支援 JWT (JSON Web Token)的規範,關于 JWT 的原理就不特别解釋了。
簡單的 JWT 有以下幾個特點:
- 小巧(Base64編碼)。
- 密碼簽名(防篡改)。
- 自包含(不需要調用驗證服務确認内容,通過相同的密鑰進行對稱解密)。
- 可擴充(可在令牌内包含額外的資訊)。
OAuth 2 的簡單總結:
- OAuth 2 是一個令牌驗證架構。
- 使用 OAuth 2 需要建立 OAuth 2 驗證服務。
- 調用受保護的資源都要通過 OAuth 2 驗證。
⑦Spring Cloud Stream 消息驅動
我們和世界的互動不是同步的,很多時候是基于消息異步驅動模型,比如郵件,點餐,訂票等等,想要了解 Spring Cloud Stream,必須先要了解基于事件(MQ)程式設計的模型,基于消息驅動有利于開發建構高度解耦的系統。
因為 Spring Cloud Stream 并不是自己實作了消息中間件,而是對于市場上主流(例如 RabbitMQ,KafKa)的 MQ 産品做了一層封裝和抽象。
Spring Cloud Stream 做的事情并不是什麼新鮮的事情,非常類似 ORM 所做的事情,了解 ORM 架構的同學應該都熟悉對于多種資料庫(MySQL,Oracle,SQL Server)産品的抽象是何等重要。
面向 ORM 進行資料庫通路,可以讓你脫離對于指定資料庫産品的深度依賴和綁定,而且可以不用特意去學習不同資料庫的本地化特性和方言,降低學習成本。
假如你想從 Oracle 遷移到 MySQL 上面,幾乎是不需要改動一行代碼,隻需要改動 ORM 的配置就可以實作了。
參考下圖簡單了解一下 ORM:
Spring Cloud Stream 類似 ORM,你隻需要基于 Spring Cloud Stream 提供的消息模型進行程式設計。
至于底層的消息元件是用的 RabbitMQ 還是 Kafka 還是其他的消息中間件産品,都沒有關系,甚至更換底層消息元件也不會對你的應用産生任何影響,這就是标準化所帶來的收益。
關于如何更好的了解 Spring Cloud Stream 工作模型可以簡單參考下圖:
微服務中使用的的兩種服務通信方式對比:
- 同步:通過 REST 端點接口進行請求:服務之間緊耦合(強依賴),服務之間的脆弱性(連鎖效應),增加新的消費者不靈活。
- 異步:基于消息中間件通信:松耦合(無接口直接調用的依賴),耐久性(服務重新開機後可以消費曆史消息),可伸縮性(消息過多可啟動多服務來處理消息),靈活性(輕松添加新的消費者)。
消息傳遞架構的缺點:
- 消息處理語義:消息順序處理,消息異常處理。
- 消息可見性:消息不會立刻被處理,事務關聯 ID 在消息中的傳遞。
消息中放置什麼資料 ?
- 消息體要盡可能的小,減少傳輸成本:通常隻傳回 action 類型和 id,然後用id擷取最新資料。
- 隻使用消息傳遞狀态:在消息中包含版本号和時間戳,處理資料服務可以檢查資料的版本号。
Spring Cloud Stream 的消息模型和概念:
- 發射器(Source):接收對象(對象表示要釋出的消息),序列化對象,将消息釋出到通道。
- 通道(Channel):隊列的抽象,通道寫在配置檔案,更改配置切換通道(讀取和寫入隊列)。
- 綁定器(Binder):與消息平台對話的 Spring 代碼,不必依賴特定的 API 來釋出和消費消息。
- 接收器(Sink):從隊列接收消息,将消息反序列化為 POJO。
Spring Cloud Stream 的簡單總結:
- 使用消息傳遞的異步通信是微服務架構的關鍵部分。
- 使用消息傳遞可以使服務能夠伸縮并且更具有容錯性。
- Spring Cloud Stream 通過簡單的注解抽象底層的消息平台細節。
⑧Sleuth 和 Zipkin 分布式跟蹤
微服務分布式架構帶來了複雜度,成本最高的就是跟蹤檢查和運維,分布式意味要在多個服務,機器跟蹤一個事務,Sleuth 和 Zipkin 都是用于 Spring Cloud 服務體系的分布式跟蹤技術,先直接看下最終效果。
下圖一個簡單的可視化鍊路跟蹤調用,ZipKin 可以清晰的看到一個用戶端請求在每個服務上面處理所消耗的事情,點選進去可以看到具體的事務執行内容,友善排查錯誤。
Spring Cloud Sleuth 的工作流程:
- 透明地建立并注入一個關聯 ID 到服務調用中。
- 管理關聯 ID 到出站服務調用的傳播。
- 将關聯資訊添加到 Spring 的 MDC 日志記錄(應用/跟蹤 ID/跨度 ID/資料發送)。
- 将服務調用中的跟蹤資訊釋出到 Zipkin 跟蹤平台。
Open Zipkin 的簡單概述:
- 調用鍊使用一張幹淨簡潔的圖檔,比一百萬條日志要好看的多。
- 分布式跟蹤平台,用于跟蹤多個服務調用的事務。
- 圖形的方式檢視事務占用的時間量,分解每個服務所用的時間。
- 4 種不同的資料存儲:記憶體資料/MySQL/Cassandra/Elasticsearch。
關于微服務全鍊路跟蹤的總結:
- Spring Cloud Sleuth 可以無縫将關聯 ID 添加到微服務中。
- 可以使用關聯 ID 檢視事務涉及的所有服務行為。
- 關聯 ID 需要與日志聚合結合使用。
- 日志平台很重要,但是可視化跟蹤事務也是有價值的工具。
⑨部署微服務
建構和部署管道是微服務架構最重要的部分,微服務的主要特點是快速建構,修改,釋出。
符合微服務特征的部署要達到以下幾個要求:
- 自動的(自動建構和部署代碼。
- 完整的(軟體成品是鏡像),不可變(釋出過程不可人為幹預)。
- 良好的微服務部署管道應該允許在幾分鐘部署新功能和修複 Bug。
Spring Cloud 大型項目的架構方案
這是一個真實用于國内某大型企業的微服務架構體系,支撐日均百萬訂單的項目,因為已經過了 2 年的保密期,是以可以拿出來分享。
剛好可以結合前面淩亂的知識點,看看 Spring Cloud 這套元件是如何搭建起來的。
整套微服務就是下面這張架構圖:
具體每個元件的作用就不在這裡詳細說明了,在這套架構方案裡面,我們沒有完全照搬 Spring Cloud 全家桶的組建,還是根據自己的需求對其中元件進行的更換。
例如:
- 配置中心從 Spring Cloud Config 更換為 Apollo ,除了有更好的性能,還有更加簡化的操作頁面,修改配置檔案毫秒級響應。
- 服務發現 Eureka 官網已經停止維護,我們後面更換為 Alibaba Nacos,服務注冊和心跳檢測都提升到毫秒級,Eureka 是 90 秒輪詢。
- 分布式任務排程引入了 XXL-JOB,這是國内主流的分布式任務排程平台,沒有特别需要說明的地方。
- 日志聚合也是用了主流的 ELK 技術方案,用于收集和檢索日志。
PS:另外在值得補充的是,在寫這篇文章的時候 Spring Cloud Zuul 已經不被官方推薦使用了,替代品是性能更好的 Spring Cloud Gateway ,大家可以在了解的時候需要注意一下。
微服務是未來大型企業的必經之路,雖然成本很高,但是可以提升 IT 系統的健壯性和提升技術人員的廣度和深度都還是很有幫助的。