微服務閘道器:從對比到選型,由理論到實踐

語言: CN / TW / HK

常用API閘道器的對比和選型,並講解我司自研的微服務閘道器,乾貨滿滿!

大家好,我是樓仔!微服務近幾年非常火,圍繞微服務的技術生態也比較多,比如微服務閘道器、Docker、Kubernetes等。

我是於2019年開始接觸微服務閘道器,當時和公司的一位同事一起開發,由於技術能力有限,我只負責閘道器後臺,後續微服務閘道器的迭代,我其實沒有參與,不過後來抽空看了微服務閘道器前臺的程式碼,所以對這套微服務閘道器的實現原理算是基本掌握。

最近在寫技術棧相關的文章,剛好寫到微服務閘道器,就把之前學習的知識進行簡單總結,同時也把市面上常用的微服務閘道器進行梳理,一方面便於後續技術選型,另一方面也算是給自己一個交代。下面是文章目錄:

API閘道器基礎

什麼是API閘道器

API閘道器是一個伺服器,是系統的唯一入口。從面向物件設計的角度看,它與外觀模式類似。

API閘道器封裝了系統內部架構,為每個客戶端提供一個定製的API。它可能還具有其它職責,如身份驗證、監控、負載均衡、快取、協議轉換、限流熔斷、靜態響應處理。

API閘道器方式的核心要點是,所有的客戶端和消費端都通過統一的閘道器接入微服務,在閘道器層處理所有的非業務功能。通常,閘道器也是提供REST/HTTP的訪問API。

閘道器的主要功能

微服務閘道器作為微服務後端服務的統一入口,它可以統籌管理後端服務,主要分為資料平面和控制平面:

  • 資料平面主要功能是接入使用者的HTTP請求和微服務被拆分後的聚合。使用微服務閘道器統一對外暴露後端服務的API和契約,路由和過濾功能正是閘道器的核心能力模組。另外,微服務閘道器可以實現攔截機制和專注跨橫切面的功能,包括協議轉換、安全認證、熔斷限流、灰度釋出、日誌管理、流量監控等。

  • 控制平面主要功能是對後端服務做統一的管控和配置管理。例如,可以控制閘道器的彈性伸縮;可以統一下發配置;可以對閘道器服務新增標籤;可以在微服務閘道器上通過配置Swagger功能統一將後端服務的API契約暴露給使用方,完成文件服務,提高工作效率和降低溝通成本。

  • 路由功能:路由是微服務閘道器的核心能力。通過路由功能微服務閘道器可以將請求轉發到目標微服務。在微服務架構中,閘道器可以結合註冊中心的動態服務發現,實現對後端服務的發現,呼叫方只需要知道閘道器對外暴露的服務API就可以透明地訪問後端微服務。

  • 負載均衡:API閘道器結合負載均衡技術,利用Eureka或者Consul等服務發現工具,通過輪詢、指定權重、IP地址雜湊等機制實現下游服務的負載均衡。

  • 統一鑑權:一般而言,無論對內網還是外網的介面都需要做使用者身份認證,而使用者認證在一些規模較大的系統中都會採用統一的單點登入(Single Sign On)系統,如果每個微服務都要對接單點登入系統,那麼顯然比較浪費資源且開發效率低。API閘道器是統一管理安全性的絕佳場所,可以將認證的部分抽取到閘道器層,微服務系統無須關注認證的邏輯,只關注自身業務即可。

  • 協議轉換:API閘道器的一大作用在於構建異構系統,API閘道器作為單一入口,通過協議轉換整合後臺基於REST、AMQP、Dubbo等不同風格和實現技術的微服務,面向Web Mobile、開放平臺等特定客戶端提供統一服務。

  • 指標監控:閘道器可以統計後端服務的請求次數,並且可以實時地更新當前的流量健康狀態,可以對URL粒度的服務進行延遲統計,也可以使用Hystrix Dashboard檢視後端服務的流量狀態及是否有熔斷髮生。

  • 限流熔斷:在某些場景下需要控制客戶端的訪問次數和訪問頻率,一些高併發系統有時還會有限流的需求。在閘道器上可以配置一個閾值,當請求數超過閾值時就直接返回錯誤而不繼續訪問後臺服務。當出現流量洪峰或者後端服務出現延遲或故障時,閘道器能夠主動進行熔斷,保護後端服務,並保持前端使用者體驗良好。

  • 黑白名單:微服務閘道器可以使用系統黑名單,過濾HTTP請求特徵,攔截異常客戶端的請求,例如DDoS攻擊等侵蝕頻寬或資源迫使服務中斷等行為,可以在閘道器層面進行攔截過濾。比較常見的攔截策略是根據IP地址增加黑名單。在存在鑑權管理的路由服務中可以通過設定白名單跳過鑑權管理而直接訪問後端服務資源。

  • 灰度釋出:微服務閘道器可以根據HTTP請求中的特殊標記和後端服務列表元資料標識進行流量控制,實現在使用者無感知的情況下完成灰度釋出。

  • 流量染色:和灰度釋出的原理相似,閘道器可以根據HTTP請求的Host、Head、Agent等標識對請求進行染色,有了閘道器的流量染色功能,我們可以對服務後續的呼叫鏈路進行跟蹤,對服務延遲及服務執行狀況進行進一步的鏈路分析。

  • 文件中心:閘道器結合Swagger,可以將後端的微服務暴露給閘道器,閘道器作為統一的入口給介面的使用方提供檢視後端服務的API規範,不需要知道每一個後端微服務的Swagger地址,這樣閘道器起到了對後端API聚合的效果。

  • 日誌審計:微服務閘道器可以作為統一的日誌記錄和收集器,對服務URL粒度的日誌請求資訊和響應資訊進行攔截。

