Kong偵聽四個端口的請求,默認情況是:
8000:此端口是Kong用來監聽來自客戶端的HTTP請求的,並將此請求轉發到您的上游服務。這也是本教程中最主要用到的端口。
8443:此端口是Kong監聽HTTP的請求的端口。該端口具有與8000端口類似的行為,但是它只監聽HTTPS的請求,並不會產生轉發行為。可以通過配置文件來禁用此端口。
8001:用於管理員對KONG進行配置的端口。
8444:用於管理員監聽HTTPS請求的端口。
在本文中,我們將介紹Kong的路由功能,並詳細說明8000端口上的客戶端請求如何根據請求頭、URI或HTTP被代理到配置中的上游服務。
基礎術語:
API:指Kong的API實例。您可以通過管理員身份配置您的API。
Plugin:他指的是Kong的“插件”,它們是在代理生命周期中運行的業務邏輯。可以通過管理員身份進行全局配置,也可針對每個API進行分別配置。
Client:指向Kong的代理端口發出請求的下游客戶,即第三方客戶端。
Upstream service:指的是位於Kong后面的您自己的API服務,客戶端請求被轉發的最終目的地。nginx中的上游服務器。
概述
從整體上來看,Kong偵聽其配置的代理端口上的HTTP請求(默認為8000),並識別正在請求的是哪個上游服務,然后運行在該API上的配置插件(如果沒有則不執行),並將上游的HTTP請求轉發到您自己的API服務。
當客戶端向代理端口發出請求時,Kong將根據API在KONG里的配置情況,來決定將請求傳入到哪個上游服務中。 您可以在KONG里對每個API添加許多屬性,但是有三個是必須要配置的,他們是hosts、uris和methods。如果KONG無法確定API請求的上游服務地址,則會返回一下內容:
HTTP/1.1 404 Not Found Content-Type: application/json Server: kong/<x.x.x> { "message": "no API found with those values" }
回憶下:如何向KONG添加一個API
在之前的添加一個API的指南中,有學習過如何使用8001端口,在KONG服務中添加一個API的操作:
$ curl -i -X POST http://localhost:8001/apis/ \ -d 'name=my-api' \ -d 'upstream_url=http://my-api.com' \ -d 'hosts=example.com' \ -d 'uris=/my-api' \ -d 'methods=GET,HEAD' HTTP/1.1 201 Created ...
該代碼表示用戶成功在Kong里注冊一個名為“my-api”的API,可通過訪問“http://example.com”發送請求。它還指定了一些HTTP請求的屬性,但請注意,這里有且只有一個HOST,URIS和METHODS屬性。當完成此配置后,以后所有的符合此host、uris和methods的請求,都將由KONG來代理過濾轉發。Kong是一個透明的代理,它會將請求轉發給您的上游服務,除了添加諸如Connection之類的各種標題。
轉發功能
現在我們來討論一下,一個HTTP請求是如何與API配置屬性(如host、uris、methods)相匹配的。需要注意的是,這三個字段(h、u、m)都是可選的,但至少要有一個被指定。對於客戶端請求與API的匹配:
· 請求必須包含所有已配置的字段
· 請求中的字段值必須與至少一個已配置的值相匹配(盡管字段配置接受一個或多個值,請求只需要考慮匹配其中之一)
讓我們來看幾個例子。請看如下一個API配置:
{ "name": "my-api", "upstream_url": "http://my-api.com", "hosts": ["example.com", "service.com"], "uris": ["/foo", "/bar"], "methods": ["GET"] }
與該API相匹配的一些請求可能是:
GET /foo HTTP/1.1 Host: example.com GET /bar HTTP/1.1 Host: service.com GET /foo/hello/world HTTP/1.1 Host: example.com
以上的三個請求都滿足API定義中設置的條件。但是,以下的幾個請求則與配置的條件不匹配:
GET / HTTP/1.1 Host: example.com POST /foo HTTP/1.1 Host: example.com GET /foo HTTP/1.1 Host: foo.com
以上的三個請求只滿足兩個配置條件。第一個請求的URI不匹配,第二個請求的HTTP方法不匹配,第三個請求的Host頭不匹配。
現在我們了解了hosts、uris、methods是如何一起工作的。下面我們來逐個了解他們是如何單獨工作的。
1. 請求的HOST頭:
基於HOST頭發送請求,是通過KONG發送請求的最直接的一種方式,這也是HOST頭的基本用法之一。KONG使通過HOST訪問API實體的流程更加簡便快捷。HOSTS 接受多種參數,當使用管理員API進行請求時,需要將每個參數間用逗號分隔開來配置:
$ curl -i -X POST http://localhost:8001/apis/ \ -d 'name=my-api' \ -d 'upstream_url=http://test-api.com' \ -d 'hosts=test-api.com,demo.com,service.com' HTTP/1.1 201 Created ...
此時,客戶端可使用 hosts=test-api.com, demo.com, service.com 中的任意一個HOST頭對 my-api 進行訪問,但不能缺省HOST的值。
1.1 使用通配符查找主機名
為了使代理更靈活,KONG允許使用通配符來配置HOSTS。通配符允許任何滿足條件的HOST頭對API實體進行訪問。在配置通配符時,只允許在HOST的最左或最右配置一個星號(*),例如:
*.example.com :允許類似於 a.example.com 或 a.b.example.com 等形式的HOST頭訪問。
example.* :允許類似於 example.a 或 example.b.cn 等形式的HOST頭訪問。
一個完整的例子:
{ "name": "test-api", "upstream_url": "http://test-api.com", "hosts": ["*.example.com", "service.com"] }
匹配的HOST頭請求類似於一下幾種,但不限於一下的例子中的形式:
GET / HTTP/1.1 Host: an.example.com GET / HTTP/1.1 Host: service.com
1.2. preserve_host屬性
當啟用代理時,KONG默認將API的 upstream_url 的值配置為上游服務主機的 host 。preserve_host提供一個boolean值,對KONG的這個行為進行限制。例如,當使用preserve_host的默認值進行配置時,API的配置信息大致為:
{ "name": "my-api", "upstream_url": "http://my-api.com", "hosts": ["service.com"], }
此時,客戶端發送的請求信息格式大致為:
GET / HTTP/1.1 Host: service.com
KONG會從API的upstream_url中提取HOST值,在做代理時,會向上游服務發送類似的請求:
GET / HTTP/1.1 Host: my-api.com
然而,當管理員在配置API時,明確指定了preserver_host=true時:
{ "name": "my-api", "upstream_url": "http://my-api.com", "hosts": ["service.com"], "preserve_host": true }
並假設客戶端發送了相同的請求:
GET / HTTP/1.1 Host: service.com
KONG將會保留客戶端發送來的HOST值,在做代理時,會向上游服務發送以下的請求:
GET / HTTP/1.1 Host: service.com
2 請求URI
KONG將請求轉發給上游服務的另一種方式,是通過配置uris屬性來指定請求的URI值。uris可以配置多個值,此時客戶端在進行請求時,必須選擇uris中的一個值作為請求的前綴。
例如,如果API的配置為:
{ "name": "my-api", "upstream_url": "http://my-api.com", "uris": ["/service", "/hello/world"] }
以下的幾種請求方式都可以對此API進行訪問:
GET /service HTTP/1.1 Host: my-api.com GET /service/resource?param=value HTTP/1.1 Host: my-api.com GET /hello/world/resource HTTP/1.1 Host: anything.com
以上幾種方式在進行請求時,KONG偵測到他們的前綴與API的uris屬性值是匹配的,所以這些請求是可用的。
當uris屬性配置好后,在進行請求時,KONG首先會取最長的uris值對URI進行匹配篩選,如果未匹配,然后才會取次長度的uris值進行匹配,依次類推,直到匹配成功為止或拒絕訪問。譬如上面的例子里,在發送請求時,KONG首先會取 `/hello/world` 與請求uri進行匹配,如果未成功,才會繼續取 `/service` 繼續匹配工作。
2.1 strip_uri屬性
在配置API的uris時,如果你希望隱藏上游服務的真實uri,並以另外的一種URI提供給客戶端時,通過簡單的配置API的strip_uri屬性就可以實現。例如:
{ "name": "my-api", "upstream_url": "http://my-api.com", "uris": ["/service"], "strip_uri": true }
啟用strip_uri屬性來指示Kong在代理此API時,在上游請求的URI中不應包含匹配的URI前綴。針對上面的API配置,當客戶端發送請求時
GET /service/path/to/resource HTTP/1.1 Host:
KONG代理到上游服務的真實uri為(此時的uri不包含uris中配置的內容)
GET /path/to/resource HTTP/1.1 Host: my-api.com
3. HTTP請求方法
從KONG 0.10版本開始,可以通過配置API的methods屬性來指定客戶端的請求。默認情況下,無論HTTP請求方式如何,Kong都會將請求將其代理到API的。但是,當配置methods屬性后,只有HTTP請求方式匹配時才會被KONG代理。methods屬性可以配置多個值:
{ "name": "api-1", "upstream_url": "http://my-api.com", "methods": ["GET", "HEAD"] }
以下的兩種HTTP請求方式都可以被代理
GET / HTTP/1.1 Host: HEAD /resource HTTP/1.1 Host:
但是不會匹配POST或DELETE等其他的請求方式。配置此屬性,可以更細粒度的管理你的API。
代理的優先級
一個API可以通過配置hosts、uris和methods屬性定義匹配規則。對於Kong來說,只有當所有的字段都匹配時,才會進行轉發。然而,KONG允許兩個或更多的API擁有相同的配置。此時,就涉及到請求的優先級問題了。
匹配優先級原則是:在評估請求時,Kong會首先與匹配規則最多的API進行匹配(when evaluating a request, Kong will first try to match the APIs with the most rules.)。例如有兩個API的配置如下
{ "name": "api-1", "upstream_url": "http://my-api.com", "hosts": ["example.com"] }, { "name": "api-2", "upstream_url": "http://my-api-2.com", "hosts": ["example.com"], "methods": ["POST"] }
api-2比api-1多了一個methods配置,所以KONG會最先將請求與api-2進行匹配。通過此配置,我們就能避免在請求api-2時,錯誤的轉發到api-1。
此時,這個請求會被轉發到api-1:
GET / HTTP/1.1 Host: example.com
而這個請求,會被轉發到api-2:
POST / HTTP/1.1 Host: example.com
以此類推,如果一個api同時配置了hosts、methods、uris,則這個API會最先與請求進行匹配,他的優先級會是三個中最高的。
代理行為
上述內容詳細講述了KONG將請求代理到上游服務的代理規則。下面我們將詳細說明,在KONG將請求轉發到上游服務的這段時間內實際發生的一些事。
1. 負載平衡
從KONG 1.10 版本開始,Kong繼承實現了負載平衡的功能,可以將轉發的請求分發到多個上游服務實例。在 Kong 0.10之前,一般情況下,Kong會將代理的請求轉發到指定的upstream_url上,如果想實現服務的負載平衡,需要額外的工具來完成。您可以通過查閱負載平衡相關資料,找到更多有關向API添加負載平衡的信息。
2. 插件擴展
使用者可以通過plugins來自定義擴展插件,並將插件應用到request/response的生命周期中。插件可以在您的生產環境中對代理行為進行多種操作。通過對插件進行配置,可以將插件應進行全局應用或針對個別API應用。如果對給定的某個或某些API應用了一個插件或多個插件,則在代理請求到上游服務前,會首先執行API的匹配,匹配成功后再執行定義的插件,當插件執行完后,如果滿足天劍,才會將請求最終代理到上游服務中去。這里包含了插件的訪問時段的概念,具體參考插件開發配置相關資料。
3. 代理或請求超時
當KONG將所有的代理邏輯,包括API驗證和插件等,都處理完后,才會將請求代理到上游服務。這些是通過Nginx的代理模塊來實現的。從KONG 0.10開始,可以通過配置以下三個API屬性來定義連接超時時間:
upstream_connect_timeout: 定義同上游服務建立連接的超時時間,單位是毫秒,默認值為60000.
upstream_send_timeout: 定義兩個連續向上游服務發送的寫入操作的超時時間,單位是毫秒,默認值為60000.
upstream_read_timeout: 定義兩個連續向上游服務發送讀取操作的超時時間,單位是毫秒,默認值為60000.
在使用HTTP/1.1時,KONG會默認添加以下請求頭信息:
HOST:<上游服務的host>
Connection:keep-alive : 允許重復使用上游連接
X-REAL-IP:<$proxy_add_x_forwarded_for>: 參見 nginx_http_proxy_module.
X-Forwarded-Proto:<protocol>: protocol是客戶端使用的協議名
其他的請求頭都按默認配置添加。
當使用WebSocket時,會有例外發生。如果發生,KONG允許通過以下請求頭對協議進行升級:
Connection:Upgrade
Upgrade:WebSocket
4. Response
Kong接收上游服務的響應,並以流式方式將其發回下游客戶端。此時,Kong將執行添加到該特定API的后續插件,並且這些插件繼承了header_filter子句。當所有繼承了header_filter的插件執行完后,以下的幾種頭信息將返回給客戶端:
Via:kong/x.x.x KONG的版本信息,x.x.x是他的版本號。
Kong-Proxy-Latency:<latency>,其中 latency 表示Kong接收來自客戶端的請求並將請求發送到上游服務所消耗的時間值(毫秒)。
Kong-Upstream-Latency:<latency>,其中 latency 表示Kong等待上游服務響應的第一個字節所消耗的時間數(毫秒)。
當headers發送到客戶端后,Kong將執行此API上已注冊的、繼承了body_filter的插件。body_filter將上游返回的數據成功處理后,然后再發送給客戶端。
配置一個fallback API
默認情況下,當向KONG發送請求時,如果請求失敗,KONG會返回 HTTP 404,“API not found” 等信息。為了避免這種情況的發生,我們可以通過配置 fallback API 實現自定義 “異常\錯誤” 信息。
下面是一個例子:
{ "name": "root-fallback", "upstream_url": "http://www.error.page", "uris": ["/"] }
觀察此配置中的“uris”,會發現,此處配置“/”可以適配所有的請求,根據uris的匹配規則,每個請求都會首先匹配最長的uri,依次遞減。所以當所有的uri都不匹配時,請求會自動轉發到這里配置的上有服務中去,因此而實現了自定義 “異常\錯誤” 信息。
配置API的SSL
Kong提供了一種在每個連接的基礎上動態提供SSL證書的方法。從0.10版本開始,SSL插件已被刪除,SSL證書由內核直接處理,並可通過Admin API進行配置。你的客戶端HTTP庫必須支持SNI(Server Name Indication)擴展以確保SSL可用。
SSL證書由Kong Admin API的兩個資源處理:
· /certificates 存儲您的密鑰和證書。
· /snis 將注冊的證書與服務器名稱指示相關聯。
以下是為給定的API配置SSL證書的方法:
首先,通過Admin API上傳SSL證書和密鑰:
$ curl -i -X POST http://localhost:8001/certificates \ -F "cert=@/path/to/cert.pem" \ -F "key=@/path/to/cert.key" \ -F "snis=ssl-example.com,other-ssl-example.com" HTTP/1.1 201 Created ...
snis是加密參數,直接插入SNI並將上傳的證書與之相關聯。
您現在必須在Kong內注冊以下API。為方便起見,我們將使用Host頭將請求轉發到此API:
$ curl -i -X POST http://localhost:8001/apis \ -d "name=ssl-api" \ -d "upstream_url=http://my-api.com" \ -d "hosts=ssl-example.com,other-ssl-example.com" HTTP/1.1 201 Created ...
好了,現在你可以向KONG發送HTTPs請求了:
$ curl -i https://localhost:8443/ \ -H "Host: ssl-example.com" HTTP/1.1 200 OK ...
1. https_only參數:
如果你希望僅通過HTTPS提供API,可以通過啟用其https_only屬性來實現:
$ curl -i -X POST http://localhost:8001/apis \ -d "name=ssl-only-api" \ -d "upstream_url=http://example.com" \ -d "hosts=my-api.com" \ -d "https_only=true" HTTP/1.1 201 Created ...
這樣配置后,KONG將拒絕代理非HTTPS的請求。如果此時使用HTTP請求來訪問這個API,KONG會提示用戶需要將客戶端升級到HTTPS:
$ curl -i http://localhost:8000 \ -H "Host: my-api.com" HTTP/1.1 426 Content-Type: application/json; charset=utf-8 Transfer-Encoding: chunked Connection: Upgrade Upgrade: TLS/1.2, HTTP/1.1 Server: kong/x.x.x {"message":"Please use HTTPS protocol"}
2. http_if_terminated參數:
當API被鎖定在HTTPS請求時,如果你想在請求頭信息中添加X-Forwarded-Proto,只要為API配置http_if_terminated屬性即可:
$ curl -i -X PATCH http://localhost:8001/apis/ssl-only-api \ -d "http_if_terminated=true" HTTP/1.1 200 OK ...
並且我們使用X-Forwarded-Proto(假設它來自可信任的客戶端)發出請求:
$ curl -i http://localhost:8000 \ -H "Host: my-api.com" \ -H "X-Forwarded-Proto: https" HTTP/1.1 200 OK ...
Kong現在代理了這個請求,因為它假定你的體系結構的先前組件已經實現了SSL。
代理WebSocket
由於KONG的底層實現了Nginx,所以他也支持WebSocket。如果你希望在客戶端和KONG之間建議一個WebSocket連接,那么你必須首先建立一個WebSocket握手。這個通過HTTP Upgrade機制完成。這個可以通過客戶端對KONG進行操作,例如:
GET / HTTP/1.1 Connection: Upgrade Host: my-websocket-api.com Upgrade: WebSocket
此時,KONG會將Connection和Upgrade兩個請求頭轉發到上游服務中去。
小結
通過本指南,希望你了解了Kong的底層代理相關的知識,從API的uris匹配,到如何使用WebSocket協議、為API配置SSL。如果想了解更多的相關知識,請參考Nginx的負載平衡相關內容。