系統(tǒng)的門面API網(wǎng)關(guān)應(yīng)該怎么設(shè)計?
隨著自己的電商網(wǎng)站知名度越來越高,系統(tǒng)迎來了一些“不速之客”,在凌晨的時候,系統(tǒng)中的搜索商品和用戶接口的調(diào)用量會急劇上升,持續(xù)一段時間之后又回歸正常。
這種搜索請求的特點是它們來自一組特定的設(shè)備。當(dāng)我在搜索服務(wù)上引入了設(shè)備ID的限流策略后,凌晨的高峰搜索請求得到了控制。然而,不久之后,用戶服務(wù)開始受到大量爬蟲請求,請求用戶信息,商品服務(wù)也開始受到爬蟲請求,請求商品信息。因此,我被迫在這兩個服務(wù)上添加了相同的限流策略。但這會引發(fā)一個問題:相同的策略需要在多個服務(wù)中重復(fù)實現(xiàn),無法實現(xiàn)代碼的重用。如果其他服務(wù)也面臨類似問題,那么就需要復(fù)制代碼,這顯然不是一個好的做法。
作為一名Java程序員,我的第一反應(yīng)是將限流功能獨立成一個可重用的JAR包供這三個服務(wù)引用。但問題是,我們的電商團隊不僅使用Java,還使用PHP、Go等多種編程語言開發(fā)服務(wù)。對于不同編程語言的服務(wù),無法直接使用Java的JAR包。這就是引入API網(wǎng)關(guān)的原因。API網(wǎng)關(guān)可以提供一種跨語言的解決方案,讓不同編程語言的服務(wù)都能夠輕松地使用相同的限流策略。
API 網(wǎng)關(guān)起到的作用
API網(wǎng)關(guān)(API Gateway)并非單一的開源組件,而是一種架構(gòu)模式。它將一些服務(wù)共享的功能整合在一起,獨立部署為一個獨立的層級,用于解決服務(wù)治理問題。你可以將其視為系統(tǒng)的前門,負責(zé)對系統(tǒng)的流量進行統(tǒng)一管理。從我的角度來看,API網(wǎng)關(guān)可以分為兩種主要類型:入口網(wǎng)關(guān)和出口網(wǎng)關(guān)。
- 入口網(wǎng)關(guān):入口網(wǎng)關(guān)位于系統(tǒng)內(nèi)部,用于處理外部請求進入系統(tǒng)的流量。它可以執(zhí)行一些重要的任務(wù),如路由請求到正確的服務(wù)、身份驗證、鑒權(quán)、限流、請求轉(zhuǎn)換和響應(yīng)轉(zhuǎn)換等。入口網(wǎng)關(guān)有助于管理系統(tǒng)對外部的可見性,并提供一致的接口。
- 出口網(wǎng)關(guān):出口網(wǎng)關(guān)則位于系統(tǒng)內(nèi)部,用于處理系統(tǒng)內(nèi)部服務(wù)調(diào)用對外部系統(tǒng)的流量。它可以執(zhí)行任務(wù)如負載均衡、緩存、重試、錯誤處理等,以確保內(nèi)部服務(wù)之間的通信可靠性和性能。出口網(wǎng)關(guān)通常用于隔離內(nèi)部服務(wù)和外部系統(tǒng),同時提供一致的接口。
這兩種類型的API網(wǎng)關(guān)可以幫助管理系統(tǒng)的邊界,提供了流量控制、安全性、性能優(yōu)化等方面的解決方案,以確保系統(tǒng)的穩(wěn)定性和可擴展性。
入口網(wǎng)關(guān)是我們經(jīng)常使用的網(wǎng)關(guān)種類,它部署在負載均衡服務(wù)器和應(yīng)用服務(wù)器之間,主要有幾方面的作用。
- API網(wǎng)關(guān)為客戶端提供了一個單一的入口地址,它可以根據(jù)客戶端的請求,動態(tài)將請求路由到不同的業(yè)務(wù)服務(wù),并執(zhí)行必要的協(xié)議轉(zhuǎn)換。在您的系統(tǒng)中,不同微服務(wù)可能以不同的協(xié)議對外提供服務(wù):一些是HTTP服務(wù),一些已經(jīng)改造為RPC服務(wù),而一些可能仍然是Web服務(wù)。API網(wǎng)關(guān)的作用是將這些服務(wù)的部署地址和協(xié)議的細節(jié)對客戶端屏蔽,從而為客戶端調(diào)用帶來便利??蛻舳酥恍枧cAPI網(wǎng)關(guān)交互,而無需直接與各種不同協(xié)議的服務(wù)通信,使整體調(diào)用過程更加簡便。
- 另一方面,在 API 網(wǎng)關(guān)中,我們可以植入一些服務(wù)治理的策略,比如服務(wù)的熔斷、降級、流量控制和分流等等。
- 再有,客戶端的認證和授權(quán)的實現(xiàn),也可以放在 API 網(wǎng)關(guān)中。
- 另外,API 網(wǎng)關(guān)還可以做一些與黑白名單相關(guān)的事情,比如針對設(shè)備 ID、用戶 IP、用戶 ID 等維度的黑白名單。
- 最后,在 API 網(wǎng)關(guān)中也可以做一些日志記錄的事情,比如記錄 HTTP 請求的訪問日志,分布式追蹤系統(tǒng)時,提到的標記一次請求的 requestId 也可以在網(wǎng)關(guān)中來生成。
圖片
出口網(wǎng)關(guān)的功能相對較簡,但在系統(tǒng)開發(fā)中也發(fā)揮著重要的作用。我們的應(yīng)用經(jīng)常需要依賴外部的第三方系統(tǒng),比如第三方賬戶登錄或支付服務(wù)。在這種情況下,我們可以在應(yīng)用服務(wù)器和外部系統(tǒng)之間部署出口網(wǎng)關(guān),用于統(tǒng)一處理對外部API的認證、授權(quán)、審計和訪問控制等任務(wù)。這樣可以幫助我們更好地管理和保護與外部系統(tǒng)的通信,確保系統(tǒng)的安全性和合規(guī)性。
圖片
API 網(wǎng)關(guān)要如何實現(xiàn)
了解了API網(wǎng)關(guān)的作用后,下一步是關(guān)注在實施API網(wǎng)關(guān)時需要注意的關(guān)鍵方面,以及了解一些常見的開源API網(wǎng)關(guān)解決方案。這將使您在實際工作中,不論是考慮自主開發(fā)API網(wǎng)關(guān)還是使用現(xiàn)有的開源實現(xiàn),都能更自如地做出決策。
在開發(fā)API網(wǎng)關(guān)時,首要考慮的是其性能。這非常重要,因為API入口網(wǎng)關(guān)需要處理所有來自客戶端的流量。舉個例子來說,假設(shè)業(yè)務(wù)服務(wù)的處理時間是10毫秒,而API網(wǎng)關(guān)的處理時間是1毫秒,那么相當(dāng)于每個接口的響應(yīng)時間都要增加10%,這將對性能產(chǎn)生巨大的影響。性能的關(guān)鍵通常在于I/O模型的選擇,這里只是舉一個例子來說明I/O模型對性能的影響
在Netflix開源的API網(wǎng)關(guān)Zuul的1.0版本中,采用的是同步阻塞I/O模型。整體系統(tǒng)可以看作是一個Servlet,它接收用戶的請求,然后在網(wǎng)關(guān)中執(zhí)行配置的認證、協(xié)議轉(zhuǎn)換等邏輯,最后調(diào)用后端服務(wù)獲取數(shù)據(jù)并返回給用戶。
在Zuul 2.0中,Netflix團隊進行了重大改進。他們將原本的Servlet轉(zhuǎn)變?yōu)榱艘粋€Netty服務(wù)器,采用了I/O多路復(fù)用模型來處理傳入的I/O請求。同時,他們還對之前的同步阻塞方式調(diào)用后端服務(wù)進行了改進,采用了非阻塞方式的Netty客戶端調(diào)用。經(jīng)過這些改進,Netflix團隊測試發(fā)現(xiàn)性能提升了約20%。
API網(wǎng)關(guān)的功能可以分為兩種類型:一種是可以預(yù)先定義和配置的,如黑白名單設(shè)置、接口動態(tài)路由等;另一種需要根據(jù)具體業(yè)務(wù)來定義和實現(xiàn)。因此,在API網(wǎng)關(guān)的設(shè)計中,關(guān)注擴展性是非常重要的。這意味著您可以隨時在執(zhí)行鏈路上添加自定義邏輯,也可以隨時移除不需要的邏輯,實現(xiàn)所謂的熱插拔功能。這種靈活性允許根據(jù)不同的業(yè)務(wù)需求自定義API網(wǎng)關(guān)的行為,使其更具通用性和適應(yīng)性。
通常情況下,我們可以將每個操作定義為一個過濾器(filter),然后使用責(zé)任鏈模式將這些過濾器串起來。責(zé)任鏈模式允許我們動態(tài)地組織這些過濾器,使它們之間解耦。這意味著我們可以隨時添加或移除過濾器,而不會對其他過濾器產(chǎn)生影響。這種模式使得我們可以靈活地定制API網(wǎng)關(guān)的行為,根據(jù)具體需求來構(gòu)建過濾器鏈,無需擔(dān)心過濾器之間的相互影響。
Zuul采用責(zé)任鏈模式來處理請求。在Zuul 1中,它將過濾器定義為三種類型:預(yù)處理過濾器(pre routing filter)、路由過濾器(routing filter)和后處理過濾器(after routing filter)。每個過濾器都定義了執(zhí)行的順序,在注冊過濾器時,它們會按照順序插入到過濾器鏈中。這意味著當(dāng)Zuul接收到請求時,它會按照順序依次執(zhí)行過濾器鏈中插入的過濾器。這種方式使得可以在請求處理過程中輕松應(yīng)用各種過濾器,以實現(xiàn)不同類型的操作。
圖片
此外,還需要特別注意,為了提高網(wǎng)關(guān)對請求的并行處理能力,通常會使用線程池來同時處理多個請求。然而,這也帶來了一個問題:如果商品服務(wù)響應(yīng)緩慢,導(dǎo)致調(diào)用商品服務(wù)的線程被阻塞,無法釋放,隨著時間的推移,線程池中的線程可能會被商品服務(wù)所占用,從而影響其他服務(wù)。因此,我們需要考慮針對不同的服務(wù)采取線程隔離或保護的策略。從我看來,有兩種主要思路來應(yīng)對這個問題:
如果你后端的服務(wù)拆分得不多,可以針對不同的服務(wù),采用不同的線程池,這樣商品服務(wù)的故障就不會影響到支付服務(wù)和用戶服務(wù)了;
在線程池內(nèi)部可以針對不同的服務(wù)甚至不同的接口做線程的保護。比如說,線程池的最大線程數(shù)是 1000,那么可以給每個服務(wù)設(shè)置一個最多可以使用的配額。
通常情況下,服務(wù)的執(zhí)行時間應(yīng)該非??欤ǔT诤撩爰墑e。使用線程后,線程應(yīng)迅速釋放回線程池,以供后續(xù)請求使用。同時,系統(tǒng)中運行的執(zhí)行中線程數(shù)量應(yīng)該保持較低水平,不會大量增加,這樣可以確保服務(wù)或接口的線程配額不會對正常執(zhí)行產(chǎn)生影響。
然而,一旦發(fā)生故障,某個接口或服務(wù)的響應(yīng)時間變慢,可能會導(dǎo)致線程數(shù)大幅增加。但由于有線程配額的限制,這不會對其他接口或服務(wù)產(chǎn)生負面影響。這種方式有助于隔離故障,確保故障不會蔓延到整個系統(tǒng)。
你在實際應(yīng)用中也可以將這兩種方式結(jié)合,比如說針對不同的服務(wù)使用不同的線程池,在線程池內(nèi)部針對不同的接口設(shè)置配額。
如何在你的系統(tǒng)中引入 API 網(wǎng)關(guān)
目前,我們的電商系統(tǒng)已經(jīng)經(jīng)歷了服務(wù)化改造,其中在服務(wù)層和客戶端之間引入了一個薄薄的Web層。這個Web層承擔(dān)兩項主要任務(wù):首先,它負責(zé)對服務(wù)層接口數(shù)據(jù)進行聚合。舉例來說,商品詳情頁面的接口可能會聚合多個服務(wù)接口的數(shù)據(jù),包括商品信息、用戶信息、店鋪信息以及用戶評論等。其次,Web層需要將HTTP請求轉(zhuǎn)換為RPC請求,并對前端的流量進行一些限制,例如為某些請求添加設(shè)備ID的黑名單等。
因此,在進行改造時,我們可以首先將API網(wǎng)關(guān)從Web層中獨立出來,將協(xié)議轉(zhuǎn)換、限流、黑白名單等功能移到API網(wǎng)關(guān)中進行處理,形成一個獨立的入口網(wǎng)關(guān)層。而對于服務(wù)接口數(shù)據(jù)聚合的操作,
通常有兩種解決思路:一種是獨立出一組網(wǎng)關(guān),專門負責(zé)流量聚合和超時控制,我們通常稱其為流量網(wǎng)關(guān);
另一種是將接口聚合操作提取到一個獨立的服務(wù)層中。這樣,服務(wù)層可以大致分為原子服務(wù)層和聚合服務(wù)層。
在我看來,接口數(shù)據(jù)聚合屬于業(yè)務(wù)操作,與其將其置于通用的網(wǎng)關(guān)層實現(xiàn),不如將其放在更貼近業(yè)務(wù)的服務(wù)層中實現(xiàn)。因此,我更傾向于第二種方案。這種方式有助于更好地劃分職責(zé),使系統(tǒng)更模塊化和可維護。
圖片
同時,我們可以在系統(tǒng)和第三方支付服務(wù),以及登陸服務(wù)之間部署出口網(wǎng)關(guān)服務(wù)。原先,你會在拆分出來的支付服務(wù)中完成對于第三方支付接口所需要數(shù)據(jù)的加密、簽名等操作,再調(diào)用第三方支付接口完成支付請求?,F(xiàn)在,你把對數(shù)據(jù)的加密、簽名的操作放在出口網(wǎng)關(guān)中,這樣一來,支付服務(wù)只需要調(diào)用出口網(wǎng)關(guān)的統(tǒng)一支付接口就可以了。
在引入了 API 網(wǎng)關(guān)之后,我們的系統(tǒng)架構(gòu)就變成了下面這樣:
圖片
強調(diào)的重點:
API網(wǎng)關(guān)可以劃分為兩類:入口網(wǎng)關(guān)和出口網(wǎng)關(guān)。入口網(wǎng)關(guān)具有多種功能,包括隔離客戶端和微服務(wù)、提供協(xié)議轉(zhuǎn)換、實施安全策略、進行認證、限流以及實現(xiàn)熔斷等功能。出口網(wǎng)關(guān)的主要作用是為調(diào)用第三方服務(wù)提供統(tǒng)一的出口,它可以執(zhí)行統(tǒng)一的認證、授權(quán)、審計和訪問控制等任務(wù),以確保與外部系統(tǒng)的通信安全和合規(guī)。
API網(wǎng)關(guān)的關(guān)鍵在于性能和可擴展性的實現(xiàn)。為了提高網(wǎng)關(guān)的性能,可以采用多路I/O復(fù)用模型和線程池并發(fā)處理請求。而為了提升網(wǎng)關(guān)的可擴展性,可以使用責(zé)任鏈模式來組織和管理過濾器,以便輕松添加、移除或修改過濾器,以滿足不同的需求。
API 網(wǎng)關(guān)中的線程池可以針對不同的接口或者服務(wù)做隔離和保護,這樣可以提升網(wǎng)關(guān)的可用性;
API 網(wǎng)關(guān)可以替代原本系統(tǒng)中的 Web 層,將 Web 層中的協(xié)議轉(zhuǎn)換、認證、限流等功能挪入到 API 網(wǎng)關(guān)中,將服務(wù)聚合的邏輯下沉到服務(wù)層。
API網(wǎng)關(guān)不僅提供了方便的API調(diào)用方式,還能將一些服務(wù)治理功能獨立出來,以實現(xiàn)更好的復(fù)用性。盡管在性能方面可能會有一些犧牲,但通常情況下,使用成熟的開源API網(wǎng)關(guān)組件,這些性能損失是可以接受的。因此,當(dāng)您的微服務(wù)系統(tǒng)變得越來越復(fù)雜時,可以考慮將API網(wǎng)關(guān)作為整個系統(tǒng)的門面,以簡化系統(tǒng)架構(gòu)并提供更好的可維護性。