API閘道器選型

常用API閘道器

先簡單看一下市面上常用的API閘道器:

Nginx

Nginx是一個高效能的HTTP和反向代理伺服器。 Nginx一方面可以做反向代理,另外一方面可以做靜態資源伺服器,介面使用Lua動態語言可以完成靈活的定製功能。

Nginx 在啟動後,會有一個 Master 程序和多個 Worker 程序,Master 程序和 Worker 程序之間是通過程序間通訊進行互動的,如圖所示。Worker 工作程序的阻塞點是在像 select()、epoll_wait() 等這樣的 I/O 多路複用函式呼叫處,以等待發生資料可讀 / 寫事件。Nginx 採用了非同步非阻塞的方式來處理請求,也就是說,Nginx 是可以同時處理成千上萬個請求的。

Zuul

Zuul 是 Netflix 開源的一個API閘道器元件,它可以和 Eureka、Ribbon、Hystrix 等元件配合使用。社群活躍,融合於 SpringCloud 完整生態,是構建微服務體系前置閘道器服務的最佳選型之一。

Zuul 的核心是一系列的過濾器,這些過濾器可以完成以下功能:

  • 統一鑑權 + 動態路由 + 負載均衡 + 壓力測試

  • 審查與監控:與邊緣位置追蹤有意義的資料和統計結果,從而帶來精確的生產檢視。

  • 多區域彈性:跨越 AWS Region 進行請求路由,旨在實現 ELB(Elastic Load Balancing,彈性負載均衡)使用的多樣化,以及讓系統的邊緣更貼近系統的使用者。

Zuul 目前有兩個大的版本: Zuul1 和 Zuul2

Zuul1 是基於 Servlet 框架構建,如圖所示,採用的是阻塞和多執行緒方式,即一個執行緒處理一次連線請求,這種方式在內部延遲嚴重、裝置故障較多情況下會引起存活的連線增多和執行緒增加的情況發生。

Netflix 釋出的 Zuul2 有重大的更新,它執行在非同步和無阻塞框架上,每個 CPU 核一個執行緒,處理所有的請求和響應,請求和響應的生命週期是通過事件和回撥來處理的,這種方式減少了執行緒數量,因此開銷較小。

Spring Cloud GetWay

Spring Cloud Gateway 是Spring Cloud的一個全新的API閘道器專案,目的是為了替換掉Zuul1,它基於Spring5.0 + SpringBoot2.0 + WebFlux(基於⾼效能的Reactor模式響應式通訊框架Netty,非同步⾮阻塞模型)等技術開發,效能⾼於Zuul,官⽅測試, Spring Cloud GateWay是Zuul的1.6倍 ,旨在為微服務架構提供⼀種簡單有效的統⼀的API路由管理⽅式。

Spring Cloud Gateway可以與Spring Cloud Discovery Client(如Eureka)、Ribbon、Hystrix等元件配合使用, 實現路由轉發、負載均衡、熔斷、鑑權、路徑重寫、⽇志監控等,並且Gateway還內建了限流過濾器,實現了限流的功能。

Kong

Kong是一款基於OpenResty(Nginx + Lua模組)編寫的高可用、易擴充套件的,由Mashape公司開源的API Gateway專案。 Kong是基於NGINX和Apache Cassandra或PostgreSQL構建的 ,能提供易於使用的RESTful API來操作和配置API管理系統,所以它可以水平擴充套件多個Kong伺服器,通過前置的負載均衡配置把請求均勻地分發到各個Server,來應對大批量的網路請求。

