2.使用API閘道器構建微服務
原文地址:https://www.nginx.com/blog/building-microservices-using-an-api-gateway/
1. 介紹
當我們使用微服務構建應用時,需要考慮客戶端如何和應用通訊。對於單體應用而言,只有一組例項(一般是負載均衡加上多個應用副本);而在微服務架構下,每個服務都可能有一組例項。在本篇文章中,我們將看到這如何影響客戶端和服務的通訊,將看到如何應用API閘道器解決問題。
假設我們要開發一個原生的手機購物應用,有一個頁面要展示詳細的商品資訊,展示效果如下圖所示:

除了展示商品的基本資訊(像名稱、描述和價格等),還包含以下資訊:
A. 購物車中的商品數
B. 歷史訂單
C. 使用者評價
D. 低庫存告警
E. 物流選項
F. 智慧推薦
G. 替代產品
對於單體應用而言,手機客戶端只需要傳送一次請求(像:GET api.company.com/productdetails/productId),負載均衡模組將請求分發到應用例項,應用查詢不同的資料表獲取資料,再將響應返回給客戶端。
對於微服務應用而言,商品詳情頁需要的資料由多個微服務擁有,例如:
購物車服務,提供購物車中的商品數量;
訂單服務,提供歷史訂單;
商品分類服務,提供商品名稱、圖片、價格等商品基本資訊;
評價服務,提供使用者評價;
庫存服務,提供低庫存警告;
物流服務,提供物流選擇、到貨日期以及物流價格等;
推薦服務,提供智慧推薦的關聯商品。

