一、實踐中的微服務:從架構到部署
在本文中,我計劃介紹微服務架構(MSA)的關鍵架構概念,以及如何在實踐中使用這些架構原理。
微服務是軟體體系結構領域最流行的流行語之一。 關於微服務的基礎知識和好處的學習材料很多,但是關於如何在現實的企業場景中使用微服務的資源很少。
在本文中,我將介紹微服務架構(MSA)的關鍵架構概念,以及如何在實踐中使用這些架構原理。
整體架構
企業軟體應用程式旨在滿足眾多業務需求;給定的軟體應用程式提供數百種功能,所有這些功能都堆積在單個整體應用程式中。例如,ERP,CRM和其他各種軟體系統被構建為具有數百種功能的整體。如此龐大的軟體應用程式的部署,故障排除,擴充套件和升級是一場噩夢。
面向服務的體系結構(SOA)旨在通過引入服務的概念,應用程式提供的相似功能的聚集和分組來克服某些上述限制。使用SOA,可以將軟體應用程式設計為粗粒度服務的組合。但是,在SOA中,服務範圍非常廣泛。這導致具有數十種操作(功能)的複雜而龐大的服務,以及複雜的訊息格式和標準(例如:所有WS *標準)。
整體架構
在大多數情況下,SOA中的服務彼此獨立。 但是它們與所有其他服務一起部署在同一執行時中(考慮一下將多個Web應用程式部署到同一Tomcat例項中)。 與單片軟體應用程式相似,這些服務具有通過累積各種功能隨著時間而增長的習慣。 從字面上看,這將這些應用程式變成了與通用整體應用程式(如ERP)沒有區別的整體結構。 該圖顯示了包含多個服務的零售軟體應用程式。 所有這些服務都部署到同一應用程式執行時中。 因此,這是一個整體架構的很好的例子。 這是基於整體架構的應用程式的一些特徵。
- ·整體應用程式是作為一個單元進行設計,開發和部署的。
- ·整體應用極為複雜; 這導致維護,升級和新增新功能的噩夢。
- ·很難使用Monolithic體系結構來實踐敏捷開發和交付方法。
- ·需要重新部署整個應用程式以更新其中的一部分。
- ·該應用程式必須按單個單元進行擴充套件,從而難以管理相互衝突的資源需求(例如,一項服務需要更多的CPU,而另一項則需要更多的記憶體)
- ·一項不穩定的服務可能會導致整個應用程式崩潰。
- ·採用新技術和框架真的很困難,因為所有功能都必須基於同類技術/框架。
微服務架構
微服務架構(MSA)的基礎是將單個應用程式開發為一組小型獨立服務,這些獨立服務在其自己的流程中執行,獨立開發和部署。
在微服務架構的大多數定義中,將其解釋為將整體中可用的服務隔離為一組獨立服務的過程。但是,在我看來,微服務不僅僅是將整體中可用的服務拆分為獨立的服務。
關鍵思想是,通過檢視整體提供的功能,我們可以確定所需的業務能力。然後,可以將那些業務功能實現為完全獨立,細粒度且自包含的(微)服務。它們可能在不同的技術堆疊之上實現,並且每種服務都針對非常特定且有限的業務範圍。
因此,我們上面解釋的線上零售系統場景可以通過微服務架構實現,如下圖所示。通過微服務體系結構,零售軟體應用程式被實現為一組微服務。因此,正如你在下面看到的那樣,根據業務需求,還有一個整體服務是根據整體中存在的原始服務集建立的。因此,很明顯,使用微服務體系結構是超出了整體中服務拆分的範圍。
微服務架構
讓我們深入研究微服務的關鍵體系結構原理,更重要的是,讓我們集中討論如何在實踐中使用它們。
設計微服務:大小,範圍和功能
你可能正在使用Microservices Architecture從頭開始構建軟體應用程式,或者可能將現有的應用程式/服務轉換為微服務。 無論哪種方式,正確決定微服務的大小,範圍和功能都是非常重要的。 這可能是你在實踐中實現微服務架構時最初遇到的最困難的事情。
讓我們討論與微服務的大小,範圍和功能有關的一些關鍵的實際問題和誤解。
- ·程式碼行/團隊規模是糟糕的指標:關於基於實現的程式碼行或團隊規模(即兩個比薩餅團隊)確定微服務規模的討論很多。 但是,這些指標被認為是非常不切實際和糟糕的指標,因為我們仍然可以使用更少的程式碼/具有兩個比薩餅的團隊來開發服務,但完全違反了微服務架構的原則。
- ·“微”是一個誤導性術語:大多數開發人員傾向於認為他們應該嘗試使服務儘可能小。 這是一個誤解。
- ·SOA上下文:在SOA上下文中,服務通常實現為整體,並支援數十種操作/功能。 因此,擁有類似於SOA的服務並將其重新命名為微服務並不會給你帶來微服務架構的任何好處。
那麼,我們應該如何在微服務架構中正確設計服務?
設計微服務準則
- ·“單一責任原則”(SRP):微服務的業務範圍有限且重點突出,可以幫助我們滿足服務開發和交付中的敏捷性。
- ·在微服務的設計階段,我們應該找到它們的邊界並將其與業務功能(在域驅動設計中也稱為受限上下文)保持一致。
- ·確保微服務設計可確保服務的敏捷/獨立開發和部署。
- ·我們的重點應該放在微服務的範圍上,而不是縮小服務範圍。 服務的(正確)大小應該是促進給定業務能力所需的大小。
- ·與SOA中的服務不同,給定的微服務應具有很少的操作/功能和簡單的訊息格式。
- ·通常,從較長的服務邊界開始,然後隨著時間的流逝而重構為較小的服務邊界(根據業務需求)通常是一個好習慣。
在我們的零售用例中,你可以發現我們已經將整體的功能分為四個不同的微服務,即“庫存”,“會計”,“運輸”和“商店”。 他們正在解決一個有限但集中的業務範圍,以便每個服務彼此完全分離,並確保開發和部署的敏捷性。
微服務中的訊息傳遞
在單片應用程式中,使用功能呼叫或語言級方法呼叫來呼叫不同處理器/元件的業務功能。 在SOA中,這已轉向更加鬆散耦合的Web服務級別訊息傳遞,該訊息傳遞主要基於SOAP並基於HTTP,JMS等不同協議。 具有數十種操作和複雜訊息模式的Web服務是Web服務普及的主要阻力。 對於微服務架構,要求具有簡單輕量的訊息傳遞機制。
同步訊息傳遞-REST,節流
對於Microservices Architecture中的同步訊息傳遞(客戶端希望服務及時響應並等待它獲得響應),REST是一致的選擇,因為它提供了一種簡單的訊息傳遞樣式,該訊息傳遞樣式基於資源API樣式通過HTTP請求響應實現。 因此,大多數微服務實現都使用HTTP以及基於資源API的樣式(每種功能都由一種資源表示,並且在這些資源之上執行操作)。
使用REST介面公開微服務
使用Thrift(你可以在其中定義微服務的介面定義),作為REST / HTTP同步訊息傳遞的替代方法。
非同步訊息傳遞-AMQP,STOMP,MQTT
對於某些微服務場景,要求使用非同步訊息傳遞技術(客戶端不希望立即響應,或者根本不接受響應)。 在這種情況下,非同步訊息傳遞協議如AMQP,STOMP或MQTT被廣泛使用。
訊息格式-JSON,XML,Thrift,ProtoBuf,Avro
roservices是另一個關鍵因素。 傳統的單片應用程式使用複雜的二進位制格式,基於SOA / Web服務的應用程式使用基於複雜訊息格式(SOAP)和架構(xsd)的文字訊息。 在大多數基於微服務的應用程式中,它們使用簡單的基於文字的訊息格式,例如HTTP資源API樣式之上的JSON和XML。 在我們需要二進位制訊息格式的情況下(文字訊息在某些用例中可能變得冗長),微服務可以利用二進位制訊息格式,例如二進位制Thrift,ProtoBuf或Avro。
服務合同-定義服務介面-Swagger,RAML,節儉
當你將業務功能實現為服務時,你需要定義和釋出服務合同。 在傳統的整體應用程式中,我們幾乎找不到用於定義應用程式業務功能的功能。 在SOA / Web服務世界中,WSDL用於定義服務協定,但是,眾所周知,WSDL並不是複雜的且與SOAP緊密耦合,因此不是定義微服務協定的理想解決方案。
由於我們是基於REST架構樣式構建微服務的,因此我們可以使用相同的REST API定義技術來定義微服務的契約。 因此,微服務使用標準的REST API定義語言(例如Swagger和RAML)來定義服務合同。
對於其他不基於HTTP / REST的微服務實現(例如Thrift),我們可以使用協議級別的介面定義語言(IDL)(例如:Thrift IDL)。
二、整合微服務(服務間/流程通訊)
在微服務體系結構中,軟體應用程式被構建為一套獨立的服務。因此,為了實現業務用例,需要在不同的微服務/流程之間具有通訊結構。這就是微服務之間的服務間/流程通訊如此重要的原因。
在SOA實現中,使用企業服務匯流排(ESB)可以促進服務之間的服務間通訊,並且大多數業務邏輯位於中間層(訊息路由,轉換和編排)。但是,微服務體系結構促進消除中央訊息匯流排/ ESB,並將“智慧性”或業務邏輯轉移到服務和客戶端(稱為智慧端點)。
由於微服務使用諸如HTTP,JSON等標準協議,因此在微服務之間進行通訊時,與不同協議整合的要求極小。微服務通訊中的另一種替代方法是使用輕量級訊息匯流排或閘道器,路由功能最少,並充當``啞管道'',而閘道器上未實現任何業務邏輯。基於這些樣式,微服務體系結構中出現了幾種通訊模式。
點對點樣式-直接呼叫服務
在點對點樣式中,整個訊息路由邏輯都駐留在每個端點上,並且服務可以直接進行通訊。 每個微服務都公開一個REST API,給定的微服務或外部客戶端可以通過其REST API呼叫另一個微服務。
具有點對點連線的服務間通訊
顯然,該模型適用於相對簡單的基於微服務的應用程式,但是隨著服務數量的增加,這將變得極為複雜。 畢竟,這就是在傳統SOA實現中使用ESB的確切原因:擺脫凌亂的點對點整合連結。 讓我們嘗試總結微服務通訊的點對點樣式的主要缺點。
- ·必須在每個微服務級別實現終端使用者身份驗證,限制,監視等非功能性要求。
- ·由於複製了通用功能,因此每個微服務實現都可能變得複雜。
- ·服務與客戶端之間的所有通訊都無法控制(即使用於監視,跟蹤或篩選)
- ·對於大規模微服務實現,通常將直接通訊樣式視為微服務反模式。
因此,對於複雜的微服務用例,我們可以使用輕量級的中央訊息傳遞匯流排,而不是使用點對點連線或中央ESB,它可以為微服務提供抽象層,並可以用於實現各種非功能性 能力。 此樣式稱為API閘道器樣式。
API閘道器樣式
API閘道器樣式背後的關鍵思想是使用輕量級訊息閘道器作為所有客戶端/消費者的主要入口點,並在閘道器級別實現常見的非功能性要求。 通常,API閘道器允許你通過REST / HTTP使用託管API。 因此,在這裡,我們可以將通過API-GW作為託管服務實現為微服務的業務功能公開。 實際上,這是微服務架構和API管理的結合,可為你提供兩全其美的體驗。
所有微服務都通過API-GW公開在我們的零售業務場景中,如上圖所示,所有微服務都通過API-GW公開,這是所有客戶端的單一入口點。 如果微服務想要使用另一個微服務,則也需要通過API-GW來完成。
API-GW樣式具有以下優點:
- ·能夠在閘道器級別為現有微服務提供所需的抽象。 例如,API閘道器可以提供每個客戶端不同的API,而不是提供一種千篇一律的樣式API。
- ·閘道器級別的輕量級訊息路由/轉換。
- ·應用非功能性功能(例如安全性,監視和限制)的中心位置。
- ·通過使用API-GW模式,由於所有非功能性需求都在閘道器級別實現,因此微服務變得更加輕量級。
API-GW樣式很可能是大多數微服務實現中使用最廣泛的模式。
訊息代理樣式
微服務可以與非同步訊息傳遞場景整合,例如單向請求和使用佇列或主題的釋出-訂閱訊息傳遞。 給定的微服務可以是訊息生產者,並且可以非同步將訊息傳送到佇列或主題。 然後,消費微服務可以消費來自佇列或主題的訊息。 這種樣式使訊息生產者與訊息使用者分離,中間訊息代理將緩衝訊息,直到使用者能夠處理它們為止。 生產者微服務完全不瞭解消費者微服務。
使用pub-sub的基於非同步訊息傳遞的整合
消費者/生產者之間的通訊通過基於非同步訊息傳遞標準(例如AMQP,MQTT等)的訊息代理來促進。
分散資料管理
在整體架構中,應用程式將資料儲存在單個集中式資料庫中,以實現應用程式的各種功能。
單一應用程式使用中央資料庫來實現其所有功能。在微服務體系結構中,功能分散在多個微服務之間,如果我們使用相同的中央資料庫,則微服務將不再彼此獨立(例如,如果 資料庫架構已從給定的微服務更改,這將破壞其他幾個服務)。 因此,每個微服務都必須具有自己的資料庫。
每個微服務都有自己的私有資料庫
這是在微服務架構中實施分散資料管理的關鍵方面。
- ·每個微服務都可以有一個私有資料庫來保留實現其提供的業務功能所需的資料。
- ·給定的微服務只能訪問專用私有資料庫,而不能訪問其他微服務的資料庫。
- ·在某些業務場景中,你可能必須為單個事務更新多個資料庫。 在這種情況下,其他微服務的資料庫應僅通過其服務API進行更新(不允許直接訪問該資料庫)
分散的資料管理為你提供了完全解耦的微服務,並且可以自由選擇不同的資料管理技術(SQL或NoSQL等,每種服務使用不同的資料庫管理系統)。 但是,對於涉及多個微服務的複雜事務用例,必須使用每個服務提供的API來實現事務行為,並且邏輯位於客戶端或中介(GW)級別。
分散治理
微服務架構支援分散式治理。
- 通常,“治理”是指建立和加強人員和解決方案如何共同協作以實現組織目標。 在SOA的背景下,SOA治理指導可重用服務的開發,確定如何設計和開發服務以及這些服務將隨著時間的變化而變化。 它在服務的提供者和這些服務的消費者之間建立協議,告訴消費者他們可以期望什麼以及提供者他們有義務提供什麼。 在SOA治理中,有兩種常用的治理型別:
那麼,微服務環境中的治理到底意味著什麼? 在微服務架構中,微服務被構建為具有各種技術和平臺的完全獨立和解耦的服務。 因此,無需為服務設計和開發定義通用標準。 因此,我們可以將微服務的去中心化治理功能總結如下:
- ·在微服務架構中,不需要進行集中的設計時治理。
- ·微服務可以自行決定其設計和實現。
- ·微服務架構促進了公共/可重用服務的共享。
- ·某些執行時治理方面,例如SLA,節流,監視,通用安全要求和服務發現,可以在API-GW級別上實現。
三、服務註冊和服務發現
在微服務架構中,你需要處理的微服務數量非常多。而且,由於微服務的快速和敏捷開發/部署性質,它們的位置會動態變化。因此,你需要在執行時找到微服務的位置。解決此問題的方法是使用服務登錄檔。
服務註冊
服務登錄檔包含微服務例項及其位置。微服務例項在啟動時在服務登錄檔中註冊,並在關機時登出。消費者可以通過服務登錄檔找到可用的微服務及其位置。
服務發現
為了找到可用的微服務及其位置,我們需要一種服務發現機制。服務發現機制有兩種型別,客戶端發現和伺服器端發現。讓我們仔細看看那些服務發現機制。
客戶端發現-通過這種方法,客戶端或API-GW通過查詢服務登錄檔來獲取服務例項的位置。
在微服務架構中,你需要處理的微服務數量非常多。 而且,由於微服務的快速和敏捷開發/部署性質,它們的位置會動態變化。 因此,你需要在執行時找到微服務的位置。 解決此問題的方法是使用服務登錄檔。
服務註冊
服務登錄檔包含微服務例項及其位置。 微服務例項在啟動時在服務登錄檔中註冊,並在關機時登出。 消費者可以通過服務登錄檔找到可用的微服務及其位置。
服務發現
為了找到可用的微服務及其位置,我們需要一種服務發現機制。 服務發現機制有兩種型別,客戶端發現和伺服器端發現。 讓我們仔細看看那些服務發現機制。
客戶端發現-通過這種方法,客戶端或API-GW通過查詢服務登錄檔來獲取服務例項的位置。
客戶端發現
伺服器端發現-使用這種方法,客戶端/ API-GW將請求傳送到在已知位置執行的元件(例如負載均衡器)。 該元件呼叫服務登錄檔並確定微服務的絕對位置。
客戶端發現
諸如Kubernetes(http://kubernetes.io/v1.1/docs/user-guide/services.html)之類的微服務部署解決方案提供了服務端發現機制。
部署方式
在微服務架構方面,微服務的部署起著至關重要的作用,並且具有以下關鍵要求:
- 能夠獨立於其他微服務進行部署/取消部署。
- 必須能夠在每個微服務級別進行擴充套件(給定的服務可能比其他服務獲得更多的流量)。
- 快速構建和部署微服務。
- 一個微服務中的故障不得影響任何其他服務。
Docker(一種開放原始碼引擎,可讓開發人員和系統管理員在Linux環境中部署自給自足的應用程式容器)提供了一種很好的方式來部署微服務以滿足上述要求。 涉及的關鍵步驟如下:
- ·將微服務打包為(Docker)容器映像。
- ·將每個服務例項部署為一個容器。
- ·縮放是根據更改容器例項的數量完成的。
- ·隨著我們使用Docker容器,構建,部署和啟動微服務的速度將大大提高(比常規VM快得多)
Kubernetes通過允許將Linux容器叢集作為一個系統進行管理,跨多個主機管理和執行Docker容器,提供容器的共置,服務發現和複製控制功能,擴充套件了Docker的功能。 如你所見,大多數這些功能在我們的微服務環境中也是必不可少的。 因此,使用Kubernetes(在Docker之上)進行微服務部署已成為一種極其強大的方法,尤其是對於大規模微服務部署。
將微服務構建和部署為容器。
在上圖中,它顯示了零售應用程式的微服務部署的概述。 每個微服務例項都作為一個容器部署,每個主機有兩個容器。 你可以任意更改在給定主機上執行的容器的數量。
安全
在實際場景中使用微服務時,保護微服務是非常普遍的要求。 在進入微服務安全性之前,讓我們快速看一下我們通常如何在整體應用程式級別上實現安全性。
- 在典型的整體應用程式中,安全性是要找到“誰是呼叫者”,“呼叫者能做什麼”和“我們如何傳播該資訊”。
- 這通常是在請求處理鏈的開始處的通用安全元件上實現的,並且該元件使用基礎使用者儲存庫(或使用者儲存)填充所需的資訊。
那麼,我們可以直接將此模式轉換為微服務架構嗎? 是的,但這需要在每個微服務級別實現的安全元件,該元件正在與集中式/共享使用者儲存庫進行對話並檢索所需的資訊。 這是解決微服務安全性問題的非常乏味的方法。 相反,我們可以利用廣泛使用的API安全標準(例如OAuth2和OpenID Connect)為我們的微服務安全問題找到更好的解決方案。 在深入探討這一點之前,讓我概述一下每個標準的目的以及如何使用它們
- ·OAuth2-是訪問委派協議。 客戶端通過授權伺服器進行身份驗證,並獲得一個不透明的令牌,稱為“訪問令牌”。 訪問令牌的使用者/客戶端資訊為零。 它僅具有對只能由授權伺服器檢索的使用者資訊的引用。 因此,這被稱為“參考令牌”,即使在公共網路/網際網路中也可以安全地使用此令牌。
·OpenIDConnect的行為類似於OAuth,但除了訪問令牌外,授權伺服器還會釋出一個ID令牌,其中包含有關使用者的資訊。 這通常由JWT(JSON Web令牌)實現,並由授權伺服器簽名。 因此,這確保了授權伺服器和客戶端之間的信任。 因此,JWT令牌被稱為“按值令牌”,因為它包含使用者的資訊,並且顯然在內部網路之外使用它是不安全的。
具有OAuth2和OpenID Connect的微服務安全性
如上圖所示,這些是實現微服務安全性涉及的關鍵步驟:
- ·將身份驗證留給OAuth和OpenID Connect伺服器(授權伺服器),以便在有人有權使用資料的情況下,微服務成功提供訪問許可權。
- ·使用API-GW樣式,在該樣式中,所有客戶端請求都有一個入口點。
- 客戶端連線到授權伺服器並獲得訪問令牌(按引用令牌)。然後將訪問令牌與請求一起傳送到API-GW。
- 閘道器上的令牌轉換-API-GW提取訪問令牌並將其傳送到授權伺服器以檢索JWT(按值令牌)。
- ·然後,GW將這個JWT與請求一起傳遞給微服務層。
- ·JWT包含必要的資訊,以幫助儲存使用者會話等。如果每個服務都可以理解JSON Web令牌,則你已經分發了身份識別機制,該機制允許你在整個系統中傳輸身份。
- 在每個微服務層,我們可以有一個處理JWT的元件,這是一個非常簡單的實現。
交易次數
微服務中的交易支援如何?實際上,支援跨多個微服務的分散式事務是非常複雜的任務。微服務架構本身鼓勵服務之間的無事務協調。
想法是,給定的服務是完全獨立的,並且基於單一責任原則。跨多個微服務進行分散式事務的需求通常是微服務體系結構中設計缺陷的徵兆,通常可以通過重構微服務的範圍來解決。但是,如果必須在多個服務之間分配事務,則可以通過在每個微服務級別引入“補償操作”來實現這種情況。關鍵思想是,給定的微服務基於單一職責原則,如果給定的微服務無法執行給定的操作,我們可以認為這是整個微服務的失敗。然後,必須通過呼叫這些微服務的相應補償操作來撤消所有其他(上游)操作。
失敗的設計
微服務架構引入了一組分散的服務,並且與單片設計相比,增加了在每個服務級別出現故障的可能性。 給定的微服務可能會由於網路問題,基礎資源的不可用等原因而失敗。不可用或無響應的微服務不應導致整個基於微服務的應用程式崩潰。 因此,微服務應該是容錯的,能夠在可能的情況下恢復,並且客戶端必須妥善處理它。
另外,由於服務隨時可能發生故障,因此能夠快速檢測(實時監視)故障並在可能的情況下自動恢復服務也很重要。
在微服務上下文中,有幾種常用的模式來處理錯誤。context.
斷路器
當你對微服務進行外部呼叫時,你需要為每次呼叫配置一個故障監視器元件,並且當故障達到某個閾值時,該元件將停止對該服務的任何進一步呼叫(使電路跳閘)。 在一定數量的請求處於開啟狀態(你可以配置)之後,將電路更改回關閉狀態。
這種模式非常有用,可避免不必要的資源消耗,由於超時而導致的請求延遲,並且還使我們有機會監視系統(基於活動的開路狀態)。
隔板
由於微服務應用程式包含微服務的數量,因此基於微服務的應用程式一部分的故障不應影響其餘的應用程式。 隔板模式是關於隔離應用程式的不同部分的,因此應用程式中服務的故障不會影響任何其他服務。
暫停
超時模式是一種機制,當你認為微服務不會響應時,它可以讓你停止等待微服務的響應。 你可以在此處配置希望等待的時間間隔。
那麼,我們在哪裡以及如何在微服務中使用這些模式? 在大多數情況下,這些模式中的大多數都適用於閘道器級別。 這意味著當微服務不可用或沒有響應時,在閘道器級別,我們可以決定是否使用斷路器或超時模式將請求傳送到微服務。 同樣,在閘道器級別實現諸如隔板等模式也是非常重要的,因為它是所有客戶端請求的單個入口點,因此贈與服務的失敗不應影響其他微服務的呼叫。
另外,閘道器可以用作中心點,當通過閘道器呼叫每個微服務時,我們可以獲得狀態並監視每個微服務。
微服務,企業整合,API管理等
我們已經討論了微服務架構的各種特徵,以及如何在現代企業IT環境中實現它們。但是,我們應該記住,微服務不是萬能藥。流行語概念的盲目適應將無法解決你的“實際”企業IT問題。正如你在整個部落格文章中所看到的那樣,微服務具有很多優勢,我們應該加以利用。但是,我們還必須記住,用微服務解決所有企業IT問題是不現實的。例如,微服務架構促進了消除ESB作為中央匯流排的發展,但是在現實世界中,有很多不基於微服務的現有應用程式/服務。因此,要與它們整合,我們需要某種整合匯流排。因此,理想情況下,微服務和其他企業體系結構概念(例如整合)的混合方法將更為現實。
以上內容借鑑於其他前輩和自己的一些簡單理解。