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

请求主机头-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>  

 

 

 

 

 

错误和重试

响应

 未完待续...

猜你喜欢

转载自www.cnblogs.com/zhoujie/p/kong3.html