apigateway-kong(三)Proxy規則


本篇詳細記錄了Kong的代理功能及其路由功能和內部工作。

  Kong公開了幾個可以通過兩個配置屬性進行調整的接口:
proxy_listen,默認8000,它定義Kong將接受來自客戶端的公共流量並將其代理到您的上游服務的地址/端口列表。
admin_listen,默認8001,它還定義了一個地址和端口列表,但這些列表應該僅限於管理員訪問,因為它們揭示了Kong的配置功能:Admin API

注意:從kong v0.13.0開始,API實體已被棄用。本文檔將介紹新的路由和服務實體的代理。

一些術語

client:指下游客戶向Kong的代理端口發出請求。
upstream service:指自己的API /服務,位於Kong后面,客戶請求被轉發到該服務。
service:服務實體,顧名思義,是對自己的每個上游服務的抽象。服務的例子可以是數據轉換微服務,賬單API等。
route:這是指Kong Routes實體。路由是進入Kong的入口點,並為要匹配的請求定義規則,並路由到給定的Service。
plugin:這是指Kong的“插件”,它是在代理生命周期中運行的業務邏輯。可以通過ADMIN API配置插件 - 全局(所有傳入流量)或特定的路由和服務。

概覽

  從上層角度看,Kong在其配置的代理端口(默認情況下為8000和8443)上偵聽HTTP流量。 Kong會根據你配置的路由評估任何傳入的HTTP請求,並嘗試找到匹配的路由。如果某個請求符合特定路由的規則,Kong將處理代理請求。由於每條路由都與一個服務鏈接,因此Kong將運行您在路由及其相關服務上配置的插件,然后向上游代理請求。
您可以通過Kong的Admin API管理routes,路由的hosts,paths和methods屬性定義匹配傳入HTTP請求的規則。
如果Kong收到無法匹配任何已配置路由的請求(或者沒有配置路由),則它將以下列情況作出響應:

HTTP/1.1 404 Not Found
Content-Type: application/json
Server: kong/<x.x.x>

{
    "message": "no route and no API found with those values"
}

注意:這個消息提到了“API”,因為為了向后兼容的原因,Kong 0.13仍然支持API實體(並且如果沒有首先匹配任何路由,則嘗試匹配針對任何配置的API的請求)。

Kong是一個透明的代理,默認情況下它會將請求轉發給上游服務,但HTTP規范要求的各種頭文件(例如Connection,Date和其他頭文件)除外 

關於如何通過配置Route和Service來代理到一個上游服務,參考上一篇admin-api配置示例說明

路由匹配規則

  現在看下Kong如何將請求與路由的配置hosts, paths and methods屬性(或字段)進行匹配,注意:這三個字段都是可選的,但至少必須指定其中的一個

匹配一個route的請求:

  • 該請求必須包含所有配置的字段
  • 請求中字段的值必須至少匹配一個配置的值(雖然字段配置接受一個或多個值,但請求只需要其中一個值被視為匹配)

例子:如下配置的route如何匹配:

{
    "hosts": ["example.com", "foo-service.com"],
    "paths": ["/foo", "/bar"],
    "methods": ["GET"]
}

下面是與此路由匹配的一些請求:

GET /foo HTTP/1.1
Host: example.com
GET /bar HTTP/1.1
Host: foo-service.com
GET /foo/hello/world HTTP/1.1
Host: example.com


這三個請求都滿足路徑定義中設置的所有條件但是,以下請求不符合配置的條件:

# 未指定paths
GET / HTTP/1.1
Host: example.com
# method不匹配
POST /foo HTTP/1.1
Host: example.com
# request host header不匹配
GET /foo HTTP/1.1
Host: foo.com

1.請求主機頭-Request Host header

  根據Host頭來路由請求是通過Kong代理流量的最直接的方式,這是HTTP Host頭的預期用法。 Kong通過
主機接受多個值,當通過管理API指定時,它們必須以逗號分隔。

主機接受多個值,這在JSON payload中很簡單:

$ curl -i -X POST http://localhost:8001/routes/ \
    -H 'Content-Type: application/json' \
    -d '{"hosts":["example.com", "foo-service.com"]}'
HTTP/1.1 201 Created
...

但由於Admin API也支持form-urlencoded content types,所以可以通過[]表示法指定一個數組:

$ curl -i -X POST http://localhost:8001/routes/ \
    -d 'hosts[]=example.com' \
    -d 'hosts[]=foo-service.com'
HTTP/1.1 201 Created
...

為了滿足此route的hosts條件,來自客戶端的任何傳入請求現在必須將其主機頭設置為以下之一:

Host: example.com
或
Host: foo-service.com

1.1 使用通配符主機名

  為了提供靈活性,Kong允許在hosts字段中指定帶有通配符的主機名。通配符主機名允許任何匹配的主機頭部滿足條件,從而匹配給定的路由。通配符主機名在domain的最左側或最右側標簽中只能包含一個*
例如:
* .example.com將允許主機值(如a.example.com和x.y.example.com)匹配。
example.*將允許諸如example.com和example.org的主機值匹配。

 

例如路由中指定如下hosts:

{
    "hosts": ["*.example.com", "service.com"]
}

將允許以下請求匹配此route:

GET / HTTP/1.1
Host: an.example.com
GET / HTTP/1.1
Host: service.com

1.2 preserve_host屬性

  代理時,Kong的默認行為是將上游請求的主機頭設置為Service主機中指定的主機名。 preserve_host字段接受一個布爾標志,默認false,指示Kong不這樣做。

例如,如果preserve_host屬性未更改且route配置如下:

{
    "hosts": ["service.com"],
    "service": {
        "id": "..."
    }
}

客戶端可能向kong發出的一個請求:

GET / HTTP/1.1
Host: service.com

Kong會從Service's host屬性中提取主機頭值,並發送以下上游請求:

GET / HTTP/1.1
Host: <my-service-host.com>

但是,通過使用preserve_host = true明確配置路由:

{
    "hosts": ["service.com"],
    "preserve_host": true,
    "service": {
        "id": "..."
    }
}

假設來自客戶端的相同請求:

GET / HTTP/1.1
Host: service.com

Kong將保留客戶端請求的主機,並將發送以下上游請求:

GET / HTTP/1.1
Host: service.com

2.請求路徑-Request path

  路由匹配的另一種方式是通過請求路徑,為了滿足此路由條件,客戶端請求的路徑必須以路徑屬性值之一作為前綴。
例如,使用如下配置的Route:

{
    "paths": ["/service", "/hello/world"]
}

下面這些請求將會匹配:

GET /service HTTP/1.1
Host: example.com
GET /service/resource?param=value HTTP/1.1
Host: example.com
GET /hello/world/resource HTTP/1.1
Host: anything.com

對於這些請求中的每一個,Kong都會檢測到它們的URL路徑前綴有一個路由路徑值。默認情況下,Kong會在不改變URL路徑的情況下向上游代理請求。
使用路徑前綴進行代理時,首先會評估最長的路徑。這使您可以定義兩條路徑:/service 和 /service/resource。

2.1 在path中使用正則表達式

  Kong通過PCRE(Perl Compatible Regular Expression)支持路徑字段的正則表達式模式匹配。

可以同時將路徑作為前綴和正則表達式分配給路由,如下面的路由配置:

{
    "paths": ["/users/\d+/profile", "/following"]
}

下面這些請求將會匹配這條路由:

GET /following HTTP/1.1
Host: ...
GET /users/123/profile HTTP/1.1
Host: ...

使用PCRE標志(PCRE_ANCHORED)評估提供的正則表達式,這意味着它們將被限制為匹配路徑中的第一個匹配點(根/字符)

2.1.1 評估(匹配)順序

  如前所述,Kong根據長度評估前綴路徑:首先評估最長的前綴路徑。但是,Kong將根據路由的regex_priority屬性評估正則表達式路徑。

這意味着考慮以下路線:

[
    {
        "paths": ["/status/\d+"],
        "regex_priority": 0
    },
    {
        "paths": ["/version/\d+/status/\d+"],
        "regex_priority": 6
    },
    {
        "paths": ["/version"],
        "regex_priority": 3
    },
]

在這種情況下,Kong將按照以下順序針對以下定義的URI評估傳入請求:

  1. /version
  2. /version/\d+/status/\d+
  3. /status/\d+

前綴路徑總是先評估。

像往常一樣,請求必須仍然與路由的主機和方法屬性相匹配,並且Kong將遍歷您的路由,直到找到與最多規則匹配的路由

2.1.2 捕獲組

  支持捕獲組,並且匹配的組將從路徑中提取並可供插件使用。如果我們考慮以下正則表達式:

/version/(?<version>\d+)/users/(?<user>\S+)

請求路徑如下:

/version/1/users/john

Kong將認為該請求路徑匹配,並且如果總體路由匹配(考慮hosts和methods字段),解壓縮的捕獲組將可以從ngx.ctx變量的插件中獲得:

local router_matches = ngx.ctx.router_matches

-- router_matches.uri_captures is:
-- { "1", "john", version = "1", user = "john" }

