1. 程式人生 > >微服務實戰系列文章

微服務實戰系列文章

【編者的話】本文來自Nginx官方部落格,是微服務系列文章的第一篇,主要探討了傳統的單體式應用的不足,以及微服務架構的優勢與挑戰。正如作者所說,微服務架構更適合用於構建複雜的應用,儘管它也有自己的不足。

這篇文章作者是Chris Richardson,他是早期基於Java的Amazonite EC2 PaaS平臺CloudFoundry.com的創始人。現在他為企業提供如何開發和部署應用的諮詢服務。他也經常在http://microservices.io上發表有關微服務的文章。

微服務正在部落格、社交媒體討論組和會議演講中獲得越來越多的關注,在Gartner的2014 Hype Cycle上它的排名非常靠前。同時,軟體社群中也有不少持懷疑論者,認為微服務不是什麼新東西。Naysayers認為這就是SOA架構的重新包裝。然而,儘管存在著不同的爭論,微服務架構模式卻正在為敏捷部署以及複雜企業應用實施提供巨大的幫助。

這篇部落格是關於如何設計、開發和部署微服務的七篇系列文章中的第一篇。
讀者將會從中學到方法,並且和單體式架構模式(譯者注:本文中會將 Monolithic翻譯為單體)進行對比。這一系列文章將描述微服務架構中不同元素。你將瞭解到微服務架構模式的優缺點,以便決定是否更好的將微服務架構應用到自己的專案中,以及如何應用這一模式。

首先我們看看為什麼要考慮使用微服務。

開發單體式應用

假設你正準備開發一款與Uber和Hailo競爭的計程車排程軟體,經過初步會議和需求分析,你可能會手動或者使用基於Rails、Spring Boot、Play或者Maven的生成器開始這個新專案,它的六邊形架構是模組化的 ,架構圖如下:
1.png
應用核心是業務邏輯,由定義服務、域物件和事件的模組完成。圍繞著核心的是與外界打交道的介面卡。介面卡包括資料庫訪問元件、生產和處理訊息的訊息元件,以及提供API或者UI訪問支援的web模組等。

儘管也是模組化邏輯,但是最終它還是會打包並部署為單體式應用。具體的格式依賴於應用語言和框架。例如,許多Java應用會被打包為WAR格式,部署在Tomcat或者Jetty上,而另外一些Java應用會被打包成自包含的JAR格式,同樣,Rails和Node.js會被打包成層級目錄。

這種應用開發風格很常見,因為IDE和其它工具都擅長開發一個簡單應用,這類應用也很易於除錯,只需要簡單執行此應用,用Selenium連結UI就可以完成端到端測試。單體式應用也易於部署,只需要把打包應用拷貝到伺服器端,通過在負載均衡器後端執行多個拷貝就可以輕鬆實現應用擴充套件。在早期這類應用執行的很好。

單體式應用的不足

不幸的是,這種簡單方法卻有很大的侷限性。一個簡單的應用會隨著時間推移逐漸變大。在每次的sprint中,開發團隊都會面對新“故事”,然後開發許多新程式碼。幾年後,這個小而簡單的應用會變成了一個巨大的怪物。這兒有一個例子,我最近和一個開發者討論,他正在寫一個工具,用來分析他們一個擁有數百萬行程式碼的應用中JAR檔案之間的依賴關係。我很確信這個程式碼正是很多開發者經過多年努力開發出來的一個怪物。

一旦你的應用變成一個又大又複雜的怪物,那開發團隊肯定很痛苦。敏捷開發和部署舉步維艱,其中最主要問題就是這個應用太複雜,以至於任何單個開發者都不可能搞懂它。因此,修正bug和正確的新增新功能變的非常困難,並且很耗時。另外,團隊士氣也會走下坡路。如果程式碼難於理解,就不可能被正確的修改。最終會走向巨大的、不可理解的泥潭。

單體式應用也會降低開發速度。應用越大,啟動時間會越長。比如,最近的一個調查表明,有時候應用的啟動時間居然超過了12分鐘。我還聽說某些應用需要40分鐘啟動時間。如果開發者需要經常重啟應用,那麼大部分時間就要在等待中渡過,生產效率受到極大影響。

另外,複雜而巨大的單體式應用也不利於持續性開發。今天,SaaS應用常態就是每天會改變很多次,而這對於單體式應用模式非常困難。另外,這種變化帶來的影響並沒有很好的被理解,所以不得不做很多手工測試。那麼接下來,持續部署也會很艱難。

單體式應用在不同模組發生資源衝突時,擴充套件將會非常困難。比如,一個模組完成一個CPU敏感邏輯,應該部署在AWS EC2 Compute Optimized instances,而另外一個記憶體資料庫模組更合適於EC2 Memory-optimized instances。然而,由於這些模組部署在一起,因此不得不在硬體選擇上做一個妥協。

單體式應用另外一個問題是可靠性。因為所有模組都執行在一個程序中,任何一個模組中的一個bug,比如記憶體洩露,將會有可能弄垮整個程序。除此之外,因為所有應用例項都是唯一的,這個bug將會影響到整個應用的可靠性。

最後,單體式應用使得采用新架構和語言非常困難。比如,設想你有兩百萬行採用XYZ框架寫的程式碼。如果想改成ABC框架,無論是時間還是成本都是非常昂貴的,即使ABC框架更好。因此,這是一個無法逾越的鴻溝。你不得不在最初選擇面前低頭。

總結一下:一開始你有一個很成功的關鍵業務應用,後來就變成了一個巨大的,無法理解的怪物。因為採用過時的,效率低的技術,使得僱傭有潛力的開發者很困難。應用無法擴充套件,可靠性很低,最終,敏捷性開發和部署變的無法完成。

那麼如何應對呢?

微處理架構——處理複雜事物

許多公司,比如Amazon、eBay和NetFlix,通過採用微處理結構模式解決了上述問題。其思路不是開發一個巨大的單體式的應用,而是將應用分解為小的、互相連線的微服務。

