天天看點

微服務設計指南

2018年,每個人都聽說過微服務。但你知道怎麼設計嗎?

微服務是當今軟體工程師的一個熱門話題。讓我們了解如何使用微服務架構風格建構真正子產品化、業務靈活的IT系統。

一、微服務概念

微服務體系結構由輕量級、松散耦合的服務集合組成。每個服務都實作了單個業務功能。理想情況下,這些服務應該是具有足夠的内聚性,可以獨立地開發、測試、釋出、部署、擴充、內建和維護。

正式定義 

“微服務架構風格是一種将單個應用程式開發為一組小型服務的方法,每個小服務運作在自己的程序中,并且以輕量級機制(通常是HTTP REST API)通信。這些服務是圍繞業務能力建立的,并且可以由完全自動化的部署機構獨立部署。這些服務的集中管理隻有最低限度,可以用不同的程式設計語言編寫并使用不同的資料存儲技術。”

——  James Lewis and Martin Fowler

定義微服務的特性

  • 每個服務都是一個輕量級、獨立和松散耦合的業務單元。
  • 每個服務都有自己的代碼庫,由一個小團隊管理和開發(主要是用于靈活環境中)。
  • 每個服務負責一部分功能或者說業務能力,并且做得很好。
  • 每個服務都可以為其用例選擇最佳的技術棧(無需将整個應用程式綁定在一個架構中)。
  • 每個服務都有自己的DevOps計劃(測試、釋出、部署、擴充、內建和獨立維護)。
  • 每個服務都部署在一個獨立自給的環境中。
  • 服務通過使用定義良好的API(智能端點)和簡單協定如基于HTTP 的REST協定(啞管道)互相通信。
  • 每個服務負責持久化自己的資料和保持外部狀态(隻有當多個服務使用相同的資料時,這種情況才在公共資料層中處理)。

白小白:

智能端點和啞管道,其實我一直認為“啞”管道不如“笨”管道或者“呆”管道更易了解。防呆設計是一種預防使用者錯誤使用産品造成不良後果的設計理念,比如USB設計成一半有實體晶片,就是讓使用者可以不假思索的在插錯後直接掉轉方向再插。不讓使用者思考就是“呆”的含義。“啞”管道的“啞”其實就是展現在微服務的通信過程盡量簡單,不要讓通信機制有“思考能力”,不在其中加入過多的處理機制,反例是SOA時代的ESB産品,ESB産品通常會包含複雜的設施用于消息路由,編排和轉換,以及業務規則應用。反過來,智能端點的概念就容易了解了,也就是将與某服務相關的處理都限定在微服務的範疇之内,通信過程中的微服務端點是“智能”的,這也從一個方面展現了微服務“高内聚”的含義,有了高内聚,才能具備自治和獨立性,進而可以支援“松耦合”的機制。

微服務的好處

微服務可以用于擴充大型系統,也為持續內建和傳遞提供了巨大的能力。

微服務設計指南

Scale Cube:用于可伸縮性的三維模型

(圖檔:Nginx部落格)

