高性能微服务网关APISIX – 常用插件(1)

APISIX 插件机制

Plugin 表示将在 HTTP 请求/响应生命周期期间执行的插件配置。

Plugin 配置可直接绑定在 Route 上,也可以被绑定在 ServiceConsumer上。而对于同一 个插件的配置,只能有一份是有效的,配置选择优先级总是 Consumer > Route > Service

conf/config.yaml 中,可以声明本地 APISIX 节点都支持哪些插件。这是个白名单机制,不在该白名单的插件配置,都将会被自动忽略。这个特性可用于临时关闭或打开特定插件,应对突发情况非常有效。 如果你想在现有插件的基础上新增插件,注意需要拷贝 conf/config-default.yaml 的插件节点内容到 conf/config.yaml 的插件节点中。

插件的配置可以被直接绑定在指定 Route 中,也可以被绑定在 Service 中,不过 Route 中的插件配置 优先级更高。

一个插件在一次请求中只会执行一次,即使被同时绑定到多个不同对象中(比如 Route 或 Service)。 插件运行先后顺序是根据插件自身的优先级来决定的,优先级越高的插件将会被先执行,例如:

local _M = {version = 0.1,priority = 0, -- 这个插件的优先级为 0name = plugin_name,schema = schema,metadata_schema = metadata_schema,
}

插件配置作为 Route 或 Service 的一部分提交的,放到 plugins 下。它内部是使用插件 名字作为哈希的 key 来保存不同插件的配置项。

{..."plugins": {"limit-count": {"count": 2,"time_window": 60,"rejected_code": 503,"key": "remote_addr"},"prometheus": {}}
}

并不是所有插件都有具体配置项,比如 prometheus 下是没有任何具体配置项,这时候用一个空的对象 标识即可。

如果一个请求因为某个插件而被拒绝,会有类似这样的 warn 日志:ip-restriction exits with http status code 403

热加载

APISIX 的插件是热加载的,不管你是新增、删除还是修改插件,都不需要重启服务。

只需要通过 admin API 发送一个 HTTP 请求即可:

curl http://127.0.0.1:9080/apisix/admin/plugins/reload -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT

注意:如果你已经在路由规则里配置了某个插件(比如在 routeplugins 字段里面添加了它),然后 禁用了该插件,在执行路由规则的时候会跳过这个插件。

redirect

URI 重定向插件。

参数

Name Type Requirement Default Valid Description
http_to_https boolean 可选 false 当设置为 true 并且请求是 http 时,会自动 301 重定向为 https,uri 保持不变
uri string 可选 可以包含 Nginx 变量的 URI,例如:/test/index.html, $uri/index.html。你可以通过类似于 $ {xxx} 的方式引用变量,以避免产生歧义,例如:${uri}foo/index.html。若你需要保留 $ 字符,那么使用如下格式:/\$foo/index.html
regex_uri array[string] 可选 转发到上游的新 uri 地址, 使用正则表达式匹配来自客户端的 uri,当匹配成功后使用模板替换发送重定向到客户端, 未匹配成功时将客户端请求的 uri 转发至上游。uriregex_uri 不可以同时存在。例如:["^/iresty/(.)/(.)/(.*)","/$1-$2-$3"] 第一个元素代表匹配来自客户端请求的 uri 正则表达式,第二个元素代表匹配成功后发送重定向到客户端的 uri 模板。
ret_code integer 可选 302 [200, …] 请求响应码
encode_uri boolean 可选 false 当设置为 true 时,对返回的 Location header进行编码,编码格式参考 RFC3986
append_query_string boolean optional false 当设置为true时,将请求url的query部分添加到Location里。如果在uriregex_uri中配置了query, 那么请求的query会被追加在这个query后,以&分隔。 注意:如果已经处理了query,比如使用了nginx变量$request_uri,那么启用此功能会造成query重复

http_to_httpsuriregex_uri 三个中只能配置一个。

示例

启用插件

下面是一个基本实例,为特定路由启用 redirect 插件:

curl http://127.0.0.1:9080/apisix/admin/routes/1  -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{"uri": "/test/index.html","plugins": {"redirect": {"uri": "/test/default.html","ret_code": 301}},"upstream": {"type": "roundrobin","nodes": {"127.0.0.1:80": 1}}
}'

我们可以在新的 URI 中使用 Nginx 内置的任意变量:

curl http://127.0.0.1:9080/apisix/admin/routes/1  -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{"uri": "/test","plugins": {"redirect": {"uri": "$uri/index.html","ret_code": 301}},"upstream": {"type": "roundrobin","nodes": {"127.0.0.1:80": 1}}
}'

测试

测试示例基于上述例子:

$ curl http://127.0.0.1:9080/test/index.html -i
HTTP/1.1 301 Moved Permanently
Date: Wed, 23 Oct 2019 13:48:23 GMT
Content-Type: text/html
Content-Length: 166
Connection: keep-alive
Location: /test/default.html...

我们可以检查响应码和响应头中的 Location 参数,它表示该插件已启用。

下面是一个实现 http 到 https 跳转的示例:
```shell
curl http://127.0.0.1:9080/apisix/admin/routes/1  -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{"uri": "/hello","plugins": {"redirect": {"http_to_https": true}}
}'

禁用插件

移除插件配置中相应的 JSON 配置可立即禁用该插件,无需重启服务:

curl http://127.0.0.1:9080/apisix/admin/routes/1  -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{"uri": "/test/index.html","plugins": {},"upstream": {"type": "roundrobin","nodes": {"127.0.0.1:80": 1}}
}'

这时该插件已被禁用。

response-rewrite

简介

该插件支持修改上游服务或网关本身返回的 body 和 header 信息。

使用场景: 1、可以设置 Access-Control-Allow-* 等 header 信息,来实现 CORS (跨域资源共享)的功能。 2、另外也可以通过配置 status_code 和 header 里面的 Location 来实现重定向,当然如果只是需要重定向功能,最好使用 redirect 插件。

配置参数

名称 类型 必选项 默认值 有效值 描述
status_code integer 可选 [200, 598] 修改上游返回状态码,默认保留原始响应代码。
body string 可选 修改上游返回的 body 内容,如果设置了新内容,header 里面的 content-length 字段也会被去掉
body_base64 boolean 可选 false 描述 body 字段是否需要 base64 解码之后再返回给客户端,用在某些图片和 Protobuffer 场景
headers object 可选 返回给客户端的 headers,这里可以设置多个。头信息如果存在将重写,不存在则添加。想要删除某个 header 的话,把对应的值设置为空字符串即可。这个值能够以 $var 的格式包含 Nginx 变量,比如 $remote_addr $balancer_ip
vars array[] 可选 vars 是一个表达式列表,只有满足条件的请求和响应才会修改 body 和 header 信息,来自 lua-resty-expr。如果 vars 字段为空,那么所有的重写动作都会被无条件的执行。

示例

开启插件

下面是一个示例,在指定的 route 上开启了 response-rewrite 插件:

curl http://127.0.0.1:9080/apisix/admin/routes/1  -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{"methods": ["GET"],"uri": "/test/index.html","plugins": {"response-rewrite": {"body": "{\"code\":\"ok\",\"message\":\"new json body\"}","headers": {"X-Server-id": 3,"X-Server-status": "on","X-Server-balancer_addr": "$balancer_ip:$balancer_port"},"vars":[[ "status","==",200 ]]}},"upstream": {"type": "roundrobin","nodes": {"127.0.0.1:80": 1}}
}'

测试插件

基于上述配置进行测试:

curl -X GET -i  http://127.0.0.1:9080/test/index.html

Copy

如果看到返回的头部信息和内容都被修改了,即表示 response-rewrite 插件生效了,vars 将确保仅覆盖状态为 200 的响应。

HTTP/1.1 200 OK
Date: Sat, 16 Nov 2019 09:15:12 GMT
Transfer-Encoding: chunked
Connection: keep-alive
X-Server-id: 3
X-Server-status: on
X-Server-balancer_addr: 127.0.0.1:80{"code":"ok","message":"new json body"}

禁用插件

禁用response-rewrite插件很简单。你不需要重新启动服务,只需要在插件配置中删除相应的 json 配置,它将立即生效。

curl http://127.0.0.1:9080/apisix/admin/routes/1  -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{"methods": ["GET"],"uri": "/test/index.html","upstream": {"type": "roundrobin","nodes": {"127.0.0.1:80": 1}}
}'

注意事项

ngx.exit将中断当前请求的执行,并返回状态码给 Nginx。

ngx.edit tabular overview

但是很多人可能会对ngx.exit理解出现偏差,即如果你在access阶段执行ngx.exit,只是中断了请求处理阶段,响应阶段仍然会处理。比如,如果你配置了response-rewrite插件,它会强制覆盖你的响应信息(如响应代码)。

cors

简介

cors 插件可以让你为服务端启用 CORS 的返回头。

属性

名称 类型 可选项 默认值 有效值 描述
allow_origins string 可选 “*” 允许跨域访问的 Origin,格式如:scheme😕/host:port,比如: https://somehost.com:8081 。多个值使用 , 分割,allow_credentialfalse 时可以使用 * 来表示所有 Origin 均允许通过。你也可以在启用了 allow_credential 后使用 ** 强制允许所有 Origin 都通过,但请注意这样存在安全隐患。
allow_methods string 可选 “*” 允许跨域访问的 Method,比如: GETPOST等。多个值使用 , 分割,allow_credentialfalse 时可以使用 * 来表示所有 Origin 均允许通过。你也可以在启用了 allow_credential 后使用 ** 强制允许所有 Method 都通过,但请注意这样存在安全隐患。
allow_headers string 可选 “*” 允许跨域访问时请求方携带哪些非 CORS 规范 以外的 Header, 多个值使用 , 分割,allow_credentialfalse 时可以使用 * 来表示所有 Header 均允许通过。你也可以在启用了 allow_credential 后使用 ** 强制允许所有 Header 都通过,但请注意这样存在安全隐患。
expose_headers string 可选 “*” 允许跨域访问时响应方携带哪些非 CORS 规范 以外的 Header, 多个值使用 , 分割,allow_credentialfalse 时可以使用 * 来表示允许任意 Header 。你也可以在启用了 allow_credential 后使用 ** 强制允许任意 Header,但请注意这样存在安全隐患。
max_age integer 可选 5 浏览器缓存 CORS 结果的最大时间,单位为秒,在这个时间范围内浏览器会复用上一次的检查结果,-1 表示不缓存。请注意各个浏览器允许的最大时间不同,详情请参考 MDN。
allow_credential boolean 可选 false 是否允许跨域访问的请求方携带凭据(如 Cookie 等)。根据 CORS 规范,如果设置该选项为 true,那么将不能在其他选项中使用 *
allow_origins_by_regex array 可选 nil 使用正则表达式数组来匹配允许跨域访问的 Origin,如[".*.test.com"] 可以匹配任何test.com的子域名*

提示

请注意 allow_credential 是一个很敏感的选项,谨慎选择开启。开启之后,其他参数默认的 * 将失效,你必须显式指定它们的值。 使用 ** 时要充分理解它引入了一些安全隐患,比如 CSRF,所以确保这样的安全等级符合自己预期再使用。

如何启用

创建 RouteService 对象,并配置 cors 插件。

curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{"uri": "/hello","plugins": {"cors": {}},"upstream": {"type": "roundrobin","nodes": {"127.0.0.1:8080": 1}}
}'

测试插件

请求下接口,发现接口已经返回了CORS相关的header,代表插件生效

curl http://127.0.0.1:9080/hello -v
...
< Server: APISIX web server
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: *
< Access-Control-Allow-Headers: *
< Access-Control-Expose-Headers: *
< Access-Control-Max-Age: 5
...

禁用插件

当你想去掉 cors 插件的时候,很简单,在插件的配置中把对应的 json 配置删除即可,无须重启服务,即刻生效:

$ curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{"uri": "/hello","plugins": {},"upstream": {"type": "roundrobin","nodes": {"127.0.0.1:8080": 1}}
}'

现在就已经移除了 cors 插件了。其他插件的开启和移除也是同样的方法。

uri-blocker

定义

该插件可帮助我们拦截用户请求,只需要指定block_rules即可。

属性列表

名称 类型 必选项 默认值 有效值 描述
block_rules array[string] 必须 正则过滤数组。它们都是正则规则,如果当前请求 URI 命中任何一个,请将响应代码设置为 rejected_code 以退出当前用户请求。例如: ["root.exe", "root.m+"]
rejected_code integer 可选 403 [200, …] 当请求 URI 命中block_rules中的任何一个时,将返回的 HTTP 状态代码。
rejected_msg string 可选 非空 当请求 URI 命中block_rules中的任何一个时,将返回的 HTTP 响应体。
case_insensitive boolean 可选 false 是否忽略大小写。当值为 true 时,在匹配请求 URI 时将忽略大小写。默认值是 false 。

启用方式

这是一个示例,在指定的路由上启用uri blocker插件:

curl -i http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{"uri": "/*","plugins": {"uri-blocker": {"block_rules": ["root.exe", "root.m+"]}},"upstream": {"type": "roundrobin","nodes": {"127.0.0.1:1980": 1}}
}'

测试插件

$ curl -i http://127.0.0.1:9080/root.exe?a=a
HTTP/1.1 403 Forbidden
Date: Wed, 17 Jun 2020 13:55:41 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 150
Connection: keep-alive
Server: APISIX web server... ...

如果你设置了属性 rejected_msg 的值为 "access is not allowed" ,将会收到如下的响应体:

$ curl -i http://127.0.0.1:9080/root.exe?a=a
HTTP/1.1 403 Forbidden
Date: Wed, 17 Jun 2020 13:55:41 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 150
Connection: keep-alive
Server: APISIX web server{"error_msg":"access is not allowed"}

禁用插件

当想禁用uri blocker插件时,非常简单,只需要在插件配置中删除相应的 json 配置,无需重启服务,即可立即生效:

curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{"uri": "/*","upstream": {"type": "roundrobin","nodes": {"127.0.0.1:1980": 1}}
}'

uri blocker 插件现在已被禁用,它也适用于其他插件。

ip-restriction

名字

ip-restriction 可以通过以下方式限制对服务或路线的访问,将 IP 地址列入白名单或黑名单。 单个 IP 地址,多个 IP 地址 或 CIDR 范围,可以使用类似 10.10.10.0/24 的 CIDR 表示法。

属性

参数名 类型 可选项 默认值 有效值 描述
whitelist array[string] 可选 加入白名单的 IP 地址或 CIDR 范围
blacklist array[string] 可选 加入黑名单的 IP 地址或 CIDR 范围
message string 可选 Your IP address is not allowed. [1, 1024] 在未允许的IP访问的情况下返回的信息

只能单独启用白名单或黑名单,两个不能一起使用。 message可以由用户自定义。

如何启用

下面是一个示例,在指定的 route 上开启了 ip-restriction 插件:

curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{"uri": "/index.html","upstream": {"type": "roundrobin","nodes": {"127.0.0.1:1980": 1}},"plugins": {"ip-restriction": {"whitelist": ["127.0.0.1","113.74.26.106/24"]}}
}'

当未允许的IP访问时,默认返回{"message":"Your IP address is not allowed"}。如果你想使用自定义的message,可以在插件部分进行配置:

"plugins": {"ip-restriction": {"whitelist": ["127.0.0.1","113.74.26.106/24"],"message": "Do you want to do something bad?"}
}

测试插件

通过 127.0.0.1 访问:

$ curl http://127.0.0.1:9080/index.html -i
HTTP/1.1 200 OK
...

通过 127.0.0.2 访问:

$ curl http://127.0.0.1:9080/index.html -i --interface 127.0.0.2
HTTP/1.1 403 Forbidden
...
{"message":"Your IP address is not allowed"}

禁用插件

当你想去掉 ip-restriction 插件的时候,很简单,在插件的配置中把对应的 json 配置删除即可,无须重启服务,即刻生效:

$ curl http://127.0.0.1:9080/apisix/admin/routes/1  -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{"uri": "/index.html","plugins": {},"upstream": {"type": "roundrobin","nodes": {"39.97.63.215:80": 1}}
}'

现在就已移除 ip-restriction 插件,其它插件的开启和移除也类似。

proxy-mirror

代理镜像插件,该插件提供了镜像客户端请求的能力。

注:镜像请求返回的响应会被忽略。

参数

名称 类型 必选项 默认值 有效值 描述
host string 必须 指定镜像服务地址,例如:http://127.0.0.1:9797(地址中需要包含 schema :http或https,不能包含 URI 部分)
sample_ratio number 可选 1 [0.00001, 1] 镜像请求采样率

示例

启用插件

示例1:为特定路由启用 proxy-mirror 插件:

curl http://127.0.0.1:9080/apisix/admin/routes/1  -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{"plugins": {"proxy-mirror": {"host": "http://127.0.0.1:9797"}},"upstream": {"nodes": {"127.0.0.1:1999": 1},"type": "roundrobin"},"uri": "/hello"
}'

测试:

$ curl http://127.0.0.1:9080/hello -i
HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Length: 12
Connection: keep-alive
Server: APISIX web server
Date: Wed, 18 Mar 2020 13:01:11 GMT
Last-Modified: Thu, 20 Feb 2020 14:21:41 GMThello world

由于指定的 mirror 地址是127.0.0.1:9797,所以验证此插件是否已经正常工作需要在端口为9797的服务上确认,例如,我们可以通过 python 启动一个简单的 server: python -m SimpleHTTPServer 9797。

禁用插件

移除插件配置中相应的 JSON 配置可立即禁用该插件,无需重启服务:

curl http://127.0.0.1:9080/apisix/admin/routes/1  -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{"uri": "/hello","plugins": {},"upstream": {"type": "roundrobin","nodes": {"127.0.0.1:1999": 1}}
}'

这时该插件已被禁用。

traffic-split

名字

traffic-split 插件使用户可以逐步引导各个上游之间的流量百分比。

注:由于加权循环算法(特别是在重置wrr状态时)的缺点,因此每个上游之间的比率可能不太准确。

属性

参数名 类型 可选项 默认值 有效值 描述
rules.match array[object] 可选 匹配规则列表,默认为空且规则将被无条件执行。
rules.match.vars array[array] 可选 由一个或多个{var, operator, val}元素组成的列表,类似这样:{{var, operator, val}, {var, operator, val}, …}}。例如:{“arg_name”, “==”, “json”},表示当前请求参数 name 是 json。这里的 var 与 Nginx 内部自身变量命名是保持一致,所以也可以使用 request_uri、host 等;对于 operator 部分,目前已支持的运算符有 ==、=、~、>、<、in、has 和 ! 。操作符的具体用法请看 lua-resty-expr 的 operator-list 部分。
rules.weighted_upstreams array[object] 可选 上游配置规则列表。
weighted_upstreams.upstream_id string / integer 可选 通过上游 id 绑定对应上游。
weighted_upstreams.upstream object 可选 上游配置信息。
upstream.type enum 可选 roundrobin [roundrobin, chash] roundrobin 支持权重的负载,chash 一致性哈希,两者是二选一。
upstream.hash_on enum 可选 vars hash_on 支持的类型有 vars(Nginx 内置变量),header(自定义 header),cookieconsumervars_combinations,默认值为 vars。更多详细信息请参考 upstream 用法。
upstream.key string 可选 该选项只有类型是 chash 才有效。根据 key 来查找对应的 node id,相同的 key 在同一个对象中,永远返回相同 id。更多详细信息请参考 upstream 用法。
upstream.nodes object 可选 哈希表,内部元素的 key 是上游机器地址 列表,格式为地址 + Port,其中地址部 分可以是 IP 也可以是域名,⽐如 192.168.1.100:80、foo.com:80等。 value 则是节点的权重,特别的,当权重 值为 0 有特殊含义,通常代表该上游节点 失效,永远不希望被选中。
upstream.timeout object 可选 15 设置连接、发送消息、接收消息的超时时间(时间单位:秒,都默认为 15 秒)。
upstream.pass_host enum 可选 “pass” [“pass”, “node”, “rewrite”] pass: 将客户端的 host 透传给上游; node: 使用 upstream node 中配置的 host; rewrite: 使用配置项 upstream_host 的值
upstream.name string 可选 标识上游服务名称、使⽤场景等。
upstream.upstream_host string 可选 只在 pass_host 配置为 rewrite 时有效。
weighted_upstreams.weight integer 可选 weight = 1 根据 weight 值做流量划分,多个 weight 之间使用 roundrobin 算法划分。

目前在 weighted_upstreams.upstream 的配置中,不支持的字段有: service_name、discovery_type、checks、retries、retry_timeout、desc、scheme、labels、create_time 和 update_time。但是你可以通过 weighted_upstreams.upstream_id 绑定 upstream 对象来实现他们。

traffic-split 插件主要由 matchweighted_upstreams 两部分组成,match 是自定义的条件规则,weighted_upstreams 是 upstream 的配置信息。如果配置 matchweighted_upstreams 信息,那么在 match 规则校验通过后,会根据 weighted_upstreams 中的 weight 值;引导插件中各个 upstream 之间的流量比例,否则,所有流量直接到达 routeservice 上配置的 upstream。当然你也可以只配置 weighted_upstreams 部分,这样会直接根据 weighted_upstreams 中的 weight 值,引导插件中各个 upstream 之间的流量比例。

注:1、在 match 里,vars 中的表达式是 and 的关系,多个 vars 之间是 or 的关系。2、在插件的 weighted_upstreams 域中,如果存在只有 weight 的结构,表示 routeservice 上的 upstream 流量权重值。例如:

"weighted_upstreams": [......{"weight": 2}
]

如何启用

创建一个路由并启用 traffic-split 插件,在配置插件上游信息时,有以下两种方式:

1、通过插件中的 upstream 属性配置上游信息。

curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{"uri": "/index.html","plugins": {"traffic-split": {"rules": [{"weighted_upstreams": [{"upstream": {"name": "upstream_A","type": "roundrobin","nodes": {"127.0.0.1:1981":10},"timeout": {"connect": 15,"send": 15,"read": 15}},"weight": 1},{"weight": 1}]}]}},"upstream": {"type": "roundrobin","nodes": {"127.0.0.1:1980": 1}}
}'

2、通过插件中的 upstream_id 属性绑定上游服务。

curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{"uri": "/index.html","plugins": {"traffic-split": {"rules": [{"weighted_upstreams": [{"upstream_id": 1,"weight": 1},{"weight": 1}]}]}},"upstream": {"type": "roundrobin","nodes": {"127.0.0.1:1980": 1}}
}'

注:1、通过 upstream_id 方式来绑定已定义的上游,它可以复用上游具有的健康检测、重试等功能。2、支持 upstreamupstream_id 的两种配置方式一起使用。

示例

灰度发布

缺少 match 规则部分,根据插件中 weighted_upstreams 配置的 weight 值做流量分流。将 插件的 upstreamroute 的 upstream 按 3:2 的流量比例进行划分,其中 60% 的流量到达插件中的 1981 端口的 upstream, 40% 的流量到达 route 上默认 1980 端口的 upstream。

curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{"uri": "/index.html","plugins": {"traffic-split": {"rules": [{"weighted_upstreams": [{"upstream": {"name": "upstream_A","type": "roundrobin","nodes": {"127.0.0.1:1981":10},"timeout": {"connect": 15,"send": 15,"read": 15}},"weight": 3},{"weight": 2}]}]}},"upstream": {"type": "roundrobin","nodes": {"127.0.0.1:1980": 1}}
}'

插件测试:

请求5次,3次请求命中插件1981端口的 upstream, 2次请求命中 route 的1980端口 upstream。

$ curl http://127.0.0.1:9080/index.html -i
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8hello 1980$ curl http://127.0.0.1:9080/index.html -i
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8world 1981......

蓝绿发布

通过请求头获取 match 规则参数(也可以通过请求参数获取或NGINX变量),在 match 规则匹配通过后,表示所有请求都命中到插件配置的 upstream ,否则所以请求只命中 route 上配置的 upstream 。

curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{"uri": "/index.html","plugins": {"traffic-split": {"rules": [{"match": [{"vars": [["http_release","==","new_release"]]}],"weighted_upstreams": [{"upstream": {"name": "upstream_A","type": "roundrobin","nodes": {"127.0.0.1:1981":10}}}]}]}},"upstream": {"type": "roundrobin","nodes": {"127.0.0.1:1980": 1}}
}'

插件测试:

match 规则匹配通过,所有请求都命中插件配置的1981端口 upstream :

$ curl http://127.0.0.1:9080/index.html -H 'release: new_release' -i
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
......world 1981

match 规则匹配失败,所有请求都命中 route 上配置的 1980端口 upstream :

$ curl http://127.0.0.1:9080/index.html -H 'release: old_release' -i
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
......hello 1980

自定义发布

match 中可以设置多个 vars 规则,vars 中的多个表达式之间是 add 的关系, 多个 vars 规则之间是 or 的关系;只要其中一个 vars 规则通过,则整个 match 通过。

示例1:只配置了一个 vars 规则, vars 中的多个表达式是 add 的关系。在 weighted_upstreams 中根据 weight 值将流量按 3:2 划分,其中只有 weight 值的部分表示 route 上的 upstream 所占的比例。 当 match 匹配不通过时,所有的流量只会命中 route 上的 upstream 。

curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{"uri": "/index.html","plugins": {"traffic-split": {"rules": [{"match": [{"vars": [["arg_name","==","jack"],["http_user-id",">","23"],["http_apisix-key","~~","[a-z]+"]]}],"weighted_upstreams": [{"upstream": {"name": "upstream_A","type": "roundrobin","nodes": {"127.0.0.1:1981":10}},"weight": 3},{"weight": 2}]}]}},"upstream": {"type": "roundrobin","nodes": {"127.0.0.1:1980": 1}}
}'

插件设置了请求的 match 规则及端口为1981的 upstream,route 上具有端口为1980的 upstream。

插件测试:

1、在 match 规则校验通过后, 60% 的请求命中到插件的1981端口的 upstream, 40% 的请求命中到 route 的1980端口的 upstream。

match 规则校验成功, 命中端口为1981的 upstream。

$ curl 'http://127.0.0.1:9080/index.html?name=jack' -H 'user-id:30' -H 'apisix-key: hello' -i
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
......world 1981

match 规则校验失败,,命中默认端口为1980的 upstream。

$ curl 'http://127.0.0.1:9080/index.html?name=jack' -H 'user-id:30' -H 'apisix-key: hello' -i
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
......hello 1980

在请求5次后,3次命中 1981 端口的服务,2次命中 1980 端口的服务。

2、match 规则校验失败(缺少请求头 apisix-key ), 响应都为默认 upstream 的数据 hello 1980

$ curl 'http://127.0.0.1:9080/index.html?name=jack' -H 'user-id:30' -i
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
......hello 1980

示例2:配置多个 vars 规则, vars 中的多个表达式是 add 的关系, 多个 vars 之间是 or 的关系。根据 weighted_upstreams 中的 weight 值将流量按 3:2 划分,其中只有 weight 值的部分表示 route 上的 upstream 所占的比例。 当 match 匹配不通过时,所有的流量只会命中 route 上的 upstream 。

curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{"uri": "/index.html","plugins": {"traffic-split": {"rules": [{"match": [{"vars": [["arg_name","==","jack"],["http_user-id",">","23"],["http_apisix-key","~~","[a-z]+"]]},{"vars": [["arg_name2","==","rose"],["http_user-id2","!",">","33"],["http_apisix-key2","~~","[a-z]+"]]}],"weighted_upstreams": [{"upstream": {"name": "upstream_A","type": "roundrobin","nodes": {"127.0.0.1:1981":10}},"weight": 3},{"weight": 2}]}]}},"upstream": {"type": "roundrobin","nodes": {"127.0.0.1:1980": 1}}
}'

插件设置了请求的 match 规则及端口为1981的 upstream,route 上具有端口为1980的 upstream 。

测试插件:

1、两个 vars 的表达式匹配成功, match 规则校验通过后, 60% 的请求命中到插件的1981端口 upstream, 40% 的请求命中到 route 的1980端口upstream。

$ curl 'http://127.0.0.1:9080/index.html?name=jack&name2=rose' -H 'user-id:30' -H 'user-id2:22' -H 'apisix-key: hello' -H 'apisix-key2: world' -i
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
......world 1981
$ curl 'http://127.0.0.1:9080/index.html?name=jack&name2=rose' -H 'user-id:30' -H 'user-id2:22' -H 'apisix-key: hello' -H 'apisix-key2: world' -i
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
......hello 1980

在请求5次后,3次命中 1981 端口的服务,2次命中 1980 端口的服务。

2、第二个 vars 的表达式匹配失败(缺少 name2 请求参数),match 规则校验通过后, 60% 的请求命中到插件的1981端口 upstream, 40% 的请求流量命中到 route 的1980端口 upstream。

$ curl 'http://127.0.0.1:9080/index.html?name=jack' -H 'user-id:30' -H 'user-id2:22' -H 'apisix-key: hello' -H 'apisix-key2: world' -i
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
......world 1981
$ curl 'http://127.0.0.1:9080/index.html?name=jack' -H 'user-id:30' -H 'user-id2:22' -H 'apisix-key: hello' -H 'apisix-key2: world' -i
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
......hello 1980

在请求5次后,3次命中 1981 端口的服务,2次命中 1980 端口的服务。

3、两个 vars 的表达式校验失败(缺少 namename2 请求参数),match 规则校验失败, 响应都为默认 route 的 upstream 数据 hello 1980

$ curl 'http://127.0.0.1:9080/index.html?name=jack' -i
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
......hello 1980

匹配规则与上游对应

通过配置多个 rules,我们可以实现不同的匹配规则与上游一一对应。

示例:

当请求头 x-api-id 等于 1 时,命中 1981 端口的上游;当 x-api-id 等于 2 时,命中 1982 端口的上游;否则,命中 1980 端口的上游(上游响应数据为对应的端口号)。

curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{"uri": "/hello","plugins": {"traffic-split": {"rules": [{"match": [{"vars": [["http_x-api-id","==","1"]]}],"weighted_upstreams": [{"upstream": {"name": "upstream-A","type": "roundrobin","nodes": {"127.0.0.1:1981":1}},"weight": 3}]},{"match": [{"vars": [["http_x-api-id","==","2"]]}],"weighted_upstreams": [{"upstream": {"name": "upstream-B","type": "roundrobin","nodes": {"127.0.0.1:1982":1}},"weight": 3}]}]}},"upstream": {"type": "roundrobin","nodes": {"127.0.0.1:1980": 1}}
}'

测试插件:

请求头 x-api-id 等于 1,命中带 1981 端口的上游。

$ curl http://127.0.0.1:9080/hello -H 'x-api-id: 1'
1981

请求头 x-api-id 等于 2,命中带 1982 端口的上游。

$ curl http://127.0.0.1:9080/hello -H 'x-api-id: 2'
1982

请求头 x-api-id 等于 3,规则不匹配,命中带 1980 端口的上游。

$ curl http://127.0.0.1:9080/hello -H 'x-api-id: 3'
1980

禁用插件

当你想去掉 traffic-split 插件的时候,很简单,在插件的配置中把对应的 json 配置删除即可,无须重启服务,即刻生效:

$ curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{"uri": "/index.html","plugins": {},"upstream": {"type": "roundrobin","nodes": {"127.0.0.1:1980": 1}}
}'

Published by

风君子

独自遨游何稽首 揭天掀地慰生平