八、代理参考

Kong侦听四个端口的请求,默认情况是:
  8000:此端口是Kong用来监听来自客户端的HTTP请求的,并将此请求转发到您的上游服务。这也是本教程中最主要用到的端口。
  8443:此端口是Kong监听HTTP的请求的端口。该端口具有与8000端口类似的行为,但是它只监听HTTPS的请求,并不会产生转发行为。可以通过配置文件来禁用此端口。
  8001:用于管理员对KONG进行配置的端口。
  8444:用于管理员监听HTTPS请求的端口。
  在本文中,我们将介绍Kong的路由功能,并详细说明8000端口上的客户端请求如何根据headerURIHTTP被代理到配置中的上游服务。

基础术语

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添加许多属性,但是有三个是必须要配置的,他们是hostsurismethods。如果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请求的属性,但请注意,这里有且只有一个HOSTURISMETHODS属性。当完成此配置后,以后所有的符合此hosturismethods的请求,都将由KONG来代理过滤转发。Kong是一个透明的代理,它会将请求转发给您的上游服务,除了添加诸如Connection之类的各种标题。

转发功能

现在我们来讨论一下,一个HTTP请求是如何与API配置属性(如hosturismethods)相匹配的。需要注意的是,这三个字段(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.1Host: foo.com

以上的三个请求只满足两个配置条件。第一个请求的URI不匹配,第二个请求的HTTP方法不匹配,第三个请求的Host头不匹配。
    现在我们了解了hostsurismethods是如何一起工作的。下面我们来逐个了解他们是如何单独工作的。

请求的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的值。

使用通配符查找主机名

为了使代理更灵活,KONG允许使用通配符来配置HOSTS。通配符允许任何满足条件的HOST头对API实体进行访问。在配置通配符时,只允许在HOST的最左或最右配置一个星号*,例如:
*.example.com:允许类似于 a.example.coma.b.example.com 等形式的HOST头访问。
example.*:允许类似于 example.aexample.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
preserve_host属性

当启用代理时,KONG默认将API的upstream_url 的值配置为上游服务主机的 hostpreserve_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.1Host: service.com

KONG将会保留客户端发送来的HOST值,在做代理时,会向上游服务发送以下的请求:

GET / HTTP/1.1Host: service.com

请求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 继续匹配工作。

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

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可以通过配置hostsurismethods属性定义匹配规则。对于Kong来说,只有当所有的字段都匹配时,才会进行转发。然而,KONG允许两个或更多的API拥有相同的配置。此时,就涉及到请求的优先级问题了。
    匹配优先级原则是:在评估请求时,Kong会首先与匹配规则最多的API进行匹配[en](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-2api-1多了一个methods配置,所以KONG会最先将请求与api-2进行匹配。通过此配置,我们就能避免在请求api-2时,错误的转发到api-1
此时,这个请求会被转发到api-1

GET / HTTP/1.1Host: example.com

而这个请求,会被转发到api-2

POST / HTTP/1.1Host: example.com

以此类推,如果一个api同时配置了hostsmethodsuris,则这个API会最先与请求进行匹配,他的优先级会是三个中最高的。

代理行为

上述内容详细讲述了KONG将请求代理到上游服务的代理规则。下面我们将详细说明,在KONG将请求转发到上游服务的这段时间内实际发生的一些事。

负载平衡

从KONG 1.10 版本开始,Kong继承实现了负载平衡的功能,可以将转发的请求分发到多个上游服务实例。在 Kong 0.10之前,一般情况下,Kong会将代理的请求转发到指定的upstream_url上,如果想实现服务的负载平衡,需要额外的工具来完成。您可以通过查阅负载平衡相关资料,找到更多有关向API添加负载平衡的信息。

插件扩展

使用者可以通过plugins来自定义扩展插件,并将插件应用到request/response的生命周期中。插件可以在您的生产环境中对代理行为进行多种操作。通过对插件进行配置,可以将插件应进行全局应用或针对个别API应用。如果对给定的某个或某些API应用了一个插件或多个插件,则在代理请求到上游服务前,会首先执行API的匹配,匹配成功后再执行定义的插件,当插件执行完后,如果满足天剑,才会将请求最终代理到上游服务中去。这里包含了插件的访问时段的概念,具体参考插件开发配置相关资料。

代理或请求超时

当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允许通过以下请求头对协议进行升级:
  ConnectionUpgrade
  UpgradeWebSocket

Response

Kong接收上游服务的响应,并以流式方式将其发回下游客户端。此时,Kong将执行添加到该特定API的后续插件,并且这些插件继承了header_filter子句。当所有继承了header_filter的插件执行完后,以下的几种头信息将返回给客户端:
  Viakong/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 404API 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库必须支持SNIServer 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...
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"}
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会将ConnectionUpgrade两个请求头转发到上游服务中去。

小结

通过本指南,希望你了解了Kong的底层代理相关的知识,从API的uris匹配,到如何使用WebSocket协议、为API配置SSL。如果想了解更多的相关知识,请参考Nginx的负载平衡相关内容。

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 84,418评论 14 122
  • 1. 配置加载 如果您通过其中一个官方软件包安装了Kong,Kong会附带默认配置文件,该文件可以在/etc/ko...
    蝉鸣的雨阅读 448评论 0 1
  • 上一篇《WEB请求处理一:浏览器请求发起处理》,我们讲述了浏览器端请求发起过程,通过DNS域名解析服务器IP,并建...
    猿码架构阅读 69,807评论 22 327
  • 第一章 Nginx简介 Nginx是什么 没有听过Nginx?那么一定听过它的“同行”Apache吧!Ngi...
    JokerW阅读 27,628评论 24 982
  • 我在煮晚饭,湘湘哭着说:"妈妈,我想上厕所。"我说那就上吧。可是我没尿,他可怜巴巴的说。我说沒尿就别上。又不是三岁...
    凡人天劫阅读 20评论 0 0