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