一個微服務一般完成某個特定的功能,比如下單管理、客戶管理等等。每一個微服務都是微型六角形應用,都有自己的業務邏輯和介面卡。一些微服務還會發布API給其它微服務和應用客戶端使用。其它微服務完成一個Web UI,執行時,每一個例項可能是一個雲VM或者是Docker容器。

比如,一個前面描述系統可能的分解如下:
2.png
每一個應用功能區都使用微服務完成,另外,Web應用會被拆分成一系列簡單的Web應用(比如一個對乘客,一個對計程車駕駛員)。這樣的拆分對於不同使用者、裝置和特殊應用場景部署都更容易。

每一個後臺服務開放一個REST API,許多服務本身也採用了其它服務提供的API。比如,駕駛員管理使用了告知駕駛員一個潛在需求的通知服務。UI服務啟用其它服務來更新Web頁面。所有服務都是採用非同步的,基於訊息的通訊。微服務內部機制將會在後續系列中討論。

一些REST API也對乘客和駕駛員採用的移動應用開放。這些應用並不直接訪問後臺服務,而是通過API Gateway來傳遞中間訊息。API Gateway負責負載均衡、快取、訪問控制、API 計費監控等等任務,可以通過NGINX方便實現,後續文章將會介紹到API Gateway。
3.png
微服務架構模式在上圖中對應於代表可擴充套件Scale Cube的Y軸,這是一個在《The Art of Scalability》書中描述過的三維擴充套件模型。另外兩個可擴充套件軸,X軸由負載均衡器後端執行的多個應用副本組成,Z軸是將需求路由到相關服務。

應用基本可以用以上三個維度來表示,Y軸代表將應用分解為微服務。執行時,X軸代表執行多個隱藏在負載均衡器之後的例項,提供吞吐能力。一些應用可能還是用Z軸將服務分割槽。下面的圖演示行程管理服務如何部署在運行於AWS EC2上的Docker上。
4.png
執行時,行程管理服務由多個服務例項構成。每一個服務例項都是一個Docker容器。為了保證高可用,這些容器一般都執行在多個雲VM上。服務例項前是一層諸如NGINX的負載均衡器,他們負責在各個例項間分發請求。負載均衡器也同時處理其它請求,例如快取、許可權控制、API統計和監控。

這種微服務架構模式深刻影響了應用和資料庫之間的關係,不像傳統多個服務共享一個數據庫,微服務架構每個服務都有自己的資料庫。另外,這種思路也影響到了企業級資料模式。同時,這種模式意味著多份資料,但是,如果你想獲得微服務帶來的好處,每個服務獨有一個數據庫是必須的,因為這種架構需要這種鬆耦合。下面的圖演示示例應用資料庫架構。
5.png
每種服務都有自己的資料庫,另外,每種服務可以用更適合自己的資料庫型別,也被稱作多語言一致性架構。比如,駕駛員管理(發現哪個駕駛員更靠近乘客),必須使用支援地理資訊查詢的資料庫。

表面上看來,微服務架構模式有點像SOA,他們都由多個服務構成。但是,可以從另外一個角度看此問題,微服務架構模式是一個不包含Web服務(WS-)和ESB服務的SOA。微服務應用樂於採用簡單輕量級協議,比如REST,而不是WS-,在微服務內部避免使用ESB以及ESB類似功能。微服務架構模式也拒絕使用canonical schema等SOA概念。

微服務架構的好處

微服務架構模式有很多好處。首先,通過分解巨大單體式應用為多個服務方法解決了複雜性問題。在功能不變的情況下,應用被分解為多個可管理的分支或服務。每個服務都有一個用RPC-或者訊息驅動API定義清楚的邊界。微服務架構模式給採用單體式編碼方式很難實現的功能提供了模組化的解決方案,由此,單個服務很容易開發、理解和維護。

第二,這種架構使得每個服務都可以有專門開發團隊來開發。開發者可以自由選擇開發技術,提供API服務。當然,許多公司試圖避免混亂,只提供某些技術選擇。然後,這種自由意味著開發者不需要被迫使用某專案開始時採用的過時技術,他們可以選擇現在的技術。甚至於,因為服務都是相對簡單,即使用現在技術重寫以前程式碼也不是很困難的事情。

第三,微服務架構模式是每個微服務獨立的部署。開發者不再需要協調其它服務部署對本服務的影響。這種改變可以加快部署速度。UI團隊可以採用AB測試,快速的部署變化。微服務架構模式使得持續化部署成為可能。

最後,微服務架構模式使得每個服務獨立擴充套件。你可以根據每個服務的規模來部署滿足需求的規模。甚至於,你可以使用更適合於服務資源需求的硬體。比如,你可以在EC2 Compute Optimized instances上部署CPU敏感的服務,而在EC2 memory-optimized instances上部署記憶體資料庫。

微服務架構的不足

Fred Brooks在30年前寫道,“there are no silver bullets”,像任何其它科技一樣,微服務架構也有不足。其中一個跟他的名字類似,『微服務』強調了服務大小,實際上,有一些開發者鼓吹建立稍微大一些的,10-100 LOC服務組。儘管小服務更樂於被採用,但是不要忘了這只是終端的選擇而不是最終的目的。微服務的目的是有效的拆分應用,實現敏捷開發和部署。

另外一個主要的不足是,微服務應用是分散式系統,由此會帶來固有的複雜性。開發者需要在RPC或者訊息傳遞之間選擇並完成程序間通訊機制。更甚於,他們必須寫程式碼來處理訊息傳遞中速度過慢或者不可用等區域性失效問題。當然這並不是什麼難事,但相對於單體式應用中通過語言層級的方法或者程序呼叫,微服務下這種技術顯得更復雜一些。

