1. 程式人生 > >apigateway-kong(三)Proxy規則

apigateway-kong(三)Proxy規則

IV apple express 正則表達式: 其中 數據轉換 配置路由 http請求 工作

本篇詳細記錄了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

請求主機頭-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

使用通配符主機名

  為了提供靈活性,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

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

請求路徑-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。

在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)評估提供的正則表達式,這意味著它們將被限制為匹配路徑中的第一個匹配點(根/字符)

評估(匹配)順序

  如前所述,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將遍歷您的路由,直到找到與最多規則匹配的路由

捕獲組

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

/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" }

轉義特殊字符

  接下來,值得註意的是,在正則表達式中找到的字符通常是根據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的空間。

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: ...


請求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請求與註冊路由匹配的時間與請求的實際轉發之間發生的內部事件。

負載均衡

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

更多負載均衡見下篇

插件執行

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

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

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

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

代理與上遊超時

  一旦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>
X-Forwarded-Host: <host>
X-Forwarded-Port: <port>

錯誤和重試

響應

未完待續...

l

apigateway-kong(三)Proxy規則