什么是微服務模式
隨著網絡基礎設施的高速發展,以及越來越多的個體接入互聯網,在考慮構建支持海量請求以及多變業務的軟件平臺時,微服務架構成為多數人的首選。微服務架構的出現時服務事物發展規律的:當問題足夠大,有足夠多的的不確定因素時,人們習慣于把大的問題拆分成小的問題。通過分割,抽象和重用小而可靠的功能模塊來構建整體方案。但是當這些小的,可重用的部分多來越多的時候,又會出現新的問題。再相似的階段,人們遇到的問題也是相似的,這個時候人們需要一些共識,需要用一些通用的詞匯來描述問題以及解決方案,這也是人們知識的總結,微服務模式就是這樣的總結和概括,是一種可以通用的共識,用于描述微服務領域的中的問題及解決方案。
單體結構的歷程
在企業發展的初期,應用程序相對較小,所有的代碼運行在一個應用程序中有以下好處
- 應用的開發很簡單:IDE 和其他開發工具只需要構建這一個單獨的應用程序。
- 易于對應用程序進行大規模的更改:可以更改代碼和數據庫模式,然后構建和部署。測試相對簡單直觀:開發者只需要寫幾個端到端的測試,啟動應用程序,調用 REST API,然后使用 Selenium 這樣的工具測試用戶界面。
- 部署簡單明了:開發者唯一需要做的,就是把 WAR 文件復制到安裝了 Tomcat 的服務器上。
- 橫向擴展不費吹灰之力:FTGO 可以運行多個實例,由一個負載均衡器進行調度。
不幸的是,開發人員已經意識到的,單體架構存在著巨大的局限性。每一次開發沖刺(Sprint), 開發團隊就會實現更多的功能,顯然這會導致代碼庫膨脹。而且,隨著公司的成功,研發團隊的規模不斷壯大。代碼庫規模變大的同時,團隊的管理成本也不斷提高。
- 過度的復雜性嚇退開發者
系統本身過于龐大和復雜,以至于任何一個開發者都很難理解它的全部。因此,修復軟件中的問題和正確地實現新功能就變得困難且耗時。各種交付截止時間都可能被錯過。更糟糕的是,這種極度的復雜性正在形成一個惡性循環:由于代碼庫太難于理解,因此開發人員在更改時更容易出錯,每一次更改都會讓代碼庫變得更復雜、更難懂 - 開發速度緩慢
巨大的項目把開發人員的 IDE 工具搞得很慢,構建一次應用需要很長時間,更要命的是,因為應用太大,每啟動一次都需要很長的時間。因此,從編輯到構建、運行再到測試這個周期花費的時間越來越長,這嚴重地影響了團隊的工作效率。 - 從代碼提交到實際部署的周期很長,而且容易出問題
從代碼完成到運行在生產環境是一個漫長且費力的過程。一個問題是,眾多開發人員都向同一個代碼庫提交代碼更改,這常常使得這個代碼庫的構建結果處于無法交付的狀態。當我們嘗試采用功能分支來解決這個問題時,帶來的是漫長且痛苦的合并過程。緊接著,一旦團隊完成一個沖刺任務,隨后迎接他們的將是一個漫長的測試和代碼穩定周期。 - 把更改推向生產環境的另一個挑戰是運行測試需要很長時間。因為代碼庫如此復雜,以至于一個更改可能引起的影響是未知的,為了避免牽一發而動全身的后果,即使是一個微小的更改,開發人員也必須在持續集成服務器上運行所有的測試套件。系統的某些部分甚至還需要手工測試。如果測試失敗,診斷和修復也需要更多的時間。因此,完成這樣的測試往往需要數天甚至更長時間。
- 需要長期依賴某個可能已經過時的技術棧
單體地獄的最終表現,也體現在團隊必須長期使用一套相同的技術棧方面。單體架構使得采用新的框架和編程語言變得極其困難。在單體應用上采用新技術或者嘗試新技術都是極其昂貴和高風險的,因為這個應用必須被徹底重寫。結果就是,開發者被困在了他們一開始選擇的這個技術之內。有時候這也就意味著團隊必須維護一個正在被廢棄或過時的技術所開發的應用程序。
單體地獄的銀彈-微服務架構
軟件架構其實對功能性需求影響并不大。事實上,在任何架構甚至是一團糟的架構之上,你都可以實現一組用例(應用的功能性需求)
架構的重要性在于它影響了應用的非功能性需求,也稱為質量屬性或者其他的能力 。隨著 FTGO 應用的增長,各種質量屬性和問題都浮出水面,最顯著的就是影響軟件交付速度的可維護性、可擴展性和可測試性。 訓練有素的團隊可以減緩項目陷入單體地獄的速度。團隊成員可以努力維護他們的模塊化應用。他們也可以編寫全面的自動化測試。但是另一方面,他們無法避免大型團隊在單體應用程序上協同工作的問題,也不能解決日益過時的技術棧問題。團隊所能做的就是延緩項目陷入單體地獄的速度,但這是不可避免的。為了逃避單體地獄,他們必須遷移到新架構:微服務架構。
今天,針對大型復雜應用的開發,越來越多的共識趨向于考慮使用微服務架構。但微服務到底是什么?不幸的是,微服務這個叫法本身暗示和強調了尺寸 。針對微服務架構有多種定義。有些僅僅是在字面意義上做了定義:服務應該是微小的不超過 100 行代碼,等等。另外有些定義要求服務的開發周期必須被限制在兩周之內。曾在 Netflix 工作的著名架構師Adrian Cockcroft 把微服務架構定義為面向服務的架構,它們由松耦合和具有邊界上下文的元素組成。這個定義不錯,但仍舊有些復雜難懂。立方體模型會是更好的定義。
擴展立方體和服務
- X 軸擴展:在多個實例之間實現請求的負載均衡
X 軸擴展是擴展單體應用程序的常用方法。在負載均衡器之后運行應用程序的多個實例。負載均衡器在 N 個相同的實例之間分配請求。這是提高應用程序吞吐量和可用性的好方法
- Z 軸擴展:根據請求的屬性路由請求
Z 軸擴展也需要運行單體應用程序的多個實例,但不同于 X 軸擴展,每個實例僅負責數據的一個子集。圖 1-5 展示了 Z 軸擴展的工作原理。置于前端的路由器使用請求中的特定屬性將請求路由到適當的實例。例如,應用程序可能會使用請求中包含的 userId 來路由請求。在這個例子中,每個應用程序實例負責一部分用戶。該路由器使用請求 Authorization頭部指定的 userId 來從 N 個相同的應用程序實例中選擇一個。對于應用程序需要處理增加的事務和數據量時,Z 軸擴展是一種很好的擴展方式
- Y軸擴展:根據功能把應用拆分為服務
X 軸和 Z 軸擴展有效地提升了應用的吞吐量和可用性,然而這兩種方式都沒有解決日益增長的開發問題和應用復雜性。為了解決這些問題,我們需要采用 Y 軸擴展,也就是功能性分解。Y 軸擴展把一個單體應用分成了一組服務
服務本質上是一個麻雀雖小但五臟俱全的應用程序,它實現了一組相關的功能,例如訂單管理、客戶管理等。服務可以在需要的時候借助 X 軸或 Z 軸方式進行擴展。例如,訂單服務可以被部署為一組負載均衡的服務實例。
微服務架構的概括性定義是:把應用程序功能性分解為一組服務的架構風格。請注意這個定義中并沒有包含任何與規模有關的內容。重要的是,每一個服務都是由一組專注的、內聚的功能職責組成。
微服務架構的好處和弊端
優點
大型的復雜應用程序可以持續交付和持續部署
- 擁有持續交付和持續部署所需要的可測試性。自動化測試是持續交付和持續部署的一個重要環節。因為每一個服務都相對較小,編寫和執行自動化測試變得很容易。因此,應用程序的 bug 也就更少。
- 擁有持續交付和持續部署所需要的可部署性。每個服務都可以獨立于其他服務進行部署。如果負責服務的開發人員需要部署對該服務的更改,他們不需要與其他開發人員協調就可以進行。因此,將更改頻繁部署到生產中要容易得多
- 使開發團隊能夠自主且松散耦合。你可以將工程組織構建為一個小型(例如,兩個比薩 )團隊的集合。每個團隊全權負責一個或多個相關服務的開發和部署。每個團隊可以獨立于所有其他團隊開發、部署和擴展他們的服務。結果,開發
的速度變得更快
每個服務都相對較小并容易維護
微服務架構的另一個好處在于:相比之下每個服務都比較小。開發者更容易理解服務中的代碼。較小規模的代碼庫不會把 IDE 等開發工具拖慢,這樣可以提升開發者的工作效率。服務的啟動速度也比大型的單體應用快得多,千萬別小看這一點,快速啟動的服務會提高效率,加速研發(提高調試、部署等環節的效率)。
更好的容錯性
微服務架構也可以實現更好的故障隔離。例如,某個服務中的內存泄漏不會影響其他服務。其他服務仍舊可以正常地響應請求。相比之下,單體架構中的一個故障組件往往會拖垮整個系統
更容易實驗和采納新的技術
原則上,當開發一個新的服務時,開發者可以自由選擇適用于這個服務的任何語言和框架。當然,很多公司對此往往有各種限制和規范,但重要的是團隊有了選擇的權利,而不是被之前選定的技術綁架。更進一步,因為服務都相對比較小,使用更好的編程語言和技術來重寫一項服務變得有可能。這也意味著,如果對一項新技術的嘗試以失敗而告終,我們可以直接丟棄這部分工作而不至于給整個應用帶來失敗的風險。這跟單體架構是完全不同的,單體架構之下的技術選型會嚴重限制后期新技術的嘗試
弊端
當然,沒有一項技術可以被稱為“銀彈” 。微服務架構也存在一些顯著的弊端和問題
微服務架構的主要弊端和問題如下:
服務的拆分和定義是一項挑戰
采用微服務架構首當其沖的問題,就是根本沒有一個具體的、良好定義的算法可以完成服務的拆分工作。與軟件開發一樣,服務的拆分和定義更像是一門藝術。更糟糕的是,如果對系統的服務拆分出現了偏差,你很有可能會構建出一個分布式的單體應用:一個包含了一大堆互相之間緊耦合的服務,卻又必須部署在一起的所謂分布式系統。這將會把單體架構和微服務架構兩者的弊端集于一身。
分布式系統帶來的各種復雜性
使用微服務架構的另一個問題是開發人員必須處理創建分布式系統的額外復雜性。服務必須使用進程間通信機制。這比簡單的方法調用更復雜。此外,必須設計服務來處理局部故障,并處理遠程服務不可用或出現高延遲的各種情況。
開發者需要思考到底應該在應用的什么階段使用微服務架構
使用微服務架構的另一個問題是決定在應用程序生命周期的哪個階段開始使用這種架構。在開發應用程序的第一個版本時,你通常不會遇到需要微服務架構才能解決的問題。此外,使用精心設計的分布式架構將減緩開發速度。這對初創公司來說可能是得不償失的,其中最大的問題通常是在快速發展業務模型和維護一個優雅的應用架構之間的取舍。微服務架構使得項目開始階段的快速迭代變得非常困難。初創公司幾乎肯定應該從單體的應用程序開始 。但是稍后,當問題變為如何處理復雜性時,那就是將應用程序功能性地分解為一組服務的時候了。由于盤根錯節的依賴關系,你會發現重構很困難
服務的拆分策略
如何定義一個微服務架構呢?跟所有的軟件開發過程一樣,一開始我們需要拿到領域專家或者現有應用的需求文檔。跟所有的軟件開發一樣,定義架構也是一項藝術而非技術。本節我們將介紹一種定義應用程序架構的三步式流程
定義其架構的第一步是將應用程序的需求提煉為各種關鍵請求。但是,不是根據特定的進程間通信技術(如 REST 或消息)來描述這些請求,而是使用更抽象的系統操作這個概念。系統操作(system operation)是應用程序必須處理的請求的一種抽象描述。它既可以是更新數據的命令,也可以是檢索數據的查詢。每個命令的行為都是根據抽象領域模型定義的,抽象領域模型也是從需求中派生出來的。系統操作是描述服務之間協作方式的架構場景
該流程的第二步是確定如何分解服務。有幾種策略可供選擇。一種源于業務架構學派的策略是定義與業務能力相對應的服務。另一種策略是圍繞領域驅動設計的子域來分解和設計服務。但這些策略的最終結果都是圍繞業務概念而非技術概念分解和設計的服務。
定義應用程序架構的第三步是確定每個服務的 API。為此,你將第一步中標識的每個系統操作分配給服務。服務可以完全獨立地實現操作?;蛘?,它可能需要與其他服務協作。在這種情況下,你可以確定服務的協作方式,這通常需要服務來支持其他操作
識別系統操作
定義應用程序架構的第一步是定義系統操作。起點是應用程序的需求,包括用戶故事及其相關的用戶場景(請注意,這些與架構場景不同)。使用圖 2-6 中所示的兩步式流程識別和定義系統操作。第一步創建由關鍵類組成的抽象領域模型,這些關鍵類提供用于描述系統操作的詞匯表。第二步確定系統操作,并根據領域模型描述每個系統操作的行為。
創建抽象領域模型
定義系統操作的第一步是為這個應用程序描繪一個抽象的領域模型。注意這個模型比我們最終要實現的簡單很多。應用程序本身并不需要一個領域模型,因為我們在稍后會學到,每一個服務都有它自己的領域模型。盡管非常簡單,抽象的領域模型仍舊有助于在開始階段提供幫助,因為它定義了描述系統操作行為的一些詞語
創建領域模型會采用一些標準的技術,例如通過與領域專家溝通后,分析用戶故事和場景中頻繁出現的名詞。例如 Place Order 用戶故事,我們可以把它分解為多個用戶場景,
Given a consumer
And a restaurantAnd a delivery address/time that can be served by that restaurantAnd an order total that meets the restaurant's order minimum
When the consumer places an order for the restaurant
Then consumer's credit card is authorized
And an order is created in the PENDING_ACCEPTANCE state
And the order is associated with the consumer
And the order is associated with the restaurant
在這個用戶場景中的名詞,如 Consumer、Order、Restaurant 和 CreditCard,暗示了這些類都是需要的
同樣,Accept Order 用戶故事也可以分解為多個場景,如下
Given an order that is in the PENDING_ACCEPTANCE state
and a courier that is available to deliver the orderWhen a restaurant accepts an order with a promise to prepare by a particular
timeThen the state of the order is changed to ACCEPTED
And the order's promiseByTime is updated to the promised time
And the courier is assigned to deliver the order
經過分析最終我們可以得出如下的類圖結構
每一個類的作用如下:
- Consumer:下訂單的用戶。
Order:用戶下的訂單,它用來描述訂單并跟蹤狀態。OrderLineItem:Order 中的一個條目。DeliveryInfo:送餐的時間和地址。Restaurant:為用戶準備生產訂單的餐館,同時也要發起送貨。MenuItem:餐館菜單上的一個條目。Courier:送餐員負責把訂單送到用戶手里。可跟蹤送餐員的可用性和他們的位置。Address:Consumer 或 Restaurant 的地址。Location:Courier 當前的位置,用經緯度表示
定義系統操作
當定義了抽象的領域模型之后,接下來就要識別系統必須處理的各種請求。我們并不討論具體的用戶界面,但是你能夠想象在每一個用戶場景下,前端的用戶界面向后端的業務邏輯發出請求,后端的業務邏輯進行數據的獲取和處理
識別系統指令的切入點是分析用戶故事和場景中的動詞。例如 Place Order 用戶故事,它非常明確地告訴我們,這個系統必須提供一個 Create Order 操作。很多用戶故事都會直接對應或映射為系統命令。表 2-1 列出了一些關鍵的系統命令
命令規范定義了命令對應的參數、返回值和領域模型類的行為。行為規范中包括前置條件(即當這個操作被調用時必須滿足的條件)和后置條件(即這個操作被調用后必須滿足的條件)。例如,以下就是 createOrder() 系統操作的規范。
前置條件對應著 Place Order 用戶場景中的 givens,后置條件對應著場景中的Then。當系統操作被調用時,它會檢查前置條件,執行操作來完成和滿足后置條件。
抽象的領域模型和系統操作能夠回答這個應用“做什么”這一問題。這有助于推動應用程序的架構設計。每一個系統操作的行為都通過領域模型的方式來描述。每一個重要的系統操作都對應著架構層面的一個重大場景,是架構中需要詳細描述和特別考慮的地方。現在我們來看看如何定義應用程序的微服務架構
系統操作被定義后,下一步就是完成應用服務的識別。如之前提到的,這并不是一個機械化的流程,相反,有多種拆分策略可供選擇。每一種都是從一個側面來解決問題,并且使用它們獨有的一些術語。但是殊途同歸,這些策略的結果都是一樣的:一個包含若干服務的架構,這樣的架構是以業務而不是技術概念為中心
根據業務能力進行服務拆分
創建微服務架構的策略之一就是采用業務能力進行服務拆分。業務能力是一個來自于業務架構建模的術語。業務能力是指一些能夠為公司(或組織)產生價值的商業活動。特定業務的業務能力取決于這個業務的類型。例如,保險公司業務能力通常包括承保、理賠管理、賬務和合規等。在線商店的業務能力包括:訂單管理、庫存管理和發貨
組織的業務能力通常是指這個組織的業務是做什么,它們通常都是穩定的。與之相反,組織采用何種方式來實現它的業務能力,是隨著時間不斷變化的。這個準則在今天尤其明顯,很多新技術在被快速采用,商業流程的自動化程度越來越高。例如,不久之前你還通過把支票交給銀行柜員的方式來兌現支票,現在很多 ATM 機都支持直接兌現支票,而今,人們甚至可以使用智能手機拍照的方式來兌現支票。正如你所見,“兌現支票”這個業務能力是穩定不變的,但是這個能力的實現方式正在發生戲劇性的變化
從業務能力到服務
一旦確定了業務能力,就可以為每個能力或相關能力組定義服務。下圖顯示了 FTGO應用程序從能力到服務的映射。某些頂級能力(如會計記賬能力)將映射到服務。在其他情況下,子能力映射到服務
上圖顯示的服務僅僅是定義架構的第一次嘗試。隨著我們對應用程序領域的了解越來越多,它們可能會隨著時間的推移而變化,特別是架構定義流程中的一個重要步驟是調查服務如何在每個關鍵架構服務中協作。例如,你可能會發現由于過多的進程間通信而導致特定的分解效率低下,導致你必須把一些服務組合在一起。相反,服務可能會在復雜性方面增長到值得將其拆分為多個服務的程度
根據子域進行服務拆分
領域驅動設計是構建復雜軟件的方法論,這些軟件通常都以面向對象和領域模型為核心。領域模型以解決具體問題的方式包含了一個領域內的知識。它定義了當前領域相關團隊的詞匯表,DDD 也稱之為通用語言(Ubiquitous language)。領域模型會被緊密地映射到應用的設計和實現環節。在微服務架構的設計層面,DDD 有兩個特別重要的概念,子域和限界上下文
領域驅動為每一個子域定義單獨的領域模型。子域是領域的一部分,領域是 DDD 中用來描述應用程序問題域的一個術語。識別子域的方式跟識別業務能力一樣:分析業務并識別業務的不同專業領域,分析產出的子域定義結果也會跟業務能力非常接近。FTGO 的子域包括:訂單獲取、訂單管理、餐館管理、送餐和會計。正如你所見:這些子域跟我們之前定義的業務能力非常接近。
DDD 把領域模型的邊界稱為限界上下文(bounded context)。限界上下文包括實現這個模型的代碼集合。當使用微服務架構時,每一個限界上下文對應一個或者一組服務。換一種說法,我們可以通過 DDD 的方式定義子域,并把子域對應為每一個服務,這樣就完成了微服務架構的設計工作。圖 2-9 展示了子域和服務之間的映射,每一個子域都有屬于它們自己的領域模型。
DDD 和微服務架構簡直就是天生一對。DDD 的子域和限界上下文的概念,可以很好地跟微服務架構中的服務進行匹配。而且,微服務架構中的自治化團隊負責服務開發的概念,也跟 DDD 中每個領域模型都由一個獨立團隊負責開發的概念吻合。更有趣的是,子域用于它自己的領域模型這個概念,為消除上帝類和優化服務拆分提供了好辦法
上帝類的處理
上帝類是在整個應用程序中使用的全局類。上帝類通常為應用程序的許多不同方面實現業務邏輯。它有大量字段映射到具有許多列的數據庫表。大多數應用程序至少有一個這樣的上帝類。Order 類是 FTGO 應用程序中上帝類的一個很好的例子。這并不奇怪:畢竟 FTGO 的
目的是向客戶提供食品訂單。系統的大多數部分都涉及訂單。如果 FTGO 應用程序具有單個領域模型,則 Order 類將是一個非常大的類。它將具有與應用程序的許多不同部分相對應的狀態和行為。下圖顯示了使用傳統建模技術創建的 Order 類的結構
Order 類具有與訂單處理、餐館訂單管理、送餐和付款相對應的字段及方法。由于一個模型必須描述來自應用程序的不同部分的狀態轉換,因此該類還具有復雜的狀態模型。在目前情況下,這個類的存在使得將代碼分割成服務變得極其困難
一種解決方案是將 Order 類打包到庫中并創建一個中央 Order 數據庫。處理訂單的所有服務都使用此庫并訪問訪問數據庫。這種方法的問題在于它違反了微服務架構的一個關鍵原則,并導致我們特別不愿意看到的緊耦合。例如,對 Order 模式的任何更改都要求其他開發團隊同步更新和重新編譯他們的代碼。
另一種解決方案是將 Order 數據庫封裝在 Order Service 中,該服務由其他服務調用以檢索和更新訂單。該設計的問題在于這樣的一個 Order Service 將成為一個純數據服務,成為包含很少或沒有業務邏輯的貧血領域模型(anemic domain model)。這兩種解決方案都沒有吸引力,但幸運的是,DDD 提供了一個好的解決方案。
更好的方法是應用 DDD 并將每個服務視為具有自己的領域模型的單獨子域。這意味著FTGO 應用程序中與訂單有關的每個服務都有自己的領域模型及其對應的 Order 類的版本。Delivery Service 是多領域模型的一個很好的例子。如圖 2-11 所示為 Order,它非常簡單:取餐地址、取餐時間、送餐地址和送餐時間。此外,Delivery Service 使用更合適的 Delivery 名稱,而不是稱之為 Order。Delivery Service 對訂單的任何其他屬性不感興趣
Kitchen Service 有一個更簡單的訂單視圖。它的 Order 版本就是一個 Ticket(后 廚 工 單)。如圖所 示,Ticket 只包含 status、requestedDeliveryTime、prepareByTime 以及告訴餐館準備的訂單項列表。它不關心消費者、付款、交付等這些與它無關的事情
Order Service 具有最復雜的訂單視圖,如圖所示。即使它有相當多的字段和方法,它仍然比原始版本的那個 Order 上帝類簡單得多。
每個領域模型中的 Order 類表示同一 Order 業務實體的不同方面。FTGO 應用程序必須維持不同服務中這些不同對象之間的一致性。例如,一旦 Order Service 授權消費者的信用卡,它必須觸發在 Kitchen Service 中創建 Ticket。同樣,如果 Kitchen Service 拒絕訂單,則必須在 Order Service 中取消訂單,并且為客戶退款。我們通常會用用分布式事務去處理這些問題,這又是微服務架構的另一個問題了。
以上就是小編整理的微服務架構設計模式,有哪里不準確的,請各位朋友多多指出,小編和大家一起共同學習進步~~~