API網關起到的作用(904)
API網關(API Gateway)不是一個開源組件,而是一種架構模式,它是將一些服務共有的功能整合在一起,獨立部署為單獨的一層,用來解決一些服務治理的問題。你可以把它看作系統的邊界,它可以對出入系統的流量做統一的管控。
在我看來,API網關可以分為兩類:一類叫做入口網關,一類叫做出口網關。
入口網關是我們經常使用的網關種類,它部署在負載均衡服務器和應用服務器之間,主要有幾方面的作用。
-
它提供客戶端一個統一的接入地址,API網關可以將用戶的請求動態路由到不同的業務服務上,並且做一些必要的協議轉換工作。在你的系統中,你部署的微服務對外暴露的協議可能不同:有些提供的是HTTP服務;有些已經完成RPC改造,對外暴露RPC服務;有些遺留系統可能還暴露的是Web Service服務。API網關可以對客戶端屏蔽這些服務的部署地址,以及協議的細節,給客戶端的調用帶來很大的便捷。
-
另一方面,在API網關中,我們可以植入一些服務治理的策略,比如服務的熔斷、降級,流量控制和分流等等(關於服務降級和流量控制的細節,我會在后面的課程中具體講解,在這里,你只要知道它們可以在API網關中實現就可以了)。
-
再有,客戶端的認證和授權的實現,也可以放在API網關中。你要知道,不同類型的客戶端使用的認證方式是不同的。在我之前項目中,手機APP使用Oauth協議認證,HTML5端和Web端使用Cookie認證,內部服務使用自研的Token認證方式。這些認證方式在API網關上,可以得到統一處理,應用服務不需要了解認證的細節。
-
另外,API網關還可以做一些與黑白名單相關的事情,比如針對設備ID、用戶IP、用戶ID等維度的黑白名單。
5.最后,在API網關中也可以做一些日志記錄的事情,比如記錄HTTP請求的訪問日志,分布式追蹤系統時,提到的標記一次請求的requestId,也可以在網關中來生成。
出口網關就沒有這么豐富的功能和作用了。在系統開發中,會依賴很多外部的第三方系統,比如典型的例子:第三方賬戶登錄、使用第三方工具支付等等。可以在應用服務器和第三方系統之間,部署出口網關,在出口網關中,對調用外部的API做統一的認證、授權,審計以及訪問控制。
API網關要如何實現
在實現一個API網關時,首先要考慮的是它的性能。這很好理解,API入口網關承擔從客戶端的所有流量。假如業務服務處理時間是10ms,而API網關的耗時在1ms,那么相當於每個接口的響應時間都要增加10%,這對於性能的影響無疑是巨大的。而提升API網關性能的關鍵還是在I/O模型上,這里只是舉一個例子來說明I/O模型對於性能的影響。
Netfix開源的API網關Zuul,在1.0版本的時候使用的是同步阻塞I/O模型,整體系統其實就是一個servlet,在接收到用戶的請求,然后執行在網關中配置的認證、協議轉換等邏輯之后,調用后端的服務獲取數據返回給用戶。
而在Zuul2.0中,Netfix團隊將servlet改造成了一個netty server(netty服務),采用I/O多路復用的模型處理接入的I/O請求,並且將之前同步阻塞調用后端服務的方式,改造成使用netty client(netty客戶端)非阻塞調用的方式。改造之后,Netfix團隊經過測試發現性能提升了20%左右。
除此之外,API網關中執行的動作有些是可以預先定義好的,比如黑白名單的設置,接口動態路由;有些則是需要業務方依據自身業務來定義。所以,API網關的設計要注意擴展性,也就是你可以隨時在網關的執行鏈路上,增加一些邏輯,也可以隨時下掉一些邏輯(也就是所謂的熱插拔)。
所以一般來說可以把每一個操作定義為一個filter(過濾器),然后使用“責任鏈模式”將這些filter串起來。責任鏈可以動態地組織這些filter,解耦filter之間的關系,無論是增加還是減少filter,都不會對其他的filter有任何的影響。
Zuul就是采用責任鏈模式,Zuul1中將filter定義為三類:pre routing filter(路由前過濾器)、routing filter(路由過濾器)和after routing filter(路由后過濾器)。每一個filter定義了執行的順序,在filter注冊時,會按照順序插入到filter chain(過濾器鏈)中。這樣Zuul在接收到請求時,就會按照順序依次執行插入到filter chain中的filter了。
另外還需要注意的一點是,為了提升網關對於請求的並行處理能力,一般會使用線程池來並行的執行請求。不過,這就帶來一個問題:如果商品服務出現問題,造成響應緩慢,那么調用商品服務的線程就會被阻塞無法釋放,久而久之,線程池中的線程就會被商品服務所占據,那么其他服務也會受到級聯的影響。因此需要針對不同的服務做線程隔離,或者保護。
兩種思路:
-
如果你后端的服務拆分得不多,可以針對不同的服務,采用不同的線程池,這樣商品服務的故障就不會影響到支付服務和用戶服務了;
-
在線程池內部可以針對不同的服務,甚至不同的接口做線程的保護。比如說,線程池的最大線程數是1000,那么可以給每個服務設置一個最多可以使用的配額。
一般來說,服務的執行時間應該在毫秒級別,線程被使用后會很快被釋放,回到線程池給后續請求使用,同時處於執行中的線程數量不會很多,對服務或者接口設置線程的配額,不會影響到正常的執行。可是一旦發生故障,某個接口或者服務的響應時間變長,造成線程數暴漲,但是因為有配額的限制,也就不會影響到其他的接口或者服務了。
你在實際應用中也可以將這兩種方式結合,比如說針對不同的服務使用不同的線程池,在線程池內部針對不同的接口設置配額。
以上就是實現API網關的一些關鍵的點,你如果要自研API網關服務的話可以參考借鑒。另外API網關也有很多開源的實現,目前使用比較廣泛的有以下幾個:
-
Kong是在Nginx中運行的Lua程序。得益於Nginx的性能優勢,Kong相比於其它的開源API網關來說,性能方面是最好的。由於大中型公司對於Nginx運維能力都比較強,所以選擇Kong作為API網關,無論是在性能還是在運維的把控力上,都是比較好的選擇;
-
Zuul是Spring Cloud全家桶中的成員,如果你已經使用了Spring Cloud中的其他組件,那么也可以考慮使用Zuul和它們無縫集成。不過,Zuul1因為采用同步阻塞模型,所以在性能上並不是很高效,而Zuul2推出時間不長,難免會有坑。但是Zuul的代碼簡單易懂,可以很好的把控,並且你的系統的量級很可能達不到Netfix這樣的級別,所以對於Java技術棧的團隊,使用Zuul也是一個不錯的選擇;
-
Tyk是一種Go語言實現的輕量級API網關,有着豐富的插件資源,對於Go語言棧的團隊來說,也是一種不錯的選擇。
那么你要考慮的是,這些開源項目適不適合作為AIP網關供自己使用。以電商系統為例,帶你將API網關引入系統之中。
如何在你的系統中引入API網關呢?
目前為止,我們的電商系統已經經過了服務化改造,在服務層和客戶端之間有一層薄薄的Web層,這個Web層做的事情主要有兩方面:
一方面是對服務層接口數據的聚合。比如,商品詳情頁的接口,可能會聚合服務層中,獲取商品信息、用戶信息、店鋪信息以及用戶評論等多個服務接口的數據;
另一方面Web層還需要將HTTP請求轉換為RPC請求,並且對前端的流量做一些限制,對於某些請求添加設備ID的黑名單等等。
因此,我們在做改造的時候,可以先將API網關從Web層中獨立出來,將協議轉換、限流、黑白名單等事情,挪到API網關中來處理,形成獨立的入口網關層;
而針對服務接口數據聚合的操作,一般有兩種解決思路:
-
再獨立出一組網關專門做服務聚合、超時控制方面的事情,一般把前一種網關叫做流量網關,后一種可以叫做業務網關;
-
抽取獨立的服務層,專門做接口聚合的操作。這樣服務層就大概分為原子服務層和聚合服務層。
我認為,接口數據聚合是業務操作,與其放在通用的網關層來實現,不如放在更貼近業務的服務層來實現,所以,我更傾向於第二種方案。
同時可以在系統和第三方支付服務,以及登陸服務之間部署出口網關服務。原先,你會在拆分出來的支付服務中,完成對於第三方支付接口所需要數據的加密、簽名等操作,再調用第三方支付接口,完成支付請求。現在,你把對數據的加密、簽名的操作放在出口網關中,這樣一來,支付服務只需要調用出口網關的統一支付接口就可以了。
在引入了API網關之后系統架構就變成了下面這樣:
-
API網關分為入口網關和出口網關兩類,入口網關作用很多,可以隔離客戶端和微服務,從中提供協議轉換、安全策略、認證、限流、熔斷等功能。出口網關主要是為調用第三方服務提供統一的出口,在其中可以對調用外部的API做統一的認證、授權,審計以及訪問控制;
-
API網關的實現重點在於性能和擴展性,你可以使用多路I/O復用模型和線程池並發處理,來提升網關性能,使用責任鏈模式來提升網關的擴展性;
-
API網關中的線程池,可以針對不同的接口或者服務做隔離和保護,這樣可以提升網關的可用性;
-
API網關可以替代原本系統中的Web層,將Web層中的協議轉換、認證、限流等功能挪入到API網關中,將服務聚合的邏輯下沉到服務層。