另外一個關於微服務的挑戰來自於分割槽的資料庫架構。商業交易中同時給多個業務分主體更新訊息很普遍。這種交易對於單體式應用來說很容易,因為只有一個數據庫。在微服務架構應用中,需要更新不同服務所使用的不同的資料庫。使用分散式交易並不一定是好的選擇,不僅僅是因為CAP理論,還因為今天高擴充套件性的NoSQL資料庫和訊息傳遞中介軟體並不支援這一需求。最終你不得不使用一個最終一致性的方法,從而對開發者提出了更高的要求和挑戰。

測試一個基於微服務架構的應用也是很複雜的任務。比如,採用流行的Spring Boot架構,對一個單體式web應用,測試它的REST API,是很容易的事情。反過來,同樣的服務測試需要啟動和它有關的所有服務(至少需要這些服務的stubs)。再重申一次,不能低估了採用微服務架構帶來的複雜性。

另外一個挑戰在於,微服務架構模式應用的改變將會波及多個服務。比如,假設你在完成一個案例,需要修改服務A、B、C,而A依賴B,B依賴C。在單體式應用中,你只需要改變相關模組,整合變化,部署就好了。對比之下,微服務架構模式就需要考慮相關改變對不同服務的影響。比如,你需要更新服務C,然後是B,最後才是A,幸運的是,許多改變一般隻影響一個服務,而需要協調多服務的改變很少。

部署一個微服務應用也很複雜,一個分散式應用只需要簡單在複雜均衡器後面部署各自的伺服器就好了。每個應用例項是需要配置諸如資料庫和訊息中介軟體等基礎服務。相對比,一個微服務應用一般由大批服務構成。例如,根據Adrian Cockcroft,Hailo有160個不同服務構成,NetFlix有大約600個服務。每個服務都有多個例項。這就造成許多需要配置、部署、擴充套件和監控的部分,除此之外,你還需要完成一個服務發現機制(後續文章中發表),以用來發現與它通訊服務的地址(包括伺服器地址和埠)。傳統的解決問題辦法不能用於解決這麼複雜的問題。接續而來,成功部署一個微服務應用需要開發者有足夠的控制部署方法,並高度自動化。

一種自動化方法是使用PaaS服務,例如Cloud Foundry。PaaS給開發者提供一個部署和管理微服務的簡單方法,它把所有這些問題都打包內建解決了。同時,配置PaaS的系統和網路專家可以採用最佳實踐和策略來簡化這些問題。另外一個自動部署微服務應用的方法是開發對於你來說最基礎的PaaS系統。一個典型的開始點是使用一個叢集化方案,比如配合Docker使用Mesos或者Kubernetes。後面的系列我們會看看如何基於軟體部署方法例如NGINX,可以方便的在微服務層面提供快取、許可權控制、API統計和監控。

總結

構建複雜的應用真的是非常困難。單體式的架構更適合輕量級的簡單應用。如果你用它來開發複雜應用,那真的會很糟糕。微服務架構模式可以用來構建複雜應用,當然,這種架構模型也有自己的缺點和挑戰。

在後續的部落格中,我會深入探索微服務架構模式,並討論諸如服務發現、服務部署選擇和如何分解一個分散式應用為多個服務的策略。

待續。。。。

微服務實戰(二):使用API Gateway

【編者的話】本系列的第一篇介紹了微服務架構模式。它討論了採用微服務的優點和缺點,除了一些複雜的微服務,這種模式還是複雜應用的理想選擇。

當你決定將應用作為一組微服務時,需要決定應用客戶端如何與微服務互動。在單體式程式中,通常只有一組冗餘的或者負載均衡的服務提供點。在微服務架構中,每一個微服務暴露一組細粒度的服務提供點。在本篇文章中,我們來看它如何影響客戶端到服務端通訊,同時提出一種API Gateway的方法。

介紹

假定你正在為線上購物應用開發一個原生手機客戶端。你需要實現一個產品最終頁來展示商品資訊。

例如,下面的圖展示了你在亞馬遜Android客戶端上滑動產品最終頁時看到的資訊。
01.png
雖然這是一個智慧手機應用,這個產品最終頁展示了非常多的資訊。例如,不僅這裡有產品基本資訊(名字、描述和價格),還有以下內容:
  • 購物車中的物品數
  • 下單歷史
  • 使用者評論
  • 低庫存警告
  • 快遞選項
  • 各式各樣的推薦,包括經常跟這個物品一起被購買的產品、購買該物品的其他顧客購買的產品以及購買該產品的顧客還瀏覽了哪些產品。
  • 可選的購物選項

當採用一個單體式應用架構,一個移動客戶端將會通過一個REST請求(GET api.company.com/productdetails/productId)來獲取這些資料。一個負載均衡將請求分發到多個應用例項之一。應用將查詢各種資料庫並返回請求給客戶端。

相對的,若是採用微服務架構,最終頁上的資料會分佈在不同的微服務上。下面列舉了可能與產品最終頁資料有關的一些微服務:
  • 購物車服務 -- 購物車中的物品數
  • 下單服務 -- 下單歷史
  • 分類服務 -- 基本產品資訊,如名字、圖片和價格
  • 評論服務 -- 使用者評論
  • 庫存服務 -- 低庫存警告
  • 快遞服務 -- 快遞選項、截止時間、來自不同快遞API的成本計算
  • 推薦服務 -- 推薦產品

02.png
我們需要決定移動客戶端如何訪問這些服務。請看下面這幾種方式

客戶端到微服務直接通訊