獨立縮放:《 The Art of Scalability》( http://t.cn/EAvlQ4o)這本優秀的書中所描述的Scale Cube概念,是微服務架構所支援的。在開發微服務以實作功能分解時,應用程式通過Y軸自動縮放。當服務調用量較高時,微服務可以通過克隆更多的CPU和記憶體,通過X軸進行擴充。為了在多台機器上分發資料,可以分離大型資料庫(分庫分表)轉換成更小、更快、更容易管理的部件,進而實作Z軸的縮放。

獨立釋出和部署:使用微服務,Bug修複和特性釋出更易于管理,風險更小。可以在不重新部署整個應用程式的情況下更新服務,并在出現問題時復原或前滾更新。

獨立開發:每個服務都有自己的代碼庫,由一個小的焦點小組開發、測試和部署。開發人員可以專注于一種服務,并且隻關注相對較小的範圍。這将提高生産率、項目速度、持續創新能力和源碼品質。

優雅降級:如果服務崩潰,其影響不會傳播到應用程式的其他部分,并導緻系統發生災難性故障,進而展現某種程度的健壯性。

分散治理:開發人員可以自由選擇技術棧,制定最适合其服務的設計标準和實作決策。團隊不必因為過去的技術決定而受到懲罰。

業務關切

獨立的服務本身并不能形成一個系統。要使微服務體系結構真正成功,需要大量投資來處理跨系統的問題,例如:

  • 服務複制:一種讓服務易于擴充的基于中繼資料的機制
  • 服務注冊和發現:啟用服務查找并查找服務端點的機制
  • 服務監測和日志:收集來自不同微服務的日志的機制,并提供一緻的報告
  • 彈性:服務在故障期間自動采取糾正行動的機制
  • DevOps:處理持續內建和部署的機制(CI和CD)
  • API網關:為用戶端提供入口的機制

二、中間件與設計模式

API網關(所有用戶端的單一入口點)

微服務設計指南

API網關風格的微服務體系結構(圖檔來自:Microsoft Azure Docs),是用于微服務的最常見的設計模式。API網關是一個中間層,具有最小化的路由功能,隻是充當一個“啞管道”,裡面沒有業務邏輯。一般來說,API網關允許用戶端基于REST/HTTP調用托管的API。其他類型的微服務內建模式有:點對點風格(直接從用戶端應用程式調用服務)和消息代理風格(實作異步消息傳遞)。

API網關充當所有用戶端的單一入口點,API網關也作為一種邊緣服務來将微服務作為托管API公開給外部世界。這聽起來像是一個反向代理,但也有一些額外的責任,例如簡單的負載平衡,認證和授權,故障處理,稽核,協定轉換,和路由機制。開發團隊可以選擇以下方法之一來實作API網關。

  • 自己程式設計實作:具有更好的客戶化和管控能力。
  • 部署現有的API網關産品:節省初始開發時間,并使用進階内置功能(缺點在于:此類産品依賴于供應商,并不完全免費。配置和維護通常是冗長而耗時的)

解釋API網關行為的一些設計模式如下(請參閱微服務設計模式 http://t.cn/RKx8bhG).

網關聚合(http://t.cn/EAvT2jl):将針對多個内部微服務的多個用戶端請求(通常是HTTP請求)聚合到單個用戶端請求中,減少了使用者和服務之間的互動和網絡延遲。

網關分流(http://t.cn/EAvTGmA):使單個微服務能夠将一些共享的服務功能分流到API網關級别。這些跨服務功能包括認證、授權、服務發現、容錯機制、QoS、負載平衡、日志記錄、分析等。

網關路由(第7層路由,通常是HTTP請求 http://t.cn/EAvTMm4):使用單一入口端點将請求路由到内部微服務的端點,這樣服務調用者就不需要自行管理多個獨立的端點

請注意,API網關應該始終是一個高可用性和高性能的元件,因為它是整個系統的入口點。

事件總線(用于異步事件驅動通信的、釋出/訂閱、中介通道)

微服務設計指南

微服務之間基于事件驅動的異步通信實作最終一緻性

(圖檔來源:microsoft.com)

應用程式的不同部分在進行互相通信時,無論消息的順序(為處理異步的消息)或使用的語言(為了展現語言無關性),都可以使用事件總線來實作。大多數事件總線支援釋出/訂閱、分布式、點對點和請求響應消息傳遞。一些事件總線(如Vert.x)允許用戶端使用相同的事件總線與相應的伺服器節點進行通信,這是全堆棧團隊所喜愛的一個很酷的特性。

服務網格(用于服務間通信的外挂(Sidecar)機制)

微服務設計指南

服務網格風格的服務間通信

(圖檔來源:微服務實踐http://t.cn/EAAJWRi)

微服務設計指南

如何在應用程式中使用服務網格

(圖檔來源:http://t.cn/EAAizgn)

服務網格通過提供服務間通信的輔助架構來實作外挂模式,包括彈性(容錯、負載平衡)、服務發現、路由、可觀察性、安全性、通路控制、通信協定支援等功能。

微服務設計指南

服務網格在網絡堆棧中的位置

實際上,外挂執行個體部署在每個服務的旁邊(理想情況下是在同一個容器中)。他們可以通過服務本身的網絡功能來進行通信。服務網格的控制平面被單獨部署,以提供中心功能,如服務發現、通路控制和可觀察性(監視、分布式日志記錄)。最重要的是,服務網格風格的設計模式允許開發人員從微服務代碼中分離網絡通信功能并使服務隻關注于業務功能。(來自:Netflix Prana, 微服務網格)

盡管上面的圖檔顯示了服務之間的直接連接配接,但是處理服務間通信的好方法是使用一個簡單的事件總線作為中介,以保持最低級别的耦合。

聚合器(BFF模式)

微服務設計指南

在API網關級别上實作BFF模式和聚合器模式

(圖:microsoft.com)

如果應用程式需要裁剪每個API以适應用戶端應用程式類型(Web端、移動端以及其他不同平台),則可以通過聚合器(Aggregator)執行不同的業務規則,也可以執行不同的配置以根據用戶端功能适配不同的建構。這可以在API網關級别實作,也可以在服務級别并行實作。這種模式對于提供特定的使用者體驗非常有用。但是,開發團隊應該足夠小心,将BFF保持在可管理的範圍内。

“通過聚合器”,原文是“via a facade”,直譯是“通過一個外觀/門面”。門面模式(外觀模式),是一種Java的設計模式,為子系統中的一組接口提供了一個統一的通路接口,引申自一個前店後廠的生意模式,前面是門面,後面會有進料、生産、包裝多個服務。用在這裡是指将相關的服務通過聚合器聚合在一起,這個聚合器就是門面。服務調用者與門面互動而不是與一組服務互動降低了耦合性,但同時違反了面向對象設計原則開閉原則,開閉原則要求子產品在擴充時可以不改動内部的代碼,但顯然當聚合器後端的某個服務發生變更時,需要在聚合器層面也發生變更,這也是文中說“開發團隊應該足夠小心”的原因,因為違反了開閉原則,就會降低可複用性。

三、最佳實踐

✅ 領域驅動設計:圍繞業務領域進行服務模組化。

微服務設計指南

為了處理大型模型和團隊,可以應用領域驅動設計(DDD)。DDD通過将大型模型劃分為不同的有界上下文來明确他們之間的互相關系和子領域。這些有界上下文可以在應用設計級别轉換為單獨的微服務。(參見:領域驅動設計中的有界上下文 http://t.cn/EAAK4Xk)

✅ 分散資料管理(避免共享資料庫):當多個服務使用一個共享資料架構時,會在資料層形成緊耦合。為了避免這種情況,每個服務都應該有自己的資料存取邏輯和獨立資料存儲。開發團隊可以根據服務和資料性質的不同自由選擇最适合的資料持久性方法。

微服務設計指南

避免共享資料存儲和通路機制

(圖檔來源:http://t.cn/RcLB5Kv)

✅ 智能端點和啞管道:每個服務都擁有一個定義良好的外部通信API,并盡量避免洩露實作細節。通信則始終使用簡單協定,如基于HTTP的REST協定。

✅ 異步通信:當跨服務使用異步通信時,其他服務不會阻塞資料流。

微服務設計指南

同步消息和異步消息傳遞

(來源: http://t.cn/EAA9xRU)

✅ 避免服務耦合:服務應保持松耦合和高内聚。産生耦合的主要原因包括共享資料庫模型和嚴格的通信協定。

✅ 分散開發:避免在多個服務/項目之間共享代碼庫、資料架構或開發團隊成員。讓開發者從源頭上關注創新和品質。

✅ 将領域知識排除在網關之外:讓網關處理路由和跨服務問題(如身份驗證、SSL終端等)。

✅ 基于令牌的認證:不要在每個微服務級别實作安全元件,因為這将需要元件與集中式/共享使用者存儲庫對話并檢索身份驗證資訊;而是考慮實作API網關級别的身份驗證,使用廣泛使用的API安全标準,如OAuth2和OpenID Connect。一旦從認證提供者獲得令牌之後,就可以用于與其他微服務進行通信。

微服務設計指南

使用OAuth2和OpenID Connect的微服務安全性

(來源:Kasun’s Blog http://t.cn/EAACGvY)

✅ 事件驅動性質:既然人可以成為對事件作出反應的自主主體,系統也可以。(參見:為什麼微服務應該是事件驅動的:自主性與權威性 http://t.cn/EAACWOx)

✅ 最終一緻性:由于微服務的高内聚特性,很難在整個系統内實作很強的一緻性。是以開發團隊必須處理最終一緻性。

✅ 容錯:由于系統由多個服務和中間件組成,是以在某些地方可能很容易發生故障。對于這些薄弱環節,有一些實作模式,如斷路器,防水艙,重試,逾時,快速失敗,故障轉移緩存,速率限制,負載釋放,可以将重大故障的風險降到最低。(參見:設計一種面向故障的微服務體系結構http://t.cn/RChKlg9)

✅ 産品工程化:把微服務工程化為一種産品,而不是作為一個項目,可以讓微服務更好的發揮作用。這意味着,不能僅僅考慮能用而且及時傳遞,而是要長期緻力于卓越的工程化。

四、微服務實踐

何時使用微服務

微服務架構最适合的應用場景:

  • 具有高可伸縮性需求的應用
  • 對傳遞速度要求較高的項目
  • 具有豐富域或多個子域的業務用例
  • 小型、跨功能的開發團隊協作開發大型産品的靈活環境(請參閱:微服務架構的真正成功故事 http://t.cn/EAANng7)

一些實作微服務的入門架構

Vert.x:輕量級,易于了解/實作/維護,多語言支援(支援多種語言),事件驅動,非阻塞,可以說,具備了以最少的硬體處理高并發需求時的最佳性能和可伸縮性,并且具備足夠的開放性(與傳統的限制性架構不同,Vert.x隻提供有用的元件,開發人員可以自由地創新并仔細建構他們的應用程式)

Akka:令人滿意的性能,實作了Actor模型(一種并發模型),有利于響應式微服務和事件驅型微服務

Spring Boot/Spring Cloud:容易上手(采用熟悉範式),基于良好的舊Spring架構,有點重的架構,許多內建可用,大規模的社群支援

Drop Wizard:有利于RESTful Web服務的快速開發,它搭載了一些不錯的Java工具和庫,如Google Guava、Jetty Server、Logback、Hibernate Validator、Joda Time、Jersey和Jackson。

部署選項

容器:有利于執行DevOps目标(快速開發,縮短上市時間,無縫縮放)

雲計算架構:有利于建構可靠和可伸縮的基礎設施,為地理位置分散的使用者服務。

無伺服器架構:适合處理高度不穩定的流量。

維護自己的IT基礎設施:對那些擁有足夠能力和資源建設整個基礎設施的組織來說是件好事。

微服務的開發理念

自給系統:由獨立系統組裝軟體(按業務垂直切分系統)

微前端:将單體應用的Web UI劃分為獨立的特性,這些特性可以作為獨立的UI元件開發,并直接與微服務進行通信。

需要搜尋和學習的關鍵詞

領域驅動設計(DDD)| 有界上下文(BC)| 聚合持久性(PP)| 指令和查詢責任隔離(CQRS)| 指令查詢分離(CQS)| 事件溯源(ES)| CAP定理 |最終一緻性 |十二要素應用 |SOLID原則 |

五、參考架構

微服務設計指南

用于線上購物應用程式的微服務體系結構

(圖檔:microsoft.com)

此體系結構是由使用Microsoft技術的Microsoft開發人員提出的。在這裡,API Gateway是針對不同的Web和移動使用者而定制的。對于資料層,資料存儲技術是根據業務功能仔細選擇的(關系資料庫用于結構化資料,Redis用于臨時資料緩存,MongoDB和Cosmos DB用于非結構化資料)。事件總線處理服務間通信。撇開技術不說,這是基于微服務的應用最常見的內建模式。

微服務設計指南

一種非阻塞應用程式的微服務體系結構,該應用程式使用來自各種事件源(例如交通資料、天氣指數、股票市場線索、社交媒體文章、傳感器輸出)的大量輸入資料流來向最終使用者顯示實時更新。這些輸入資料流最初由使用Kafka實作的事件日志收集。它将資料儲存在磁盤上,是以可以用于批處理調用(分析、報告、資料科學、備份、審計)或用于實時調用(營運分析、CEP、管理儀表闆、警報應用程式)。上圖中,使用Spark按指定的時間間隔,将持續的輸入資料流劃分為微批次,并輸入到WSO2 Siddhi CEP引擎中。後者辨別事件并使用MongoDB存儲以非結構化形式存儲資料。微服務調取這些資料并顯示給最終使用者。仔細觀察這一設計, Vert.x事件總線能夠建立與前端UI元件的連接配接,該特性僅用于有效地更新UI中的相關部分。撇開技術不說,這是基于事件驅動的非阻塞微服務應用程式的一個很好的架構。