選擇Kong作為你的API網(wǎng)關(guān)
Kong(https://github.com/Kong/kong)是一個云原生,高效,可擴展的分布式 API 網(wǎng)關(guān)。自 2015 年在 github 開源后,廣泛受到關(guān)注,目前已收獲 1.68w+ 的 star,其核心價值在于高性能和可擴展性。
為什么需要 API 網(wǎng)關(guān)

在微服務架構(gòu)之下,服務被拆的非常零散,降低了耦合度的同時也給服務的統(tǒng)一管理增加了難度。如上圖左所示,在舊的服務治理體系之下,鑒權(quán),限流,日志,監(jiān)控等通用功能需要在每個服務中單獨實現(xiàn),這使得系統(tǒng)維護者沒有一個全局的視圖來統(tǒng)一管理這些功能。API 網(wǎng)關(guān)致力于解決的問題便是為微服務納管這些通用的功能,在此基礎(chǔ)上提高系統(tǒng)的可擴展性。如右圖所示,微服務搭配上 API 網(wǎng)關(guān),可以使得服務本身更專注于自己的領(lǐng)域,很好地對服務調(diào)用者和服務提供者做了隔離。
為什么是 Kong
SpringCloud 玩家肯定都聽說過 Zuul 這個路由組件,包括 Zuul2 和 Springcloud Gateway 等框架,在國內(nèi)的知名度都不低。沒錯,我稱呼這些為組件 Or 框架,而 Kong 則更襯的上產(chǎn)品這個詞。在此我們可以簡單對比下 Zuul 和 Kong。
舉例而言,如果選擇使用 Zuul,當需要為應用添加限流功能,由于 Zuul 只提供了基本的路由功能,開發(fā)者需要自己研發(fā) Zuul Filter,可能你覺得一個功能還并不麻煩,但如果在此基礎(chǔ)上對 Zuul 提出更多的要求,很遺憾,Zuul 使用者需要自行承擔這些復雜性。而對于 Kong 來說,限流功能就是一個插件,只需要簡單的配置,即可開箱即用。
Kong 的插件機制是其高可擴展性的根源,Kong 可以很方便地為路由和服務提供各種插件,網(wǎng)關(guān)所需要的基本特性,Kong 都如數(shù)支持:
-
云原生: 與平臺無關(guān),Kong可以從裸機運行到Kubernetes
-
動態(tài)路由:Kong 的背后是 OpenResty+Lua,所以從 OpenResty 繼承了動態(tài)路由的特性
-
熔斷
-
健康檢查
-
日志: 可以記錄通過 Kong 的 HTTP,TCP,UDP 請求和響應。
-
鑒權(quán): 權(quán)限控制,IP 黑白名單,同樣是 OpenResty 的特性
-
SSL: Setup a Specific SSL Certificate for an underlying service or API.
-
監(jiān)控: Kong 提供了實時監(jiān)控插件
-
認證: 如數(shù)支持 HMAC, JWT, Basic, OAuth2.0 等常用協(xié)議
-
限流
-
REST API: 通過 Rest API 進行配置管理,從繁瑣的配置文件中解放
-
可用性: 天然支持分布式
-
高性能: 背靠非阻塞通信的 nginx,性能自不用說
-
插件機制: 提供眾多開箱即用的插件,且有易于擴展的自定義插件接口,用戶可以使用 Lua 自行開發(fā)插件
上面這些特性中,反復提及了 Kong 背后的 OpenResty,實際上,使用 Kong 之后,Nginx 可以完全摒棄,Kong 的功能是 Nginx 的父集。
而 Zuul 除了基礎(chǔ)的路由特性以及其本身和 SpringCloud 結(jié)合較為緊密之外,并無任何優(yōu)勢。
Kong 的架構(gòu)

從技術(shù)的角度講,Kong 可以認為是一個 OpenResty 應用程序。OpenResty 運行在 Nginx 之上,使用 Lua 擴展了 Nginx。Lua 是一種非常容易使用的腳本語言,可以讓你在 Nginx 中編寫一些邏輯操作。之前我們提到過一個概念 Kong = OpenResty + Nginx + Lua,但想要從全局視角了解 Kong 的工作原理,還是直接看源碼比較直接。我們定位到本地的 Kong 文件夾,按照上圖中的目錄層級來識識 Kong 的廬山真面目。
-
Kong 文件下包含了全部源碼和必要組件,分析他們,我們便得到了 Kong 的架構(gòu)。0.13.x 是目前 Kong 的最新版本。
-
從 2 號塊中可以看到 nginx.conf ,這其實便是一個標準的 Nginx 目錄結(jié)構(gòu),這也揭示了 Kong 其實就是運行在 Nginx 的基礎(chǔ)之上,而進行的二次封裝。由 share 文件夾向下展開下一次分析。
-
share 文件夾中包含了 OpenResty 的相關(guān)內(nèi)容,其實背后就是一堆 Lua 腳本,例如 lapis 包含了數(shù)據(jù)庫操作,Nginx 生命周期,緩存控制等必要的 Lua 腳本,logging 包含了日志相關(guān)的 Lua 腳本,resty 包含了 dns,健康檢查等相關(guān)功能的 Lua 腳本…而其中的 kong 目錄值得我們重點分析,他包含了 Kong 的核心對象。
-
api 和 core 文件夾,封裝了 Kong 對 service,route,upstream,target 等核心對象的操作代碼(這四個核心對象將會在下面的小節(jié)重點介紹),而 plugins 文件夾則是 Kong 高可擴展性的根源,存放了 kong 的諸多擴展功能。
-
plugins 文件夾包含了上一節(jié)提到的 Kong 的諸多插件功能,如權(quán)限控制插件,跨域插件,jwt 插件,oauth2 插件…如果需要自定義插件,則需要將代碼置于此處。
從上述文件夾瀏覽下來,大概可以看到它和 Nginx 的相似之處,并在此基礎(chǔ)之上借助于 Lua 對自身的功能進行了拓展,除了 nginx.conf 中的配置,和相對固定的文件層級,Kong 還需要連接一個數(shù)據(jù)庫來管理路由配置,服務配置,upstream 配置等信息,是的,由于 Kong 支持動態(tài)路由的特性,所以幾乎所有動態(tài)的配置都不是配置在文件中,而是借助于 Postgres 或者 Cassandra 進行管理。

Kong 對外暴露了 Restful API,最終的配置便是落地在了數(shù)據(jù)庫之中。
Kong 的管理方式
通過文件夾結(jié)構(gòu)的分析,以及數(shù)據(jù)庫中的表結(jié)構(gòu),我們已經(jīng)對 Kong 的整體架構(gòu)有了一個基本的認識,但肯定還存在一個疑問:我會配置 Nginx 來控制路由,但這個 Kong 應當怎么配置才能達到相同的目的呢?莫急,下面來看看 Kong 如何管理配置。
Kong 簡單易用的背后,便是因為其所有的操作都是基于 HTTP Restful API 來進行的。

其中 8000/8443 分別是 Http 和 Https 的轉(zhuǎn)發(fā)端口,等價于 Nginx 默認的 80 端口,而 8001 端口便是默認的管理端口,我們可以通過 HTTP Restful API 來動態(tài)管理 Kong 的配置。
一個典型的 Nginx 配置
upstream helloUpstream { server localhost:3000 weight=100; } server { listen 80; location /hello { proxy_pass http://helloUpstream; } }
如上這個簡單的 Nginx 配置,便可以轉(zhuǎn)換為如下的 Http 請求。
對應的 Kong 配置
# 配置 upstream curl -X POST http://localhost:8001/upstreams --data "name=helloUpstream" # 配置 target curl -X POST http://localhost:8001/upstreams/hello/targets --data "target=localhost:3000" --data "weight=100" # 配置 service curl -X POST http://localhost:8001/services --data "name=hello" --data "host=helloUpstream" # 配置 route curl -X POST http://localhost:8001/routes --data "paths[]=/hello" --data "service.id=8695cc65-16c1-43b1-95a1-5d30d0a50409"
這一切都是動態(tài)的,無需手動 reload nginx.conf。
我們?yōu)?Kong 新增路由信息時涉及到了 upstream,target,service,route 等概念,他們便是 Kong 最最核心的四個對象。(你可能在其他 Kong 的文章中見到了 api 這個對象,在最新版本 0.13 中已經(jīng)被棄用,api 已經(jīng)由 service 和 route 替代)
從上面的配置以及他們的字面含義大概能夠推測出他們的職責,upstream 是對上游服務器的抽象;target 代表了一個物理服務,是 ip + port 的抽象;service 是抽象層面的服務,他可以直接映射到一個物理服務(host 指向 ip + port),也可以指向一個 upstream 來做到負載均衡;route 是路由的抽象,他負責將實際的 request 映射到 service。
他們的關(guān)系如下
upstream 和 target :1 對 n
service 和 upstream :1 對 1 或 1 對 0 (service 也可以直接指向具體的 target,相當于不做負載均衡)
service 和 route:1 對 n
高可擴展性的背后—插件機制
Kong 的另一大特色便是其插件機制,這也是我認為的 Kong 最優(yōu)雅的一個設計。
文章開始時我們便提到一點,微服務架構(gòu)中,網(wǎng)關(guān)應當承擔所有服務共同需要的那部分功能,這一節(jié)我們便來介紹下,Kong 如何添加 jwt 插件,限流插件。
插件(Plugins)裝在哪兒?對于部分插件,可能是全局的,影響范圍是整個 Kong 服務;大多數(shù)插件都是裝在 service 或者 route 之上。這使得插件的影響范圍非常靈活,我們可能只需要對核心接口進行限流控制,只需要對部分接口進行權(quán)限控制,這時候,對特定的 service 和 route 進行定向的配置即可。
為 hello 服務添加50次/秒的限流
curl -X POST http://localhost:8001/services/hello/plugins \ --data "name=rate-limiting" \ --data "config.second=50"
為 hello 服務添加 jwt 插件
curl -X POST http://localhost:8001/services/login/plugins \ --data "name=jwt"
同理,插件也可以安裝在 route 之上
curl -X POST http://localhost:8001/routes/{routeId}/plugins \ --data "name=rate-limiting" \ --data "config.second=50" curl -X POST http://localhost:8001/routes/{routeId}/plugins \ --data "name=jwt"
在官方文檔中,我們可以獲取全部的插件 https://konghq.com/plugins/,部分插件需要收費的企業(yè)版才可使用。

總結(jié)
Kong 是目前市場上相對較為成熟的開源 API 網(wǎng)關(guān)產(chǎn)品,無論是性能,擴展性,還是功能特性,都決定了它是一款優(yōu)秀的產(chǎn)品,對 OpenResty 和 Lua 感興趣的同學,Kong 也是一個優(yōu)秀的學習參考對象?;?OpenResty,可以在現(xiàn)有 Kong 的基礎(chǔ)上進行一些擴展,從而實現(xiàn)更復雜的特性,比如我司內(nèi)部的 ABTest 插件和定制化的認證插件,開發(fā)成本都相對較低。Kong 系列的文章將會在以后持續(xù)連載。
Kirito 有話說:感謝閱讀,為了博主更好地創(chuàng)作,提供更多的原創(chuàng)文章,請大家務必!務必!務必!幫我點擊閱讀一下明天即將發(fā)出來的公眾號文章?。。「兄x老鐵~
閱讀擴展
初識 Kong 之負載均衡 https://www.cnkirito.moe/kong-loadbalance/
Kong 集成 Jwt 插件 https://www.cnkirito.moe/kong-jwt/
免責聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!