API 網關知識看這篇就足夠了!

本文已經收錄自 JavaGuide (60k+ Star【Java學習+面試指南】 一份涵蓋大部分Java程序員所需要掌握的核心知識。)

本文授權轉載自:https://github.com/javagrowing/JGrowing/blob/master/服務端開發/淺析如何設計一個億級網關.md

1.背景

1.1 什麼是API網關

API網關可以看做系統與外界聯通的入口,我們可以在網關進行處理一些非業務邏輯的邏輯,比如權限驗證,監控,緩存,請求路由等等。

1.2 爲什麼需要API網關

  • RPC協議轉成HTTP。

由於在內部開發中我們都是以RPC協議(thrift or dubbo)去做開發,暴露給內部服務,當外部服務需要使用這個接口的時候往往需要將RPC協議轉換成HTTP協議。

  • 請求路由

在我們的系統中由於同一個接口新老兩套系統都在使用,我們需要根據請求上下文將請求路由到對應的接口。

  • 統一鑑權

對於鑑權操作不涉及到業務邏輯,那麼可以在網關層進行處理,不用下層到業務邏輯。

  • 統一監控

由於網關是外部服務的入口,所以我們可以在這裏監控我們想要的數據,比如入參出參,鏈路時間。

  • 流量控制,熔斷降級

對於流量控制,熔斷降級非業務邏輯可以統一放到網關層。

有很多業務都會自己去實現一層網關層,用來接入自己的服務,但是對於整個公司來說這還不夠。

1.3 統一API網關

統一的API網關不僅有API網關的所有的特點,還有下面幾個好處:

  • 統一技術組件升級

在公司中如果有某個技術組件需要升級,那麼是需要和每個業務線溝通,通常幾個月都搞不定。舉個例子如果對於入口的安全鑑權有重大安全隱患需要升級,如果速度還是這麼慢肯定是不行,那麼有了統一的網關升級是很快的。

  • 統一服務接入

對於某個服務的接入也比較困難,比如公司已經研發出了比較穩定的服務組件,正在公司大力推廣,這個週期肯定也特別漫長,由於有了統一網關,那麼只需要統一網關統一接入。

  • 節約資源

不同業務不同部門如果按照我們上面的做法應該會都自己搞一個網關層,用來做這個事,可以想象如果一個公司有100個這種業務,每個業務配備4臺機器,那麼就需要400臺機器。並且每個業務的開發RD都需要去開發這個網關層,去隨時去維護,增加人力。如果有了統一網關層,那麼也許只需要50臺機器就可以做這100個業務的網關層的事,並且業務RD不需要隨時關注開發,上線的步驟。

2.統一網關的設計

2.1 異步化請求

對於我們自己實現的網關層,由於只有我們自己使用,對於吞吐量的要求並不高所以,我們一般同步請求調用即可。

對於我們統一的網關層,如何用少量的機器接入更多的服務,這就需要我們的異步,用來提高更多的吞吐量。對於異步化一般有下面兩種策略:

  • Tomcat/Jetty+NIO+servlet3

這種策略使用的比較普遍,京東,有贊,Zuul,都選取的是這個策略,這種策略比較適合HTTP。在Servlet3中可以開啓異步。

  • Netty+NIO

Netty爲高併發而生,目前唯品會的網關使用這個策略,在唯品會的技術文章中在相同的情況下Netty是每秒30w+的吞吐量,Tomcat是13w+,可以看出是有一定的差距的,但是Netty需要自己處理HTTP協議,這一塊比較麻煩。

對於網關是HTTP請求場景比較多的情況可以採用Servlet,畢竟有更加成熟的處理HTTP協議。如果更加重視吞吐量那麼可以採用Netty。

2.1.1 全鏈路異步

對於來的請求我們已經使用異步了,爲了達到全鏈路異步所以我們需要對去的請求也進行異步處理,對於去的請求我們可以利用我們rpc的異步支持進行異步請求所以基本可以達到下圖:

img

由在web容器中開啓servlet異步,然後進入到網關的業務線程池中進行業務處理,然後進行rpc的異步調用並註冊需要回調的業務,最後在回調線程池中進行回調處理。

2.2 鏈式處理

在設計模式中有一個模式叫責任鏈模式,他的作用是避免請求發送者與接收者耦合在一起,讓多個對象都有可能接收請求,將這些對象連接成一條鏈,並且沿着這條鏈傳遞請求,直到有對象處理它爲止。通過這種模式將請求的發送者和請求的處理者解耦了。在我們的各個框架中對此模式都有實現,比如servlet裏面的filter,springmvc裏面的Interceptor。

在Netflix Zuul中也應用了這種模式,如下圖所示:

img

這種模式在網關的設計中我們可以借鑑到自己的網關設計:

  • preFilters:前置過濾器,用來處理一些公共的業務,比如統一鑑權,統一限流,熔斷降級,緩存處理等,並且提供業務方擴展。
  • routingFilters: 用來處理一些泛化調用,主要是做協議的轉換,請求的路由工作。
  • postFilters: 後置過濾器,主要用來做結果的處理,日誌打點,記錄時間等等。
  • errorFilters: 錯誤過濾器,用來處理調用異常的情況。

這種設計在有讚的網關也有應用。

2.3 業務隔離

上面在全鏈路異步的情況下不同業務之間的影響很小,但是如果在提供的自定義FiIlter中進行了某些同步調用,一旦超時頻繁那麼就會對其他業務產生影響。所以我們需要採用隔離之術,降低業務之間的互相影響。

2.3.1 信號量隔離

信號量隔離只是限制了總的併發數,服務還是主線程進行同步調用。這個隔離如果遠程調用超時依然會影響主線程,從而會影響其他業務。因此,如果只是想限制某個服務的總併發調用量或者調用的服務不涉及遠程調用的話,可以使用輕量級的信號量來實現。有讚的網關由於沒有自定義filter所以選取的是信號量隔離。

2.3.2 線程池隔離

最簡單的就是不同業務之間通過不同的線程池進行隔離,就算業務接口出現了問題由於線程池已經進行了隔離那麼也不會影響其他業務。在京東的網關實現之中就是採用的線程池隔離,比較重要的業務比如商品或者訂單 都是單獨的通過線程池去處理。但是由於是統一網關平臺,如果業務線衆多,大家都覺得自己的業務比較重要需要單獨的線程池隔離,如果使用的是Java語言開發的話那麼,在Java中線程是比較重的資源比較受限,如果需要隔離的線程池過多不是很適用。如果使用一些其他語言比如Golang進行開發網關的話,線程是比較輕的資源,所以比較適合使用線程池隔離。

2.3.3 集羣隔離

如果有某些業務就需要使用隔離但是統一網關又沒有線程池隔離那麼應該怎麼辦呢?那麼可以使用集羣隔離,如果你的某些業務真的很重要那麼可以爲這一系列業務單獨申請一個集羣或者多個集羣,通過機器之間進行隔離。

2.4 請求限流

流量控制可以採用很多開源的實現,比如阿里最近開源的Sentinel和比較成熟的Hystrix。

一般限流分爲集羣限流和單機限流:

  • 利用統一存儲保存當前流量的情況,一般可以採用Redis,這個一般會有一些性能損耗。
  • 單機限流:限流每臺機器我們可以直接利用Guava的令牌桶去做,由於沒有遠程調用性能消耗較小。

2.5 熔斷降級

這一塊也可以參照開源的實現Sentinel和Hystrix,這裏不是重點就不多提了。

2.6 泛化調用

泛化調用指的是一些通信協議的轉換,比如將HTTP轉換成Thrift。在一些開源的網關中比如Zuul是沒有實現的,因爲各個公司的內部服務通信協議都不同。比如在唯品會中支持HTTP1,HTTP2,以及二進制的協議,然後轉化成內部的協議,淘寶的支持HTTPS,HTTP1,HTTP2這些協議都可以轉換成,HTTP,HSF,Dubbo等協議。

2.6.1泛化調用

如何去實現泛化調用呢?由於協議很難自動轉換,那麼其實每個協議對應的接口需要提供一種映射。簡單來說就是把兩個協議都能轉換成共同語言,從而互相轉換。

img一般來說共同語言有三種方式指定:

  • json:json數據格式比較簡單,解析速度快,較輕量級。在Dubbo的生態中有一個HTTP轉Dubbo的項目是用JsonRpc做的,將HTTP轉化成JsonRpc再轉化成Dubbo。

比如可以將一個 www.baidu.com/id = 1 GET 可以映射爲json:

代碼塊

  • xml:xml數據比較重,解析比較困難,這裏不過多討論。
  • 自定義描述語言:一般來說這個成本比較高需要自己定義語言來進行描述並進行解析,但是其擴展性,自定義個性化性都是最高。例:spring自定義了一套自己的SPEL表達式語言

對於泛化調用如果要自己設計的話JSON基本可以滿足,如果對於個性化的需要特別多的話倒是可以自己定義一套語言。

2.7 管理平臺

上面介紹的都是如何實現一個網關的技術關鍵。這裏需要介紹網關的一個業務關鍵。有了網關之後,需要一個管理平臺如何去對我們上面所描述的技術關鍵進行配置,包括但不限於下面這些配置:

  • 限流
  • 熔斷
  • 緩存
  • 日誌
  • 自定義filter
  • 泛化調用

3.總結

最後一個合理的標準網關應該按照如下去實現:

img

--- 京東 唯品會 有贊 阿里 Zuul
實現關鍵 servlet3.0 netty servlet3.0 servlet3.0 servlet3.0
異步情況 servlet異步,rpc是否異步不清楚 全鏈路異步 全鏈路異步 全鏈路異步 Zuul1同步阻塞,Zuul2異步非阻塞
限流 --- --- 平滑限流。最初是codis,後續換到每個單機的令牌桶限流。 1.基本流控:基於API的QPS做限流。2.運營流控:支持APP流量包,APP+API+USER的流控33.大促流控:APP訪問API的權重流控。阿里開源:Sentinel 提供了jar包:spring-cloud-zuul-ratelimit。1.對請求的目標URL進行限流(例如:某個URL每分鐘只允許調用多少次)。2.對客戶端的訪問IP進行限流(例如:某個IP每分鐘只允許請求多少次)3.對某些特定用戶或者用戶組進行限流(例如:非VIP用戶限制每分鐘只允許調用100次某個API等)4.多維度混合的限流。此時,就需要實現一些限流規則的編排機制。與、或、非等關係。支持四種存儲方式ConcurrentHashMap,Consul,Redis,數據庫。
熔斷降級 --- --- Hystrix --- 只支持服務級別熔斷,不支持URL級別。
隔離 線程池隔離 --- 信號量隔離 --- 線程池隔離,信號量隔離
緩存 redis --- 二級緩存,本地緩存+Codis HDCC 本地緩存,遠程緩存,數據庫 需要自己開發
泛化調用 --- http,https,http1,http2,二進制 dubbo,http,nova hsf,dubbo,http,https,http2,http1 只支持http

4.參考

開源項目推薦

作者的其他開源項目推薦:

  1. JavaGuide:【Java學習+面試指南】 一份涵蓋大部分Java程序員所需要掌握的核心知識。
  2. springboot-guide : 適合新手入門以及有經驗的開發人員查閱的 Spring Boot 教程(業餘時間維護中,歡迎一起維護)。
  3. programmer-advancement : 我覺得技術人員應該有的一些好習慣!
  4. spring-security-jwt-guide :從零入門 !Spring Security With JWT(含權限驗證)後端部分代碼。

公衆號

我的公衆號

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章