2.1.3 轉義特殊字符

  接下來,值得注意的是,在正則表達式中找到的字符通常是根據RFC 3986的保留字符,因此應該使用percent-encoded。通過管理API配置正則表達式路徑時,請確保在必要時對您的payload進行URL編碼。例如,使用curl並使用application/x-www-form-urlencoded MIME 類型:

$ curl -i -X POST http://localhost:8001/routes \
    --data-urlencode 'uris[]=/status/\d+'
HTTP/1.1 201 Created
...

注意,curl不會自動對payload進行URL編碼,並且請注意--data-urlencode的用法,該操作可防止+字符被URL解碼,並將其解釋為Kong's Admin API的空間。

2.2 strip_path屬性

  可能需要指定一個path前綴來匹配Route,但不會將其包含在上游請求中。為此,請通過配置Route來使用strip_path布爾屬性,如下所示:

{
    "paths": ["/service"],
    "strip_path": true,
    "service": {
        "id": "..."
    }
}

啟用此標志將指示Kong在匹配此路由並繼續處理代理服務時,不應在上游請求的URL中包含匹配的URL路徑部分

例如,以下客戶端對上述路由的請求:

GET /service/path/to/resource HTTP/1.1
Host: ...

將導致Kong發送以下上游請求:

GET /path/to/resource HTTP/1.1
Host: ...


同樣,如果在啟用strip_path的Route上定義了正則表達式路徑,則請求URL匹配序列的全部內容將被刪除。

例:

{
    "paths": ["/version/\d+/service"],
    "strip_path": true,
    "service": {
        "id": "..."
    }
}

以下HTTP請求與提供的正則表達式路徑匹配:

GET /version/1/service/path/to/resource HTTP/1.1
Host: ...

kong將上游代理為:

GET /path/to/resource HTTP/1.1
Host: ...

3.請求HTTP方法-Request HTTP method

  方法字段允許根據HTTP方法匹配請求。它接受多個值。它的默認值是空(HTTP方法不用於路由)。

下面端路由允許通過GET/HEAD方法進行路由:

{
    "methods": ["GET", "HEAD"],
    "service": {
        "id": "..."
    }
}

下面的請求匹配上述路由:

GET / HTTP/1.1
Host: ...
HEAD /resource HTTP/1.1
Host: ...

上述路由不匹配POST和DELETE請求。

在路由上配置插件時,這可以實現更細的粒度。例如,可以設想兩個路由指向相同的服務:一個具有無限的未經身份驗證的GET請求,另一個只允許經過身份驗證和速率限制的POST請求(通過將身份驗證和速率限制插件應用於此類請求)。

匹配優先級

  Route可以根據hosts,paths和methods字段定義匹配規則。為了使Kong將傳入請求匹配到Route,必須滿足所有現有字段。然而,Kong通過允許兩個或多個路由配置包含相同值的字段來允許相當大的靈活性 - 當發生這種情況時,Kong應用優先級規則。

規則是:在評估請求時,Kong將首先嘗試將路線與大部分規則進行匹配

例如,如下兩個路由配置:

{
    "hosts": ["example.com"],
    "service": {
        "id": "..."
    }
},
{
    "hosts": ["example.com"],
    "methods": ["POST"],
    "service": {
        "id": "..."
    }
}

第二個Route有一個hosts字段和一個methods字段,因此它將首先被Kong評估。通過這樣做,避免了第一個路由覆蓋掉第二個。

因此,這個請求將匹配第一條路線:

GET / HTTP/1.1
Host: example.com

下面這個匹配第二個路由:

POST / HTTP/1.1
Host: example.com

按照這個邏輯,如果第三個Route要配置一個hosts字段,一個methods字段和一個uris字段,它將首先由Kong評估。

代理行為

  上面的代理規則詳細說明了Kong如何將傳入請求轉發給您的上游服務。在下面,詳細介紹在一個HTTP請求與注冊路由匹配的時間與請求的實際轉發之間發生的內部事件。

1. 負載均衡

Kong通過上游服務的實例池分發代理請求來實現負載平衡功能。

更多負載均衡見下篇

2. 插件執行

  Kong可以通過“插件”進行擴展,這些插件將自己hook在代理請求的request/response生命周期中。插件可以在您的環境中執行各種操作或對代理請求進行轉換。

插件可以配置為全局運行(適用於所有代理流量)或特定的路由和服務。在這兩種情況下,您都必須通過ADMIN API創建插件配置。

一旦route匹配(及其關聯的services),Kong將運行與這些實體中的任何一個相關聯的插件。配置在route上的插件會先於service上的插件運行

