構建微服務:API網關

博客原文

在nginx官網的blog中,作者Chris Richardson關于微服務的文章有七篇:
1. Introduction to Microservices(微服務介紹)
2. Building Microservices: Using an API Gateway(構建微服務:API網關
3. Building Microservices: Inter-Process Communication in a Microservices Architecture(構建微服務:微服務架構中的進程間通信)
4. Service Discovery in a Microservices Architecture(微服務架構中的服務發現)
5. Event-Driven Data Management for Microservices微服務中基于事件驅動的數據管理
6. Choosing a Microservices Deployment Strategy(微服務部署策略)
7. Refactoring a Monolith into Microservices(重構單體應用為微服務)
本篇文章翻譯的是其中的第二篇,關于微服務中的API網關。

璀璨的煙花

00 寫在前面的話

當你選擇使用一系列的微服務來構建你的應用的時候,你需要決定你的應用如何與這些微服務進行交互。在單體應用中僅僅是一些端點(通常是重復的、負載均衡的),然而在微服務架構中每個微服務都暴露一系列細粒度的端點。在這篇文章中,我們將研究這個問題對客戶端和應用之間通信的影響,然后提出一個解決辦法,就是使用API網關。

01 簡介

假設你正在為購物應用開發一個手機客戶端,好像你需要實現一個產品詳情頁,用來展示任何給定的產品的詳細信息。

舉個例子,下圖展示了你在Amazon的android手機客戶端上滾動看到的產品詳情頁。

Amazon手機客戶端上的產品詳情頁

即使這是一個智能手機上的應用,產品詳情頁一樣展示了很多信息。例如,不僅有基本的產品信息(如名稱、描述和價格),而且這個頁面還展示了:

  • 購物車中的商品數量
  • 歷史訂單數
  • 用戶評論
  • 低庫存的預警
  • 發貨選項
  • 各種推薦,包括經常和本產品一起被購買的其他產品,購買該產品的顧客購買的其他產品,還有購買該產品的顧客查看的其他產品

當你使用單體應用架構時,手機客戶端可以簡單地通過一個REST接口(GET api.company.com/productdetails/productId)檢索到該數據。一個負載均衡器將請求路由到N個相同的應用實例中的一個。然后這個應用實例查詢多張數據庫表,最后將數據響應給客戶端。

相反,當我們使用微服務架構之后,在產品詳情頁上展示的數據來自多個微服務。這里是一些可能擁有產品詳情頁數據的微服務:

  • 購物車服務 - 購物車中物品的數量
  • 訂單服務 - 歷史訂單
  • Catalog 服務 - 產品基本信息,如名稱、圖片和價格
  • 評論服務 - 用戶的評論
  • 庫存服務 - 低庫存預警
  • 郵寄服務 - 發貨選項,期限,來自各個郵寄提供商的API的成本
  • 推薦服務 - 推薦商品
來自多個微服務的數據

我們需要決定手機客戶端如何訪問這些服務,讓我們看看有幾種選擇。

02 客戶端和微服務直接交互

理論上來說,客戶端可以直接請求每個微服務,每個微服務都有一個公共的端點(https://serviceName.api.company.name)。這個URL會被映射到微服務的負載均衡器上,然后它再分發請求到可用的應用實例上。為了檢索產品詳情,客戶端需要向上面列出的所有微服務發送請求。

可惜,這種方法實現起來是有困難和限制的。一個問題是,客戶端需求和每個微服務暴露的細粒度的API是不匹配的。在這個例子中,客戶端需要分別發送七次請求,在更復雜的應用中可能需要請求更多次。例如,Amazon介紹說,在他們的產品詳情頁上涉及到數百個微服務。雖然客戶端在局域網上可以發起這么多的請求,但是在公網上可能效率太低,這在移動網絡上是不符合實際的。而且這種方法也會使客戶端的代碼特別復雜。

客戶端直接調用微服務的另外一個問題是,一些微服務使用的協議不是web友好的。一個服務使用的是Thrift RPC的二進制協議,而另一個服務可能使用的是AMQP消息協議,這兩個協議對瀏覽器和防火墻都是不友好的,最好是在內部使用。一個應用應該使用例如 HTTP 和 WebSocket 這樣能穿透防火墻的協議。

這個方法的另外一個缺點是,它會使微服務的重構比較苦難。隨著時間的推移,我們可能需要將某個系統拆分成服務。例如,我們可能將兩個服務合并成一個,或者將一個服務拆分成兩個甚至更多的服務。不管怎樣,如果客戶端和很多服務之間都是直接通信,這樣的重構可能是極端困難。

由于以上這些問題,客戶端很少會和微服務直接通信。

03 API網關

通常情況下,更好的方法是使用所謂的API網關。API網關是一個系統的一個入口,它類似于面向對象設計中的外觀模式。API網關封裝了內部系統架構,為每個客戶端單獨提供一個API。API網關可能還有其他的職責,例如授權、監控、負載均衡、緩存、請求的修改和管理、靜態響應的處理。

下圖展示了一個API網關通常適合的架構:

API網關所適合的架構

API網關負責請求的路由、組合和協議轉換,所有來自客戶端的請求首先都要經過API網關,然后它再路由請求到合適的微服務。API網關處理請求的方式通常是,調用多個微服務,然后合并響應結果。API網關可以在web協議(如HTTP、WebSocket)和內部使用的web不友好的協議之間做轉換。

API網關也可以為每個客戶端提供一個特定的API,它通常會為手機客戶端暴露一些粗粒度的API,例如在產品詳情的場景下,API網關可以提供一個端點(/productdetails?productid=xxx),這樣手機客戶端發送一個請求就能檢索到產品的所有信息。API網關處理這個請求調用了多個服務(產品信息、推薦、評論等),然后合并響應結果。

Netflix API 網關是一個非常好的例子,Netflix的流服務支撐著數百種設備,包括電視、機頂盒、智能手機、游戲系統、平板電腦等等。最初,Netflix試圖給所有的設備提供統一的API服務,然而他們發現效果不是很好,因為各種各樣的設備都有獨特的需求。現在他們使用API網關為每種設備提供定制化的API,通過執行特定設備的適配器代碼實現的。通常每個適配器處理一個請求平均要調用后端六七個服務。Netflix的API網關每天處理數十億的請求。

04 API網關的優點和缺點

正如你期待的那樣,API網關既有優點也有缺點。使用API網關的一個主要優點就是,它封裝了應用的內部架構,客戶端不需要調用特定的服務,而只需要與網關通信。API網關給每一種客戶端都提供了特定的API,這簡化了客戶端與應用之間的通信往返次數,也簡化了客戶端代碼。

API網關也有一些缺點,這又是一個高可用的組件,需要開發、部署和管理。API網關成為開發的瓶頸,這也是有風險的。開發人員為了暴露每個微服務的端點必須更新API網關,更新API網關的過程盡量輕量化也是很重要的,否則開發人員將在更新API網關的過程上被迫排隊。盡管有這么多的缺點,但是在現實中的應用上使用API網關還是有意義的。

05 API網關的實現

我們已經看到了使用API網關的動機和一些利弊權衡,現在讓我們看一下你需要考慮的各種設計問題。

05.1 性能和伸縮性

只有少數的公司有Netflix的運營規模,每天需要處理數十億的請求,然而對于大多數的應用程序來說,API網關的性能和伸縮性是非常重要的。因此,在構建API網關的時候使用異步調用和非阻塞I/O是非常有意義的。有很多種不同的技術都可以用來實現可伸縮的API網關,在JVM平臺中你可以使用Netty、Vertx、Spring Reactor 或者 JBoss Undertow等這些機遇NIO的框架,在非JVM的平臺中流行的技術是Node.js,它是運行在chrome的JavaScript引擎中的,另一個選擇是Nginx Plus,Nginx Plus提供了一個成熟的、可擴展的、高性能的Web服務器,并且還提供了易于部署、配置和編程的反向代理,Nginx Plus可以管理授權、訪問控制、請求的負載均衡、響應的緩存,并提供了應用本身的健康檢查和監控。

05.2 使用響應式編程模型

API網關處理一些請求的方式是,簡單將他們路由到合適的后端服務;處理其它請求的方式是,調用多個后端服務,然后將它們的響應結果聚合在一起。對于某些請求,如產品詳情的請求,它的后端服務都是彼此獨立的,為了減少響應時間,API網關應該并行地執行這些獨立的請求。然而,有時候一些請求之間是彼此依賴的。API網關在將請求路由到后端服務之前,可能需要調用身份驗證服務來驗證請求。類似地,在獲取顧客需要的產品列表時,API網關必須首先檢索顧客需要的產品概要信息,然后才能檢索每個產品的詳細信息。另外一個有趣的API組合的示例是 Netflix Video Grid

使用傳統的異步回調的方法編寫API組合的代碼,很快就會將你帶到回調的地獄,代碼將會變得混亂、難于理解并且易于出錯。一個更好的實現API網關的方法是,使用響應式編程方法來實現聲明式的API網關代碼。響應式概念的例子有Scala中的Future,Java 8 中的 CompletableFuture,JavaScript中的 Promise。也還有Reactive Extensions,也叫 Rx 或者 ReactiveX,最初是微軟為 .Net 平臺開發的;Netflix創建了JVM平臺的 RxJava,并將它使用在他們的API網關上;也有 JavaScript 上的 RxJS,它運行在瀏覽器或者Node.js上。使用響應式方法編寫API網關的代碼簡單又高效。

05.3 服務調用

基于微服務的應用是一個分布式系統,必須使用進程間通信機制。進程間通信有兩種方式:一種是異步的、基于消息的機制,有些是用消息中間件(JMS or AMQP)實現的,其它的是直接與服務通信的無中間件模式,如Zeromq;進程間通信的另外一種方式是采用同步機制,如HTTP或者Thrift。通常一個系統會同時使用同步和異步方式,甚至會使用每種方式的不同實現形式,因此API網關必須支持多種通信機制。

05.4 服務發現

API網關需要知道要調用的每個微服務的位置(IP地址和端口號),在傳統的應用中,你可能需要硬性地配置各個微服務的位置,但現在的基于云的微服務應用中就很簡單了。基礎設施服務,例如消息中間件,通常都是一個靜態的位置,可以通過操作系統的環境變量來指定。然而,確定一個應用服務的位置不是那么簡單的,應用服務是動態分配位置的,并且一個服務的實例集合也是動態改變的,這是因為一些自動的擴縮容和升級。因此,API網關要像其他的服務客戶端一樣,需要使用系統的服務發現機制:Server?Side Discovery 或者 Client?Side Discovery。在后面的文章中,將詳細地介紹服務發現。現在需要我們注意的是,如果系統使用的是客戶端側的發現,API網關必須能夠查詢到服務注冊中心,它是所有微服務實例和對應位置的數據庫存儲。

05.5 部分失敗的處理

在實現API網關的時候必須解決的一個問題是部分失敗問題。這個問題在所有的分布式系統中都會出現,因為一個服務調用另外的服務時有可能響應慢或者服務不可用。API網關決不能由于等待下游服務而被無限期的阻塞下去,例如在產品詳情的場景中,如果推薦服務未響應,API網關應該將其余的產品詳情信息返回給客戶端,因為這些東西仍然對用戶是有用的,這時的推薦內容是空的或者被其他的內容代替,例如top 10的產品。但是如果產品信息服務未響應,API網關應該返回錯誤給客戶端。

如果后端服務不可用,API網關也可以返回緩存數據,例如,因為產品價格是很少改變的,如果產品價格服務不可用,API網關可以返回緩存的價格數據。數據可以被緩存在API網關本身,也可以緩存在外部,如Redis或者Memcached。API網關在后端系統調用失敗的時候通過返回默認數據和緩存數據來確保不影響用戶體驗。

Netflix Hystrix是一個在調用遠程服務時非常有用的編碼庫,Hystrix調用時間超過某個設定的閾值,就是所謂的超時,它是實現了斷路器模式的,這時它會阻止客戶端對不響應的服務的不必要等待。如果一個服務的錯誤率超過指定的閾值,然后Hystrix就會觸發斷路器,然后所有的請求在一個時間區間內都會立即失敗。Hystrix會讓你定義一個請求失敗后的返回函數,例如從緩存讀取數據或者返回默認值。如果你正在使用JVM的平臺,你應該考慮使用Hystrix。如果在使用非JVM平臺你應該等效的類庫。

06 總結

對于大多數基于微服務的應用,實現一個API網關都是非常有意義的,它是一個系統的唯一入口。API網關負責請求路由、組合服務和協議轉換。它為應用的每個客戶端提供定制化的API,API網關也可以通過返回緩存或默認數據來屏蔽調用后端服務失敗。在這個系列的下一篇文章中,我們將介紹服務之間的通信。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,570評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,505評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,786評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,219評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,438評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,971評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,796評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,995評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,230評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,697評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容