我們需要考慮手機客戶端如何訪問這些服務。
2. 客戶端直接訪問微服務
理論上說,客戶端可以直接訪問微服務。每個微服務都會發佈一個公共訪問入口(像:https://serviceName.api.company.name),這個連結指向微服務的負載均衡模組。為了獲取商品詳情的各種資料,手機客戶端需要訪問多個相關的微服務。
不幸的是,由客戶端直接訪問微服務有很多限制和挑戰。其中一個問題是客戶端的需求和微服務提供的介面不匹配。在商品詳情的例子中,客戶端需要訪問多個微服務,多個微服務組合的才是客戶端想要的。應用越複雜,需要訪問的微服務就越多,Amazon展示商品詳情時需要訪問上百個服務。客戶端通過網際網路和移動網路訪問這麼多服務顯得效率低下,還會造成客戶端程式碼特別複雜。
客戶端直接訪問微服務的另外一個問題是,有些微服務提供的介面並不是Web友好的,一個服務可能使用Thrift的RPC,而另外一個服務可能使用AMQP的訊息機制;這些服務都不是防火牆友好的,並不是網際網路上的最佳實踐。一個應用應該在防火牆外使用HTTP和WebSocket這些對Web友好的通訊協議。
最後一個問題是,客戶端直接訪問微服務會導致微服務重構困難。隨著時間的推移,我們可能會對原有服務進行拆分或合併等重構工作,而如果客戶端直接訪問微服務,這些重構工作會不可避免的影響到客戶端,從而使得重構變得異常困難。
由於以上原因,客戶端直接訪問微服務不可行。
3. 使用API閘道器
API閘道器是一種較好的客戶端訪問微服務的方式。API閘道器是系統的單一訪問入口,就像面向物件設計模式中的Façade模式,它隱藏了應用的內部結構,為不同型別的客戶端定製不同介面。API閘道器還能提供鑑權、監視、負載均衡、快取、協議轉換和管理等功能。下圖展示了本例中API閘道器的結構:

API閘道器負責請求路由、響應組裝和協議轉換。所有的客戶端請求先到API閘道器,閘道器將請求路由到對應的微服務;在客戶端的一次請求中,閘道器可能呼叫多個微服務,再將多個返回結果組裝後返回給客戶端。API閘道器還能實現Web協議(Http、WebSocket)與其他協議的轉換。
API閘道器能為每一類客戶端定製介面。比如它能為手機客戶端定製商品詳情的介面(/productdetails?productid=xxx),手機客戶端呼叫這個介面獲取商品詳情頁的所有資訊。
Netflix的API閘道器是個典型的例子,Netflix的流視訊服務在電視、電視盒子、智慧手機、遊戲機等上百種不同的裝置上都能使用。最初,Netflix努力設計能適用於所有裝置的統一介面,後來發現不同裝置對介面的需求差異太大。今天,Netflix通過API閘道器使用不同的裝置介面卡為每種裝置提供不同的介面,每種裝置介面卡呼叫6到7個微服務。每天Netflix的API閘道器處理上百萬次的客戶端請求。
4. API閘道器的優點和缺點
最大的優點是API閘道器封裝了應用的具體實現,簡化了客戶端與應用之間的通訊,極大減少了客戶端的程式碼量。
缺點是你需要開發、部署、管理一個高可靠的API閘道器。由於所有的請求都先到API閘道器,它很可能變成整個系統的瓶頸;另外API閘道器要足夠輕量級,要方便升級以反映微服務的變化。雖然有上述缺點,API閘道器對大多數現實中的應用來說還是一種可行的微服務實現方案。
5. 實現API閘道器
現在我們來看實現API閘道器時需要重點考慮的幾個問題:
效能和伸縮性
全世界只有大約100個像Netflix那樣規模的應用,每天需要處理百萬級的服務請求。你的應用規模可能沒有那麼大,可API閘道器的效能和伸縮性依然很重要。因此,在支援非同步呼叫、非阻塞I/O等高效能特性的平臺上構建API閘道器就很重要。在JVM之上,你可以使用基於NIO的框架,像Netty、Vertx、Spring Reactor或者JBoss Undertow;另外一種非JVM的選項是基於Chrome的js引擎Node.js;還有一種選擇是Nginx Plus。Nginx Plus可作為成熟的、可伸縮的、高效能的web伺服器和反向代理伺服器,部署簡單,配置和程式設計方便,並能支援身份鑑別、訪問控制、負載均衡、服務健康檢查等功能。
使用響應式程式設計模型
對有些服務請求,API閘道器簡單轉發;對另外一些服務請求,API閘道器需要呼叫幾個服務,並組裝多個服務的響應。對於某些服務請求,像之前講到的商品詳情,需要呼叫多個互相獨立的服務;為了縮短服務響應時間,API閘道器應該支援併發多個服務請求;而有些時候,服務之間可能有依賴,比如需要先進行身份鑑別再呼叫服務。類似的,要展示使用者心願列表中的商品詳情,需要先獲取使用者的心願列表,再根據心願列表獲取每件商品的詳細資訊。另外一個有趣的服務組合示例是Netflix的視訊網格。
使用傳統非同步呼叫方式編寫服務組合的程式碼會是一場災難,程式碼會變得雜亂、不易理解、容易出錯。一個更好的方式是使用響應式程式設計編寫宣告式程式碼,現在很多語言都支援響應式的抽象,像Scala中的Future、Java8中的CompletableFuture、JaveScript中的Promise。還有一些響應式的擴充套件(也叫Rx或者ReactiveX),這最早是微軟為.Net平臺開發的,後來Netflix為他們的API閘道器開發了RxJava,現在針對瀏覽器和Node.js有了RxJS。使用響應式程式設計可以讓你書寫簡單高效的API閘道器程式碼。
服務呼叫
基於微服務的應用是分散式系統,必須使用程序間通訊的機制。程序間通訊主要有兩大類,其中一種基於訊息的非同步呼叫,使用類似JMS、AMQP等訊息中介軟體,或者像Zeromq這種不需要訊息中介軟體的服務間直接通訊;另外一種是像Http和Thrift之類的同步呼叫。這兩種通訊機制,在應用中一般都會用到,甚至會組合使用。因此,使用API閘道器實現微服務架構時,需要支援多種服務間通訊機制。
服務發現
API閘道器需要知道跟它通訊的所有微服務的訪問入口(IP地址加埠號)。在傳統的單體應用中,直接將IP地址和埠號硬編碼到應用中就可以,而在基於微服務的應用中就不行了。提供基礎設施的服務(像訊息中介軟體)還可以通過配置環境變數事先確定,而應用的其他服務的訪問入口是動態變化的。另外,由於自動伸縮特性和服務實時更新,構成服務的例項的集合也是動態變化的。因此,API閘道器像其他服務客戶端一樣,需要使用系統的服務發現機制(服務端發現或者客戶端發現),後續會有文章專門描述服務發現。需要指出的是,如果系統採用客戶端服務發現機制,那API閘道器必須能查詢服務登錄檔(包含所有服務例項和訪問入口的資料庫)。
處理部分失敗
另外一個必須考慮的問題是部分失敗。在分散式系統中,一個服務呼叫另外一個反應遲鈍或者不可達的服務時,就會出現部分失敗的情況。如何處理服務呼叫失敗要看具體情況,比如說,在展示商品詳情時呼叫智慧推薦服務失敗,API閘道器就應該展示其他商品資訊,因為這些資訊對使用者依然有價值,智慧推薦可以是空,或者是預設的top10最受歡迎商品;但是,如果呼叫商品資訊服務失敗,API閘道器就應該返回錯誤。
API閘道器也能返回快取資料。比方說,既然商品價格不會經常變化,就可以在呼叫價格服務失敗時返回之前快取的價格資料;可以使用API閘道器自身快取,也可以使用像Redis或Memcached等第三方快取。通過返回預設資料或者快取資料,API閘道器可以確保部分系統失敗不影響使用者體驗。
NetflixHystrix對編寫遠端服務呼叫的程式碼而言是一個特別有用的庫,它會終止超過閾值的服務呼叫。它實現了一種斷路器模式,停止客戶端對沒有響應的服務做無意義的等待;如果某一個服務出錯的概率達到閾值,Hystrix會將該服務阻斷,這樣在一定時間內,所有對該服務的請求會立即返回錯誤。Hystrix允許你為錯誤設定回撥操作,在回撥操作中可以讀取快取或者是返回預設值。如果你的應用使用JVM,一定要考慮使用Hystrix;如果你的應用使用沒有JVM的環境,你也應該考慮找一個類似的庫。
6、 總結
對於大多數基於微服務的架構而言,使用API閘道器是有意義的,它可以作為應用的單一訪問入口。API閘道器負責請求路由、響應組裝和協議轉換,它能為每一類客戶端定製介面;API閘道器還能標註出錯的服務,並返回預設值或者快取資料。在接下來的章節中,我們將詳細討論服務間通訊。