這些配置的插件將運行他們的訪問階段,見后篇 插件開發指南

3. 代理與上游超時

  一旦Kong執行了所有必要的邏輯(包括插件),就可以將請求轉發給您的上游服務。這是通過Nginx的ngx_http_proxy_module完成的。可以通過服務的以下屬性為Kong和給定的上游之間的連接配置所需的超時時間:

屬性 描述
upstream_connect_timeout 以毫秒為單位定義建立到上游服務的連接的超時時間。默認為60000
upstream_send_timeout 以毫秒為單位定義兩次連續寫入操作之間的超時,用於向上游服務發送請求。默認為60000
upstream_read_timeout 以毫秒為單位定義兩個連續讀取操作之間的超時,用於接收來自上游服務的請求。默認為60000

 

 Kong將通過HTTP / 1.1發送請求,並設置以下標題:

屬性 描述
Host: <your_upstream_host>  如前所述
Connection: keep-alive 允許重新使用上游連接
X-Real-IP: <remote_addr> 其中$remote_addr是由ngx_http_core_module提供的具有相同名稱的變量。請注意,$remote_addr很可能被ngx_http_realip_module覆蓋
X-Forwarded-For: <address> 其中<address>是由ngx_http_realip_module提供的$realip_remote_addr的內容附加到具有相同名稱的請求標頭
X-Forwarded-Proto: <protocol>  <protocol>是客戶端使用的協議。在$realip_remote_addr是可信地址之一的情況下,如果提供相同名稱的請求頭,則會被轉發。否則,將使用由ngx_http_core_module提供的$scheme變量的值。
X-Forwarded-Host: <host>  <host>是客戶端發送的主機名。在$realip_remote_addr是可信地址之一的情況下,如果提供相同名稱的請求頭,則會被轉發。否則,將使用由ngx_http_core_module提供的$ host變量的值。
X-Forwarded-Port: <port>  <port>是接受請求的服務器的端口。在$realip_remote_addr是可信地址之一的情況下,如果提供相同名稱的請求頭,則會被轉發。否則,將使用ngx_http_core_module提供的$ server_port變量的值。

 所有其他的請求頭文件都是由Kong原封不動地轉發。

使用WebSocket協議時會產生一個例外,Kong將設置以下標題以允許升級客戶端和上游服務之間的協議:

Connection: Upgrade
Upgrade: websocket

4. 錯誤和重試

  在代理期間發生錯誤時,Kong將使用底層的Nginx重試機制將請求傳遞到下一個上游。

這里有兩個可配置的元素:
1、重試次數:這可以使用retries屬性為每個服務進行配置,有關詳情,請參閱Admin API。
2、究竟是什么構成了一個錯誤:這里Kong使用Nginx默認值,這意味着在建立與服務器的連接,傳遞請求或讀取響應頭文件時發生錯誤或超時。

第二個基於Nginx的[proxy_next_upstream] [proxy_next_upstream]指令。該選項不能直接通過Kong配置,但可以使用自定義Nginx配置添加。詳見后續配置參考篇

5. 響應

  Kong從上游服務接收響應,並以流媒體方式將其發送回下游客戶端。此時,Kong將執行添加到Route和/或Service中的后續插件,這些插件在header_filter階段實現掛鈎(hook)。

所有已注冊插件的header_filter階段執行完畢后,Kong將添加以下標題,並將全套標題發送給客戶端:

  • Via:kong/x.x.x,其中x.x.x是正在使用的Kong版本
  • X-Kong-Proxy-Latency:<latency>,其中latency表示Kong從客戶端收到請求並將請求發送到上游服務之間的時間(以毫秒為單位)。
  • X-Kong-Upstream-Latency:<latency>,其中latency是Kong等待上游服務響應的第一個字節的時間(以毫秒為單位)。

一旦頭文件被發送到客戶端,Kong將開始為執行body_filter hook的Route或Service執行已注冊的插件。由於Nginx的流媒體特性,該hook可能會被多次調用。由body_filter hook成功處理的每個上游響應塊都會發送回客戶端。

配置回退路由(fallback route)

  作為Kong代理功能所提供的靈活性的一個實際用例和實例,“回退路由”為了避免Kong用HTTP 404響應“no route found”,我們可以捕獲請求並將它們代理到一個特殊的上游服務,或者向它應用一個插件(例如,這樣一個插件可以用不同的狀態碼或響應終止請求,而不用代理請求)。

這是一個這樣的回退路線的例子:

{
    "paths": ["/"],
    "service": {
        "id": "..."
    }
}