理論上說,一個客戶端可以直接給多個微服務中的任何一個發起請求。每一個微服務都會有一個對外服務端(https://serviceName.api.company.name)。這個URL可能會對映到微服務的負載均衡上,它再轉發請求到具體節點上。為了搜尋產品細節,移動端需要向上述微服務逐個發請求。

不幸的是,這個方案有很多困難和限制。其中一個問題是客戶端的需求量與每個微服務暴露的細粒度API數量的不匹配。如圖中,客戶端需要7次單獨請求。在更復雜的場景中,可能會需要更多次請求。例如,亞馬遜的產品最終頁要請求數百個微服務。雖然一個客戶端可以通過LAN發起很多個請求,但是在公網上這樣會很沒有效率,這個問題在移動網際網路上尤為突出。這個方案同時會導致客戶端程式碼非常複雜。

另一個存在的問題是客戶端直接請求微服務的協議可能並不是web友好型。一個服務可能是用Thrift的RPC協議,而另一個服務可能是用AMQP訊息協議。它們都不是瀏覽或防火牆友好的,並且最好是內部使用。應用應該在防火牆外採用類似HTTP或者WEBSocket協議。

這個方案的另一個缺點是它很難重構微服務。隨著時間的推移,我們可能需要改變系統微服務目前的切分方案。例如,我們可能需要將兩個服務合併或者將一個服務拆分為多個。但是,如果客戶端直接與微服務互動,那麼這種重構就很難實施。

由於上述三種問題的原因,客戶端直接與伺服器端通訊的方式很少在實際中使用。

採用一個API Gateway

通常來說,一個更好的解決辦法是採用API Gateway的方式。API Gateway是一個伺服器,也可以說是進入系統的唯一節點。這跟面向物件設計模式中的Facade模式很像。API Gateway封裝內部系統的架構,並且提供API給各個客戶端。它還可能有其他功能,如授權、監控、負載均衡、快取、請求分片和管理、靜態響應處理等。下圖展示了一個適應當前架構的API Gateway。
03.png
API Gateway負責請求轉發、合成和協議轉換。所有來自客戶端的請求都要先經過API Gateway,然後路由這些請求到對應的微服務。API Gateway將經常通過呼叫多個微服務來處理一個請求以及聚合多個服務的結果。它可以在web協議與內部使用的非Web友好型協議間進行轉換,如HTTP協議、WebSocket協議。

API Gateway可以提供給客戶端一個定製化的API。它暴露一個粗粒度API給移動客戶端。以產品最終頁這個使用場景為例。API Gateway提供一個服務提供點(/productdetails?productid=xxx)使得移動客戶端可以在一個請求中檢索到產品最終頁的全部資料。API Gateway通過呼叫多個服務來處理這一個請求並返回結果,涉及產品資訊、推薦、評論等。

一個很好的API Gateway例子是Netfix API Gateway。Netflix流服務提供數百個不同的微服務,包括電視、機頂盒、智慧手機、遊戲系統、平板電腦等。起初,Netflix檢視提供一個適用全場景的API。但是,他們發現這種形式不好用,因為涉及到各式各樣的裝置以及它們獨特的需求。現在,他們採用一個API Gateway來提供容錯性高的API,針對不同型別裝置有相應程式碼。事實上,一個介面卡處理一個請求平均要呼叫6到8個後端服務。Netflix API Gateway每天處理數十億的請求。

API Gateway的優點和缺點

如你所料,採用API Gateway也是優缺點並存的。API Gateway的一個最大好處是封裝應用內部結構。相比起來呼叫指定的服務,客戶端直接跟gatway互動更簡單點。API Gateway提供給每一個客戶端一個特定API,這樣減少了客戶端與伺服器端的通訊次數,也簡化了客戶端程式碼。

API Gateway也有一些缺點。它是一個高可用的元件,必須要開發、部署和管理。還有一個問題,它可能成為開發的一個瓶頸。開發者必須更新API Gateway來提供新服務提供點來支援新暴露的微服務。更新API Gateway時必須越輕量級越好。否則,開發者將因為更新Gateway而排佇列。但是,除了這些缺點,對於大部分的應用,採用API Gateway的方式都是有效的。

實現一個API Gateway

既然我們已經知道了採用API Gateway的動機和優缺點,下面來看在設計它時需要考慮哪些事情。

效能和可擴充套件性

只有少數公司需要處理像Netflix那樣的規模,每天需要處理數十億的請求。但是,對於大多數應用,API Gateway的效能和可擴充套件性也是非常重要的。因此,建立一個支援同步、非阻塞I/O的API Gateway是有意義的。已經有不同的技術可以用來實現一個可擴充套件的API Gateway。在JVM上,採用基於NIO技術的框架,如Netty,Vertx,Spring Reactor或者JBoss Undertow。Node.js是一個非JVM的流行平臺,它是一個在Chrome的JavaScript引擎基礎上建立的平臺。一個可選的方案是NGINX Plus。NGINX Plus提供一個成熟的、可擴充套件的、高效能web伺服器和反向代理,它們均容易部署、配置和二次開發。NGINX Plus可以管理授權、許可權控制、負載均衡、快取並提供應用健康檢查和監控。

採用反應性程式設計模型

對於有些請求,API Gateway可以通過直接路由請求到對應的後端服務上的方式來處理。對於另外一些請求,它需要呼叫多個後端服務併合並結果來處理。對於一些請求,例如產品最終頁面請求,發給後端服務的請求是相互獨立的。為了最小化響應時間,API Gateway應該併發的處理相互獨立的請求。但是,有時候請求之間是有依賴的。API Gateway可能需要先通過授權服務來驗證請求,然後在路由到後端服務。類似的,為了獲得客戶的產品願望清單,需要先獲取該使用者的資料,然後返回清單上產品的資訊。這樣的一個API 元件是Netflix Video Grid

利用傳統的同步回撥方法來實現API合併的程式碼會使得你進入回撥函式的噩夢中。這種程式碼將非常難度且難以維護。一個優雅的解決方案是採用反應性程式設計模式來實現。類似的反應抽象實現有Scala的Future,Java8的CompletableFuture和JavaScript的Promise。基於微軟.Net平臺的有Reactive Extensions(Rx)。Netflix為JVM環境建立了RxJava來使用他們的API Gateway。同樣地,JavaScript平臺有RxJS,可以在瀏覽器和Node.js平臺上執行。採用反應程式設計方法可以幫助快速實現一個高效的API Gateway程式碼。

服務呼叫

一個基於微服務的應用是一個分散式系統,並且必須採用執行緒間通訊的機制。有兩種執行緒間通訊的方法。一種是採用非同步機制,基於訊息的方法。這類的實現方法有JMS和AMQP。另外的,例如Zeromq屬於服務間直接通訊。還有一種執行緒間通訊採用同步機制,例如Thrift和HTTP。事實上一個系統會同時採用同步和非同步兩種機制。由於它的實現方式有很多種,因此API Gateway就需要支援多種通訊方式。

服務發現

API Gateway需要知道每一個微服務的IP和埠。在傳統應用中,你可能會硬編碼這些地址,但是在現在雲基礎的微服務應用中,這將是個簡單的問題。基礎服務通常會採用靜態地址,可以採用作業系統環境變數來指定。但是,探測應用服務的地址就沒那麼容易了。應用服務通常動態分配地址和埠。同樣的,由於擴充套件或者升級,服務的例項也會動態的改變。因此,API Gateway需要採用系統的服務發現機制,要麼採用服務端發現,要麼是客戶端發現。後續的一篇文章將會更詳細的介紹這部分。如果採用客戶端發現服務,API Gateway必須要去查詢服務註冊處,也就是微服務例項地址的資料庫。

處理部分失敗

在實現API Gateway過程中,另外一個需要考慮的問題就是部分失敗。這個問題發生在分散式系統中當一個服務呼叫另外一個服務超時或者不可用的情況。API Gateway不應該被阻斷並處於無限期等待下游服務的狀態。但是,如何處理這種失敗依賴於特定的場景和具體服務。例如,如果是在產品詳情頁的推薦服務模組無響應,那麼API Gateway應該返回剩下的其他資訊給使用者,因為這些資訊也是有用的。推薦部分可以返回空,也可以返回固定的頂部10個給使用者。但是,如果是產品資訊服務無響應,那麼API Gateway就應該給客戶端返回一個錯誤。

在快取有效的時候,API Gateway應該能夠返回快取。例如,由於產品價格變化並不頻繁,API Gateway在價格服務不可用時應該返回快取中的數值。這類資料可以由API Gateway自身來快取,也可以由Redis或Memcached這類外部快取實現。通過返回快取資料或者預設資料,API Gateway來確保系統錯誤不影響到使用者體驗。

Netflix Hystrix對於實現遠端服務呼叫程式碼來說是一個非常好用的庫。Hystrix記錄那些超過預設定的極限值的呼叫。它實現了circuit break模式,使得可以將客戶端從無響應服務的無盡等待中停止。如果一個服務的錯誤率超過預設值,Hystrix將中斷服務,並且在一段時間內所有請求立刻失效。Hystrix可以為請求失敗定義一個fallback操作,例如讀取快取或者返回預設值。如果你在用JVM,就應該考慮使用Hystrix。如果你採用的非JVM環境,那麼應該考慮採用類似功能的庫。

總結

對於大多數微服務基礎的應用,實現一個API Gateway都是有意義的,它就像是進入系統的一個服務提供點。API Gateway負責請求轉發、請求合成和協議轉換。它提供給應用客戶端一個自定義的API。API Gateway可以通過返回快取或者預設值的方式來掩蓋後端服務的錯誤。在本系列的下一篇文章中,我們將討論服務間的通訊問題。



===============================================
譯者介紹
陳杰,北京理工大學計算機學院在讀博士,研究方向是自然語言處理在企業網路信譽評價方面的應用,平時也樂於去實現一些突發的想法。在疲於配置系統環境時發現了Docker,跟大家一起學習、使用和研究Docker。

微服務實戰(三):深入微服務架構的程序間通訊

【編者的話】這是採用微服務架構建立自己應用系列第三篇文章。第一篇介紹了微服務架構模式,和單體式模式進行了比較,並且討論了使用微服務架構的優缺點。第二篇描述了採用微服務架構應用客戶端之間如何採用API Gateway方式進行通訊。在這篇文章中,我們將討論系統服務之間如何通訊。

簡介

在單體式應用中,各個模組之間的呼叫是通過程式語言級別的方法或者函式來實現的。但是一個基於微服務的分散式應用是執行在多臺機器上的。一般來說,每個服務例項都是一個程序。因此,如下圖所示,服務之間的互動必須通過程序間通訊(IPC)來實現。

Richardson-microservices-part3-monolith-vs-microservices-1024x518.png

後面我們將會詳細介紹IPC技術,現在我們先來看下設計相關的問題。

互動模式

當為某一個服務選擇IPC時,首先需要考慮服務之間如何互動。客戶端和伺服器之間有很多的互動模式,我們可以從兩個維度進行歸類。第一個維度是一對一還是一對多:

• 一對一:每個客戶端請求有一個服務例項來響應。
• 一對多:每個客戶端請求有多個服務例項來響應

第二個維度是這些互動式同步還是非同步:

• 同步模式:客戶端請求需要服務端即時響應,甚至可能由於等待而阻塞。
• 非同步模式:客戶端請求不會阻塞程序,服務端的響應可以是非即時的。

下表顯示了不同互動模式:

74.pic_.jpg

一對一的互動模式有以下幾種方式:

• 請求/響應:一個客戶端向伺服器端發起請求,等待響應。客戶端期望此響應即時到達。在一個基於執行緒的應用中,等待過程可能造成執行緒阻塞。
• 通知(也就是常說的單向請求):一個客戶端請求傳送到服務端,但是並不期望服務端響應。
• 請求/非同步響應:客戶端傳送請求到服務端,服務端非同步響應請求。客戶端不會阻塞,而且被設計成預設響應不會立刻到達。

一對多的互動模式有以下幾種方式:

• 釋出/ 訂閱模式:客戶端釋出通知訊息,被零個或者多個感興趣的服務消費。

• 釋出/非同步響應模式:客戶端釋出請求訊息,然後等待從感興趣服務發回的響應。

每個服務都是以上這些模式的組合,對某些服務,一個IPC機制就足夠了;而對另外一些服務則需要多種IPC機制組合。下圖展示了在一個打車服務請求中服務之間是如何通訊的。

Richardson-microservices-part3-taxi-service-1024x609.png

上圖中的服務通訊使用了通知、請求/響應、釋出/訂閱等方式。例如,乘客通過移動端給『行程管理服務』傳送通知,希望申請一次出租服務。『行程管理服務』傳送請求/響應訊息給『乘客服務』以確認乘客賬號是有效的。緊接著建立此次行程,並用釋出/訂閱互動模式通知其他服務,包括定位可用司機的排程服務。

現在我們瞭解了互動模式,接下來我們一起來看看如何定義API。

定義API

API是服務端和客戶端之間的契約。不管選擇了什麼樣的IPC機制,重要的是使用某種互動式定義語言(IDL)來精確定義一個服務的API。甚至有一些關於使用API first的方法(API-first approach)來定義服務的很好的理由。在開發之前,你需要先定義服務的介面,並與客戶端開發者詳細討論確認。這樣的討論和設計會大幅度提到API的可用度以及滿意度。

在本文後半部分你將會看到,API定義實質上依賴於選擇哪種IPC。如果使用訊息機制,API則由訊息頻道(channel)和訊息型別構成;如果選擇使用HTTP機制,API則由URL和請求、響應格式構成。後面將會詳細描述IDL。

API的演化

服務端API會不斷變化。在一個單體式應用中經常會直接修改API,然後更新給所有的呼叫者。而在基於微服務架構應用中,這很困難,即使只有一個服務使用這個API,不可能強迫使用者跟服務端保持同步更新。另外,開發者可能會嘗試性的部署新版本的服務,這個時候,新舊服務就會同事執行。你需要知道如何處理這些問題。

你如何處理API變化,這依賴於這些變化有多大。某些改變是微小的,並且可以和之前版本相容。比如,你可能只是為某個請求和響應添加了一個屬性。設計客戶端和服務端時候應該遵循健壯性原理,這很重要。客戶端使用舊版API應該也能和新版本一起工作。服務端仍然提供預設響應值,客戶端忽略此版本不需要的響應。使用IPC機制和訊息格式對於API演化很有幫助。

但是有時候,API需要進行大規模的改動,並且可能與之前版本不相容。因為你不可能強制讓所有的客戶端立即升級,所以支援老版本客戶端的服務還需要再執行一段時間。如果你正在使用基於基於HTTP機制的IPC,例如REST,一種解決方案是把版本號嵌入到URL中。每個服務都可能同時處理多個版本的API。或者,你可以部署多個例項,每個例項負責處理一個版本的請求。

處理部分失敗

在上一篇關於API gateway的文章中,我們瞭解到分散式系統中部分失敗是普遍存在的問題。因為客戶端和服務端是都是獨立的程序,一個服務端有可能因為故障或者維護而停止服務,或者此服務因為過載停止或者反應很慢。

考慮這篇文章中描述的部分失敗的場景。假設推薦服務無法響應請求,那客戶端就會由於等待響應而阻塞,這不僅會給客戶帶來很差的體驗,而且在很多應用中還會佔用很多資源,比如執行緒,以至於到最後由於等待響應被阻塞的客戶端越來越多,執行緒資源被耗費完了。如下圖所示:

Richardson-microservices-part3-threads-blocked-1024x383.png

為了預防這種問題,設計服務時候必須要考慮部分失敗的問題。

Netfilix提供了一個比較好的解決方案,具體的應對措施包括:

• 網路超時:當等待響應時,不要無限期的阻塞,而是採用超時策略。使用超時策略可以確保資源不會無限期的佔用。
• 限制請求的次數:可以為客戶端對某特定服務的請求設定一個訪問上限。如果請求已達上限,就要立刻終止請求服務。
• 斷路器模式(Circuit Breaker Pattern):記錄成功和失敗請求的數量。如果失效率超過一個閾值,觸發斷路器使得後續的請求立刻失敗。如果大量的請求失敗,就可能是這個服務不可用,再發請求也無意義。在一個失效期後,客戶端可以再試,如果成功,關閉此斷路器。
• 提供回滾:當一個請求失敗後可以進行回滾邏輯。例如,返回快取資料或者一個系統預設值。

Netflix Hystrix是一個實現相關模式的開源庫。如果使用JVM,推薦考慮使用Hystrix。而如果使用非JVM環境,你可以使用類似功能的庫。

IPC技術

現在有很多不同的IPC技術。服務之間的通訊可以使用同步的請求/響應模式,比如基於HTTP的REST或者Thrift。另外,也可以選擇非同步的、基於訊息的通訊模式,比如AMQP或者STOMP。除以之外,還有其它的訊息格式供選擇,比如JSON和XML,它們都是可讀的,基於文字的訊息格式。當然,也還有二進位制格式(效率更高)的,比如Avro和Protocol Buffer。接下來我們將會討論非同步的IPC模式和同步的IPC模式,首先來看非同步的。
非同步的,基於訊息通訊
當使用基於非同步交換訊息的程序通訊方式時,一個客戶端通過向服務端傳送訊息提交請求。如果服務端需要回復,則會發送另外一個獨立的訊息給客戶端。因為通訊是非同步的,客戶端不會因為等待而阻塞,相反,客戶端理所當然的認為響應不會立刻接收到。

一個訊息由頭部(元資料例如傳送方)和訊息體構成。訊息通過channel傳送,任何數量的生產者都可以傳送訊息到channel,同樣的,任何數量的消費者都可以從渠道中接受資料。有兩類channel,點對點釋出/訂閱。點對點channel會把訊息準確的傳送到某個從channel讀取訊息的消費者,服務端使用點對點來實現之前提到的一對一互動模式;而釋出/訂閱則把訊息投送到所有從channel讀取資料的消費者,服務端使用釋出/訂閱channel來實現上面提到的一對多互動模式。

下圖展示了打車軟體如何使用釋出/訂閱:

Richardson-microservices-part3-pub-sub-channels-1024x639.png

行程管理服務在釋出-訂閱channel內建立一個行程訊息,並通知排程服務有一個新的行程請求,排程服務發現一個可用的司機然後向釋出-訂閱channel寫入司機建議訊息(Driver Proposed message)來通知其他服務。

有很多訊息系統可以選擇,最好選擇一種支援多程式語言的。一些訊息系統支援標準協議,例如AMQP和STOMP。其他訊息系統則使用獨有的協議,有大量開源訊息系統可選,比如RabbitMQApache KafkaApache ActiveMQNSQ。它們都支援某種形式的訊息和channel,並且都是可靠的、高效能和可擴充套件的;然而,它們的訊息模型完全不同。

使用訊息機制有很多優點:

• 解耦客戶端和服務端:客戶端只需要將訊息傳送到正確的channel。客戶端完全不需要了解具體的服務例項,更不需要一個發現機制來確定服務例項的位置。

• Message Buffering:在一個同步請求/響應協議中,例如HTTP,所有的客戶端和服務端必須在互動期間保持可用。而在訊息模式中,訊息broker將所有寫入channel的訊息按照佇列方式管理,直到被消費者處理。也就是說,線上商店可以接受客戶訂單,即使下單系統很慢或者不可用,只要保持下單訊息進入佇列就好了。

• 彈性客戶端-服務端互動:訊息機制支援以上說的所有互動模式。

• 直接程序間通訊:基於RPC機制,試圖喚醒遠端服務看起來跟喚醒本地服務一樣。然而,因為物理定律和部分失敗可能性,他們實際上非常不同。訊息使得這些不同非常明確,開發者不會出現問題。

然而,訊息機制也有自己的缺點:

• 額外的操作複雜性:訊息系統需要單獨安裝、配置和部署。訊息broker(代理)必須高可用,否則系統可靠性將會受到影響。

• 實現基於請求/響應互動模式的複雜性:請求/響應互動模式需要完成額外的工作。每個請求訊息必須包含一個回覆渠道ID和相關ID。服務端傳送一個包含相關ID的響應訊息到channel中,使用相關ID來將響應對應到發出請求的客戶端。也許這個時候,使用一個直接支援請求/響應的IPC機制會更容易些。

現在我們已經瞭解了基於訊息的IPC,接下來我們來看看基於請求/響應模式的IPC。

同步的,基於請求/響應的IPC
當使用一個同步的,基於請求/響應的IPC機制,客戶端向服務端傳送一個請求,服務端處理請求,返回響應。一些客戶端會由於等待服務端響應而被阻塞,而另外一些客戶端也可能使用非同步的、基於事件驅動的客戶端程式碼(Future或者Rx Observable的封裝)。然而,不像使用訊息機制,客戶端需要響應及時返回。這個模式中有很多可選的協議,但最常見的兩個協議是REST和Thrift。首先我們來看下REST。

REST

現在很流行使用RESTful風格的API。REST是基於HTTP協議的。另外,一個需要理解的比較重要的概念是,REST是一個資源,一般代表一個業務物件,比如一個客戶或者一個產品,或者一組商業物件。REST使用HTTP語法協議來修改資源,一般通過URL來實現。舉個例子,GET請求返回一個資源的簡單資訊,響應格式通常是XML或者JSON物件格式。POST請求會建立一個新資源,PUT請求更新一個資源。這裡引用下REST之父Roy Fielding說的:

當需要一個整體的、重視模組互動可擴充套件性、介面概括性、元件部署獨立性和減小延遲、提供安全性和封裝性的系統時,REST可以提供這樣一組滿足需求的架構。
下圖展示了打車軟體是如何使用REST的。

Richardson-microservices-part3-rest-1024x397.png

乘客通過移動端向行程管理服務的/trips資源提交了一個POST請求。行程管理服務收到請求之後,會發送一個GET請求到乘客管理服務以獲取乘客資訊。當確認乘客資訊之後,緊接著會建立一個行程,並向移動端返回201(譯者注:狀態碼)響應。

很多開發者都表示他們基於HTTP的API是RESTful的。但是,如同Fielding在他的部落格中所說,這些API可能並不都是RESTful的。Leonard Richardson為REST定義了一個成熟度模型,具體包含以下4個層次(摘自

相關推薦

微服務實系列文章

【編者的話】本文來自Nginx官方部落格,是微服務系列文章的第一篇,主要探討了傳統的單體式應用的不足,以及微服務架構的優勢與挑戰。正如作者所說,微服務架構更適合用於構建複雜的應用,儘管它也有自己的不足。 這篇文章作者是Chris Richardson,他是早期基於Java的Amazonite EC2

ASP.NET Core微服務實系列

  希望給你3-5分鐘的碎片化學習,可能是坐地鐵、等公交,積少成多,水滴石穿,碼字辛苦,如果你吃了蛋覺得味道不錯,希望點個贊,謝謝關注。 前言   不好意思,讓你有點小失望,該篇只是一個系列目錄和構想,並沒有完全真正開弓。之所以有這個題目,是因為.NET Core在國內的學習資源七零八落,偶爾園

微服務實(四):服務發現的可行方案以及實踐案例

mesos aws ec2 動態配置 load 顯示 一個 cer c118 分布 微服務實戰(四):服務發現的可行方案以及實踐案例 這是關於使用微服務架構創建應用系列的第四篇文章。第一篇介紹了微服務架構的模式,討論了使用微服務架構的優缺點。第二和第三篇描

微服務實(六):選擇微服務部署策略

因此 區別 嚴重 http 虛擬化 one rose 精確 命名空間 微服務實戰(一):微服務架構的優勢與不足 微服務實戰(二):使用API Gateway 微服務實戰(三):深入微服務架構的進程間通信 微服務實戰(四):服務發現的可行方案以及實踐案例 微服務實踐(五)

SpringCloud微服務實——第二章Springboot

java with 當前 tom 參數 請求 bubuko zha 格式 Spring Boot項目工程      src/main/java:主程序入口HelloApplication,可以通過直接運行該類來啟動Spring Boot應用。   src/main/reso

PK1648-Spring Cloud微服務實視頻

集成 後端 aid 前後端分離 隨筆 使用 log eight container PK1648-Spring Cloud微服務實戰視頻 對接真實數據 從0開發前後端分離企業級上線項目 新年伊始,學習要趁早,點滴記錄,學習就是進步! 隨筆背景:在很多時候,很多入門不久的

微軟Azure AspNetCore微服務實 第二期

技術分享 gem 實踐 swe web app reactjs .net 圖片 辦公室 2018新年快樂!繼第1期微服務技術交流活動後(http://www.huodongxing.com/event/4403161781600),本次我們將與上海的小夥伴們繼續深入分享微服

Spring Cloud微服務實教程|Spring Cloud教程

Spring CloudSpring Cloud微服務實戰教程網盤地址:https://pan.baidu.com/s/1nN_tpaUuIhWIHTTtk1k9lg 密碼:9obv備用地址(騰訊微雲):https://share.weiyun.com/5Sc3eEK 密碼:q6cmrk 微服務架構已是當下最

SpringCloud微服務實---服務的註冊和發現(Eureka)

clas 清理 註冊中心 rac red mls 保護 gist clu 一、Spring Cloud簡介Spring Cloud是基於SpringBoot的,為開發人員提供了快速構建分布式系統的一些工具,包括配置管理、服務發現、斷路器、路由、微代理、事件總線、全局鎖、決策

《Spring Cloud微服務實》讀書筆記之客戶端負載均衡:Spring Cloud Ribbon - 4

摘要 客戶端負載均衡元件部署在客戶端,由客戶端維護要訪問的服務端清單(清單來源於服務註冊中心)。在Spring Cloud 中預設使用Ribbon作為客戶端負載均衡。 關鍵詞:客戶端負載均衡,Ribbon 一、什麼是客戶端負載均衡 負載均衡是對系統高可用、緩解網路壓力、處理能力擴容的重要手段之一。通常

【.NET Core微服務實-統一身份認證】開篇及目錄索引

簡介 學習.NETCORE也有1年多時間了,發現.NETCORE專案實戰系列教程很少,都是介紹開源專案或基礎教程,對於那些觀望的朋友不能形成很好的學習思路,遇到問題怕無法得到解決而不敢再實際專案中嘗試,今天我想通過專案系列實戰的方式,進一步推廣應用.NETCORE,讓大家感受它的魅力以及已經無所不能的神奇魔

Spring Cloud 微服務實詳細筆記

本文是看某課網關於 SpringCloud 微服務實戰的視訊總結的筆記,其中涉及了 Eureka Server 和 Eureka Client 的配置 Eureka Server 高可用性 服務間通訊的兩種方式:RestTemplate 和 Feign RabbitMQ 的安裝和使用

SpringBoot構建微服務實 之 熱部署(二)

在使用SpringBoot熱部署時可能會遇到一些異常比如熱部署設定完成生效之後,專案重啟會遇到快取重複存在的問題。 在部署時會遇見一下異常: The source of the existing CacheManager is: InputStreamConfigurati

SpringBoot構建微服務實 之 熱部署(-)

本小結主要學習下Spring boot實現熱部署的兩種方式,這兩種方法分別是使用 Spring Loaded和使用spring-boot-devtools進行熱部署。 什麼時熱部署 大家都知道在專案開發過程中,常常會改動頁面資料或者修改資料結構,為了顯示改動效果,往往需要重

微服務實(九):落地微服務架構到直銷系統(回顧總結)

這個系列我們大概寫了八篇文章,將微服務的最重要的內容過了一遍。當然其中有些內容還沒有涉及到,比如Docker(不是微服務架構風格中必須的)等,關於Docker我們自己可以在網上找找其他文章。 這篇文章就來回顧下微服務架構風格是如何落地的,如果你對以下回顧的內容都很清楚並已經有一些實踐的經驗,那麼恭喜你,你已

微服務實分享

導語喜歡踢球的同學一般知道這樣的一個段子,當年有好事的記者問風頭正盛的球王馬拉多納,在他進球中,哪個踢得最精彩?馬拉多納想了想就說:“一個吧”所以,努力追求“下一個”是一個普通球員到天才球員的必備品質,那麼在快速網際網路行業中同樣如此,每一次的技術更新變革也意味著無數個追求“下一個”的網際網路從事著的日日夜夜

SpringCloud微服務實專案總結

一、前言     在微服務的浪潮下,公司原有的架構已經嚴重跟不上日益發展的業務需求,隨著使用者量的增加,架構越來越臃腫,乃至沒有人敢於去動原有的架構,加之架構已經有7、8年的歷史,前前後後的開發人員也換過一批有一批,一旦修改,輕則bug一堆,重則牽一髮動全身,所以只

Spring cloud微服務實(三)——基於OAUTH2.0統一認證授權的微服務基礎架構升級

前言 從2018年年初寫的一篇主題為 Spring cloud微服務實戰——基於OAUTH2.0統一認證授權的微服務基礎架構的文章後就很少更新了。自從小寶貝誕生和公司業務的繁忙,年初計劃每週更新一篇博文的計劃已經落空了。年底了,終於清閒了些。 升級 Spring cloud微

《Spring微服務實》讀書筆記——構建微服務

設計微服務架構 構建程式碼的腳手架 分解業務問題 描述業務問題,安裝名詞來描述問題 注意動詞 尋找資料內聚性 確定服務粒度 使用資料模型作為將單體應用分解到微服務的基礎。 可以通過下面的概念來確定正確的服務粒度: 廣泛的使用微

《Spring微服務實》讀書筆記——通過配置伺服器來管理配置

管理配置 管理配置的四個原則 隔離 將服務配置資訊與服務的例項物理部署完全分離 抽象 抽象服務介面背後的配置資料訪問 集中 將應用程式配置集中到儘可能少的儲存庫中 穩定 實現高可用和冗餘 建立Spring Cloud 配置伺服器 Sprin