Kong主要有三個元件:

  • Kong Server :基於Nginx的伺服器,用來接收API請求。

  • Apache Cassandra/PostgreSQL :用來儲存操作資料。

  • Kong dashboard:官方推薦UI管理工具,也可以使用 restfull 方式管理admin api。

Kong採用外掛機制進行功能定製,外掛集(可以是0或N個)在API請求響應迴圈的生命週期中被執行。外掛使用Lua編寫,目前已有幾個基礎功能: HTTP基本認證、金鑰認證、CORS(Cross-Origin Resource Sharing,跨域資源共享)、TCP、UDP、檔案日誌、API請求限流、請求轉發以及Nginx監控。

Kong閘道器具有以下的特性:

  • 可擴充套件性: 通過簡單地新增更多的伺服器,可以輕鬆地進行橫向擴充套件,這意味著您的平臺可以在一個較低負載的情況下處理任何請求;

  • 模組化: 可以通過新增新的外掛進行擴充套件,這些外掛可以通過RESTful Admin API輕鬆配置;

  • 在任何基礎架構上執行: Kong閘道器可以在任何地方都能執行。您可以在雲或內部網路環境中部署Kong,包括單個或多個數據中心設定,以及public,private 或invite-only APIs。

Traefik

Træfɪk 是一個為了讓部署微服務更加便捷而誕生的現代HTTP反向代理、負載均衡工具。它支援多種後臺 (Docker, Swarm, Kubernetes, Marathon, Mesos, Consul, Etcd, Zookeeper, BoltDB, Rest API, file…) 來自動化、動態的應用它的配置檔案設定。

重要特性:

  • 它非常快,無需安裝其他依賴,通過Go語言編寫的單一可執行檔案;

  • 多種後臺支援:Docker, Swarm, Kubernetes, Marathon, Mesos, Consul, Etcd;

  • 支援支援Rest API、Websocket、HTTP/2、Docker映象;

  • 監聽後臺變化進而自動化應用新的配置檔案設定;

  • 配置檔案熱更新,無需重啟程序;

  • 後端斷路器、負載均衡、容錯機制;

  • 清爽的前端頁面,可監控服務指標。

關於Traefik的更多內容,可以檢視官網:https://traefik.cn/

API閘道器對比

上面是閘道器對比截圖,偷個懶,大家主要關注Kong、Traefik和Zuul即可:

  • 開源社群活躍度 來看,無疑是Kong和Traefik較好;

  • 成熟度 來看,較好的是Kong、Tyk、Traefik;

  • 效能 來看,Kong要比其他幾個領先一些;

  • 架構優勢 的擴充套件性來看,Kong、Tyk有豐富的外掛,Ambassador也有外掛但不多,而Zuul是完全需要自研,但Zuul由於與Spring Cloud深度整合,使用度也很高,近年來Istio服務網格的流行,Ambassador因為能夠和Istio無縫整合也是相當大的優勢。

下面是其它網友的思考結論,可供參考:

  • 效能:Nginx+Lua形式必然是高於Java語言實現的閘道器的,Java技術棧裡面Zuul1.0是基於Servlet實現的,剩下都是基於webflux實現,效能是高於基於Servlet實現的。 在效能方面我覺得選擇閘道器可能不算那麼重要,多加幾臺機器就可以搞定。

  • 可維護性和擴充套件性:Nginx+Lua這個組合掌握的人不算多,如果團隊有大神,大佬們就隨意了,當沒看到這段話,對於一般團隊來說的話,選擇自己團隊擅長的語言更重要。Java技術棧下的3種閘道器,對於Zuul和Spring Cloud Getway需要或多或少要搞一些整合和配置頁面來維護,但是對於Soul我就無腦看看文章,需要哪個搬哪個好了,尤其是可以無腦對接Dubbo美滋滋,此外Soul2.0以後版本可以擺脫ZK,在我心裡再無詬病,我就喜歡無腦操作。

  • 高可用:對於閘道器高可用基本都是統一的策略都是採用多機器部署的方式,前面掛一個負載,對於而外需要用的一些元件大家注意一下。

基於Traefik自研的微服務閘道器

這個是我司自研的微服務閘道器,基於Traefik進行開發,下面從技術選型、閘道器框架、閘道器後臺、協議轉換進行講解,絕對乾貨!

技術棧選型

  • Traefik:一款開源的反向代理與負載均衡工具,它最大的優點是能夠與常見的微服務系統直接整合,可以實現自動化動態配置。traefik較為輕量,非常易於使用和設定,效能比較好,已在全球範圍內用於生產環境。

  • Etcd:一個Go言編寫的分散式、高可用的一致性鍵值儲存系統,用於提供可靠的分散式鍵值儲存、配置共享和服務發現等功能。(更多內容可以檢視文章 肝了一個月的ETCD,從Raft原理到實踐

  • Go:併發能力強,效能媲美C,處理能力是PHP的4倍,效率高,語法簡單,易上手,開發效率接近PHP。

閘道器框架

整個閘道器框架分為3塊:

  • 閘道器後臺(hal-fe和hal-admin):用於應用、服務和外掛的配置,然後將配置資訊釋出到ETCD;

  • Traefik:讀取ETCD配置,根據配置資訊對請求進行路由分發,如果需要鑑權,會直接通過hal-agent模組進行統一鑑權。鑑權完畢後,如果是Http請求,直接打到下游服務,如果是Grpc和Thrift協議,會通過hal-proxy模組進行協議轉換。

  • 協議轉換模組:讀取ETCD配置,對Traefik分發過來的請求,進行Grpc和Thrift協議轉換(更多內容可以檢視文章 RPC框架:從原理到選型,一文帶你搞懂RPC ),並通過服務發現機制,獲取服務下游機器,並通過負載均衡,將轉換後的資料打到下游服務機器。

閘道器後臺

主要由3大模組組成:

  • 應用:主要包括應用名、域名、路徑字首、所屬組、狀態等,比如印度海外商城、印度社群;

  • 服務:主要包括服務名、註冊方式、協議型別、所屬組、狀態等,比如評論服務、地址服務、搜尋服務。

  • 外掛:主要包括外掛名稱、外掛型別、外掛屬性配置等,比如路徑字首替換外掛、鑑權外掛。

一個應用只能繫結一個服務,但是可以繫結多個外掛。通過後臺完成閘道器配置後,將這些配置資訊生成Config檔案,釋出到ETCD中,Config檔案需要遵循嚴格的資料格式,比如Traefix配置需要遵循官方的檔案配置格式,才能被Traefik識別。

協議轉換模組

hal-proxy模組是整個微服務閘道器最複雜,也是技術含量最高的模組,所以給大家詳細講解一下。

問題引入

在講這個模組前,我們先看下面幾個問題:

  • 當請求從上游的trafik過來時,需要知道訪問下游的機器IP和埠,才能將請求傳送給下游,這些機器如何獲取呢?

  • 有了機器後,我們需要和下游機器建立連線,如果連線用一次就直接釋放,肯定對服務會造成很大的壓力,這就需要引入Client快取池,那這個Client快取池我們又該如何實現呢?

  • 最後就是需要對協議進行轉換,因為不同的下游服務,支援的協議型別是不一樣的,這個閘道器又是如何動態支援的呢?

實現原理

我們還是先看一下hal-proxy內部有哪些模組,首先是Resolver模組,這個模組的是什麼作用呢?這裡我簡單介紹一下,目前公司內部通過服務獲取到機器列表的方式有多種,比如MIS平臺、服務樹等,也就是有的是通過平臺配置的,有的是直接掛在服務樹下,無論哪種方式,我們都通過服務名,通過一定的方式,找到該服務下面所有的主機。

所以Resolver模組的作用,其實就是通過服務名,找到該服務下的所有機器的IP和服務埠,然後持久化到記憶體中,並定時更新。

協議模組就是支援不同的協議轉換,每個協議型別的轉換,都需要單獨實現, 這些協議轉換,無非就是先通過機器IP和埠初始化Client,然後再將資料進行轉換後,直接傳送到下游的機器。

最後就是連線池,之前我們其實也用到go自帶的pool來做,但是當對pool資料進行更新時,需要加鎖,所以效能一直起不來,後來 改成了環形佇列,然後對資料的操作全部通過原子操作方式,就實現了無鎖操作,大大提高的併發效能。 環形佇列的程式碼,也給你安排上,可以直接看這篇文章 Go語言核心手冊-10.原子操作

實現邏輯

這個是hal-proxy的邏輯實現圖,畫了2天,包含所有核心物件的互動方式,這裡就不去細講,能掌握多少,靠大家自己領悟,如果有任何疑問(或者看不清圖片),可以關注我公眾號,加我微信溝通。

盡信書則不如無書,因個人能力有限,難免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激。

往期精選: