構建微服務:使用API Gateway

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

介紹

假定你正在爲在線購物應用開發一個原生手機客戶端。你須要實現一個產品最終頁來展現商品信息。數據庫

例如,下面的圖展現了你在亞馬遜Android客戶端上滑動產品最終頁時看到的信息。

雖然這是一個智能手機應用,這個產品最終頁展現了很是多的信息。例如,不只這裏有產品基本信息(名字、描述和價格),還有如下內容:編程

  • 購物車中的物品數後端

  • 下單歷史設計模式

  • 用戶評論api

  • 低庫存警告瀏覽器

  • 快遞選項緩存

  • 各式各樣的推薦,包括常常跟這個物品一塊兒被購買的產品、購買該物品的其餘顧客購買的產品以及購買該產品的顧客還瀏覽了哪些產品。服務器

  • 可選的購物選項架構

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

相對的,如果採用微服務架構,最終頁上的數據會分佈在不一樣的微服務上。下面列舉了可能與產品最終頁數據有關的一些微服務:

  • 購物車服務 -- 購物車中的物品數

  • 下單服務 -- 下單歷史

  • 分類服務 -- 基本產品信息,如名字、圖片和價格

  • 評論服務 -- 用戶評論

  • 庫存服務 -- 低庫存警告

  • 快遞服務 -- 快遞選項、截止時間、來自不一樣快遞API的成本計算

  • 推薦服務 -- 推薦產品


咱們須要決定移動客戶端如何訪問這些服務。請看下面這幾種方式

客戶端到微服務直接通訊

理論上說,一個客戶端能夠直接給多個微服務中的任何一個發起請求。每個微服務都會有一個對外服務端。這個URL可能會映射到微服務的負載均衡上,它再轉發請求到具體節點上。爲了搜索產品細節,移動端須要向上述微服務逐個發請求。

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

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

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

因爲上述三種問題的緣由,客戶端直接與服務器端通訊的方式不多在實際中使用。

採用一個API Gateway

一般來講,一個更好的解決辦法是採用API Gateway的方式。API Gateway是一個服務器,也能夠說是進入系統的惟一節點。這跟面向對象設計模式中的Facet模式很像。API Gateway封裝內部系統的架構,而且提供API給各個客戶端。它還可能有其餘功能,如受權、監控、負載均衡、緩存、請求分片和管理、靜態響應處理等。下圖展現了一個適應當前架構的API Gateway。

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能夠經過返回緩存或者默認值的方式來掩蓋後端服務的錯誤。在本系列的下一篇文章中,咱們將討論服務間的通訊問題。

相關文章
相關標籤/搜索