
背景
我們知道在微服務(wù)架構(gòu)風(fēng)格中,一個大應(yīng)用被拆分成為了多個小的服務(wù)系統(tǒng)提供出來瞄勾,這些小的服務(wù)他們自成體系塘揣,也就是說這些小系統(tǒng)可以擁有自己的數(shù)據(jù)庫,框架甚至語言等憋他,這些小系統(tǒng)通常以提供 Rest Api 風(fēng)格的接口來被 H5, Android, IOS 以及第三方應(yīng)用程序調(diào)用孩饼。
在《淺入淺出消息隊列》這一篇文章中,我提到了消息隊列是方便服務(wù)與服務(wù)之間的通信解耦竹挡,如下圖所示:

那么這時候問題來了镀娶,如果一個外部的應(yīng)用(瀏覽器、App)要去訪問這個大應(yīng)用怎么辦揪罕?
很簡單啊梯码,直接通過HTTP請求不就完了?
問題
真的這么簡單嗎好啰?我們以淘寶的商品詳情頁為例:

如上圖所示轩娶,這個頁面包含了視頻、庫存框往、商品價格鳄抒、商品評價等內(nèi)容,這些數(shù)據(jù)都來自不同的微服務(wù)中椰弊,所以沒辦法像傳統(tǒng)單體應(yīng)用一樣依靠數(shù)據(jù)庫的 join 查詢來得到最終結(jié)果许溅,因此就需要多次調(diào)用以檢索數(shù)據(jù),如下圖所示:

這就會引發(fā)幾個嚴重的問題:
- 不同的客戶端設(shè)備可能需要不同的數(shù)據(jù)秉版。Web,H5,APP贤重,需要單獨寫一套API
- 多次客戶端請求導(dǎo)致用戶體驗不佳。移動網(wǎng)絡(luò)相較于服務(wù)于服務(wù)間的局域網(wǎng)沐飘,有更低的帶寬和更高的延時游桩,如果可以同時執(zhí)行請求倒也還好,但如果客戶端要按照順序執(zhí)行請求耐朴,就會讓用戶體驗變得異常糟糕借卧。
- 缺乏封裝導(dǎo)致前后端不協(xié)調(diào)。過分的拆分API筛峭,會導(dǎo)致客戶端和服務(wù)端過度耦合铐刘,再加上移動端APP的新版本迭代到每個手機用戶時需要很久,這樣會使后端很難更改服務(wù)的API影晓。
這樣顯然是不好的設(shè)計镰吵,因此檩禾,本期的“天降猛男”就出現(xiàn)了——API網(wǎng)關(guān)。
API網(wǎng)關(guān)
再介紹API網(wǎng)關(guān)前疤祭,我們先來介紹一個設(shè)計模式——外觀模式盼产。
外觀模式(Facade Pattern)它向現(xiàn)有的系統(tǒng)添加一個接口,來隱藏系統(tǒng)的復(fù)雜性勺馆。類圖如下所示:

之所以要在說API網(wǎng)關(guān)前說一下外觀模式戏售,是因為二者的設(shè)計理念是類似的。
和外觀模式類似草穆,API 網(wǎng)關(guān)封裝了應(yīng)用程序的內(nèi)部架構(gòu)灌灾,并為其客戶端提供API,他還可能具有其他職責(zé)悲柱,如身份驗證锋喜、監(jiān)控、負載均衡豌鸡、緩存嘿般、請求分片與管理、靜態(tài)響應(yīng)處理直颅。下圖展示了客戶端博个、API網(wǎng)關(guān)和服務(wù)之間的關(guān)系。

所有的客戶端和消費端都通過統(tǒng)一的網(wǎng)關(guān)接入微服務(wù)功偿,在網(wǎng)關(guān)層處理所有的非業(yè)務(wù)功能。其出現(xiàn)也是側(cè)面貫徹了軟件工程中"高內(nèi)聚往堡,低耦合"的思想械荷。
核心作用
API 網(wǎng)關(guān)負責(zé)請求路由、API組合和協(xié)議轉(zhuǎn)換虑灰。來自外部客戶端的所有API請求首先會先轉(zhuǎn)到API網(wǎng)關(guān)吨瞎,后者再將請求路由到相應(yīng)的服務(wù)。API網(wǎng)關(guān)使用API組合模式處理其他請求穆咐,調(diào)用多個服務(wù)并聚合結(jié)果颤诀。同時他還可以在客戶端友好的協(xié)議(例如HTTP)與客戶端不友好的協(xié)議之間進行轉(zhuǎn)換。
請求路由
當(dāng)API網(wǎng)關(guān)收到請求時对湃,隨機會查詢路由映射崖叫,該映射將指定請求路由到哪個服務(wù)。例如拍柒,路由映射可以將HTTP方法和路徑映射到服務(wù)的HTTP URL心傀,這一點和Nginx提供的反向代理的功能是一樣的,后面我們也會對其進行一個比較拆讯。
既然有路由映射脂男,那存放在哪就是一個問題了养叛,我們需要為API網(wǎng)關(guān)設(shè)置一個路由映射的存儲位置,通過可能會用zookeeper等作為注冊中心來使用宰翅,文末我們也會提一下弊端挠进。
API組合
除去反向代理這個功能外,API網(wǎng)關(guān)還提供了API組合的操作帘靡。以上面的淘寶詳情頁為例乐严,如果我們單獨獲取視頻、商品價格掉缺、商品評論等信息卜录,需要發(fā)多個請求(getVideo,getPrice,getComments)。有了API網(wǎng)關(guān)后我們就可以將API接口組合起來眶明,通過一次請求(getItemDetail)來獲取需要的信息艰毒,如下圖所示,這樣可以極大的改善由于網(wǎng)絡(luò)延時導(dǎo)致的差用戶體驗搜囱。

協(xié)議轉(zhuǎn)換
API網(wǎng)關(guān)可以為外部客戶端提供RESTful API丑瞧,即使內(nèi)部的服務(wù)使用混合的通信協(xié)議,例如REST蜀肘、gRPC等绊汹。這樣做的好處是,對于外部客戶端而言扮宠,服務(wù)端更像是一個不可見的黑盒西乖。
API網(wǎng)關(guān)和Nginx
本質(zhì)上看API網(wǎng)關(guān)也是做了請求的轉(zhuǎn)發(fā),那既然Nginx也可以做請求轉(zhuǎn)發(fā)坛增,這兩者有什么區(qū)別获雕?
一張圖就可以很好的理解了。

Nginx做負載均衡時收捣,考慮到API網(wǎng)關(guān)在系統(tǒng)中不止一個(以集群的方式做高可用)届案,我們可以將Nginx至于API網(wǎng)關(guān)前,負責(zé)對API網(wǎng)關(guān)的負載均衡罢艾,然后再由網(wǎng)關(guān)決定進入到哪個真實的web 服務(wù)器楣颠。
這樣就可以讓兩者的分工更加明確:API網(wǎng)關(guān)聚合服務(wù),Nginx請求轉(zhuǎn)發(fā)
API 網(wǎng)關(guān)的優(yōu)缺點
API網(wǎng)關(guān)封裝了應(yīng)用程序的內(nèi)部結(jié)構(gòu)咐蚯,使得客戶端只需要同網(wǎng)關(guān)交互童漩,而不必調(diào)用特定的服務(wù)。同時API 網(wǎng)關(guān)為每一類客戶端提供了特定的 API 仓蛆,從而減少客戶端與應(yīng)用程序間的交互次數(shù)睁冬,簡化客戶端代碼的處理。
但就和所有中間件一樣,他們都存在一個共同的問題豆拨,API網(wǎng)關(guān)的存在使系統(tǒng)增加了一個必須開發(fā)直奋、部署和維護的高可用組件。如果這個組件沒有處理好施禾,那么 API 網(wǎng)關(guān)就會變成了應(yīng)用的性能瓶頸脚线。
而且為了暴露每個微服務(wù),開發(fā)人員必須更新 API 網(wǎng)關(guān)弥搞,所以就有可能會搭配其他服務(wù)發(fā)現(xiàn)類的中間件使用邮绿,例如zookeeper,這樣就又引入了新的中間件攀例。所以我們需要保證API 網(wǎng)關(guān)的更新過程要盡可能地簡單船逮,否則為了更新網(wǎng)關(guān),開發(fā)人員將不得不排隊等待粤铭。
由此可見挖胃,API網(wǎng)關(guān)也并不是一顆"銀彈",我們在中間件的選擇上還是需要結(jié)合項目的實際情況梆惯,萬不可追求新穎就濫用中間件酱鸭,適合自己的才是最好的。不過垛吗,API網(wǎng)關(guān)雖然仍有不足凹髓,但對于大多數(shù)現(xiàn)實世界的應(yīng)用程序而言使用 API 網(wǎng)關(guān)是合理的。