APISIX 插件机制
Plugin
表示将在 HTTP
请求/响应生命周期期间执行的插件配置。
Plugin
配置可直接绑定在 Route
上,也可以被绑定在 Service
或 Consumer
上。而对于同一 个插件的配置,只能有一份是有效的,配置选择优先级总是 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
注意:如果你已经在路由规则里配置了某个插件(比如在 route
的 plugins
字段里面添加了它),然后 禁用了该插件,在执行路由规则的时候会跳过这个插件。
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 转发至上游。uri 和 regex_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里。如果在uri 或regex_uri 中配置了query, 那么请求的query会被追加在这个query后,以& 分隔。 注意:如果已经处理了query,比如使用了nginx变量$request_uri ,那么启用此功能会造成query重复 |
http_to_https
,uri
或 regex_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.exit
理解出现偏差,即如果你在access
阶段执行ngx.exit
,只是中断了请求处理阶段,响应阶段仍然会处理。比如,如果你配置了response-rewrite
插件,它会强制覆盖你的响应信息(如响应代码)。
cors
简介
cors
插件可以让你为服务端启用 CORS 的返回头。
属性
名称 | 类型 | 可选项 | 默认值 | 有效值 | 描述 |
---|---|---|---|---|---|
allow_origins | string | 可选 | “*” | 允许跨域访问的 Origin,格式如:scheme 😕/host :port ,比如: https://somehost.com:8081 。多个值使用 , 分割,allow_credential 为 false 时可以使用 * 来表示所有 Origin 均允许通过。你也可以在启用了 allow_credential 后使用 ** 强制允许所有 Origin 都通过,但请注意这样存在安全隐患。 |
|
allow_methods | string | 可选 | “*” | 允许跨域访问的 Method,比如: GET ,POST 等。多个值使用 , 分割,allow_credential 为 false 时可以使用 * 来表示所有 Origin 均允许通过。你也可以在启用了 allow_credential 后使用 ** 强制允许所有 Method 都通过,但请注意这样存在安全隐患。 |
|
allow_headers | string | 可选 | “*” | 允许跨域访问时请求方携带哪些非 CORS 规范 以外的 Header, 多个值使用 , 分割,allow_credential 为 false 时可以使用 * 来表示所有 Header 均允许通过。你也可以在启用了 allow_credential 后使用 ** 强制允许所有 Header 都通过,但请注意这样存在安全隐患。 |
|
expose_headers | string | 可选 | “*” | 允许跨域访问时响应方携带哪些非 CORS 规范 以外的 Header, 多个值使用 , 分割,allow_credential 为 false 时可以使用 * 来表示允许任意 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,所以确保这样的安全等级符合自己预期再使用。
如何启用
创建 Route
或 Service
对象,并配置 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),cookie ,consumer ,vars_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 插件主要由 match
和 weighted_upstreams
两部分组成,match
是自定义的条件规则,weighted_upstreams
是 upstream 的配置信息。如果配置 match
和 weighted_upstreams
信息,那么在 match
规则校验通过后,会根据 weighted_upstreams
中的 weight
值;引导插件中各个 upstream 之间的流量比例,否则,所有流量直接到达 route
或 service
上配置的 upstream
。当然你也可以只配置 weighted_upstreams
部分,这样会直接根据 weighted_upstreams
中的 weight
值,引导插件中各个 upstream 之间的流量比例。
注:1、在 match
里,vars 中的表达式是 and
的关系,多个 vars
之间是 or
的关系。2、在插件的 weighted_upstreams 域中,如果存在只有 weight
的结构,表示 route
或 service
上的 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、支持upstream
和upstream_id
的两种配置方式一起使用。
示例
灰度发布
缺少 match
规则部分,根据插件中 weighted_upstreams
配置的 weight
值做流量分流。将 插件的 upstream
与 route 的 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
的表达式校验失败(缺少name
和name2
请求参数),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}}
}'