對Kong的任何HTTP請求實際上都會匹配這個Route,因為所有的URI都以根字符/作為前綴。正如我們從request path部分了解到的,最長的URL路徑首先由Kong評估,因此/ path最終將由Kong最后評估,並有效地提供“fallback”Route,僅作為最后的手段匹配。然后,您可以將流量發送到特殊服務,或者在此路線上應用您希望的任何插件。

配置路由的SSL

  Kong提供了一種在每個連接的基礎上動態提供SSL證書的方法。 SSL證書由core直接處理,並可通過ADMIN API進行配置。通過TLS連接到Kong的客戶端必須支持SNI(Server Name Indication)擴展才能使用此功能。

SSL證書由Kong Admin API中的兩個資源處理:

  • /certificates,存儲您的密鑰和證書。
  • /snis,它將注冊證書與服務器名稱指示關聯起來。

以下是如何在給定route上配置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注冊以下route,為了方便起見,我們僅使用主機頭將請求匹配到此路由:

$ curl -i -X POST http://localhost:8001/routes \
    -d 'hosts=ssl-example.com,other-ssl-example.com' \
    -d 'service.id=d54da06c-d69f-4910-8896-915c63c270cd'
HTTP/1.1 201 Created
...

現在可以預期Kong將通過HTTPS提供路由:

$ curl -i https://localhost:8443/ \
  -H "Host: ssl-example.com"
HTTP/1.1 200 OK
...

當建立連接並協商SSL握手時,如果客戶端將ssl-example.com作為SNI擴展的一部分發送,則Kong將為先前配置的cert.pem證書提供服務。

限制客戶端協議(HTTP/HTTPS)

  routes具有protocols屬性來限制他們應該監聽的客戶端協議。該屬性接受一組值,可以是http或https。
無論客戶端連接的協議(純文本還是通過TLS),使用http和https的路由都將接受流量:

{
    "hosts": ["..."],
    "paths": ["..."],
    "methods": ["..."],
    "protocols": ["http", "https"],
    "service": {
        "id": "..."
    }
}

不指定https具有相同的效果,路由將接受純文本和TLS(transport layer security)流量:

{
    "hosts": ["..."],
    "paths": ["..."],
    "methods": ["..."],
    "protocols": ["http"],
    "service": {
        "id": "..."
    }
}

但是,只有https的路由才會接受通過TLS的流量。如果先前從可信IP發生SSL終止,它也會接受未加密的流量。如果請求來自trusted_ips中配置的IP之一,並且X-Forwarded-Proto:https頭已設置,則SSL終止被認為是有效的:

{
    "hosts": ["..."],
    "paths": ["..."],
    "methods": ["..."],
    "protocols": ["https"],
    "service": {
        "id": "..."
    }
}

如果上述路由與請求相匹配,但該請求是純文本而沒有有效的SSL終止,則Kong會回應:

HTTP/1.1 426 Upgrade Required
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
Connection: Upgrade
Upgrade: TLS/1.2, HTTP/1.1
Server: kong/x.y.z

{"message":"Please use HTTPS protocol"}

代理WebSocket流量

  Kong支持WebSocket流量,這要歸功於底層的Nginx實現。當希望通過Kong在客戶端和上游服務之間建立WebSocket連接時,必須建立WebSocket握手。這是通過HTTP升級機制完成的。

客戶端請求對kong的要求:

GET / HTTP/1.1
Connection: Upgrade
Host: my-websocket-api.com
Upgrade: WebSocket

這使Kong將Connection和Upgrade報頭轉發給上游服務,而不是由標准HTTP代理的hop-by-hop性質而丟棄它們。WebSocket和TLS

WebSocket和TLS

  Kong將在其各自的http和https端口上接受ws和wss連接。要從客戶端強制執行TLS連接,將Route的protocols屬性設置為僅https。

當設置服務指向上游WebSocket服務時,應該仔細選擇要在Kong和上游之間使用的協議。如果要使用TLS(wss),則必須使用Service協議屬性中的https協議和適當的端口(通常為443)來定義上游WebSocket服務。要在沒有TLS(ws)的情況下進行連接,則應該在協議中使用http協議和端口(通常為80)。

如果希望Kong終止SSL/TLS,則只能從客戶端接受wss,但可以通過純文本或ws代理上游服務。

 

參考:

http://www.cnblogs.com/SummerinShire/p/6628021.html

 

現在基本了解了Kong的基本代理機制,從請求如何匹配路由,從路由到其相關服務,以及如何允許使用WebSocket協議或設置動態SSL證書,可以參考github.com/Mashape/getkong.org上。

下一篇記錄kong負載均衡

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM