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、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 -iHTTP/1.1 200 OKContent-Type: text/html; charset=utf-8
hello 1980
$ curl http://127.0.0.1:9080/index.html -iHTTP/1.1 200 OKContent-Type: text/html; charset=utf-8
world 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' -iHTTP/1.1 200 OKContent-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' -iHTTP/1.1 200 OKContent-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' -iHTTP/1.1 200 OKContent-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' -iHTTP/1.1 200 OKContent-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' -iHTTP/1.1 200 OKContent-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' -iHTTP/1.1 200 OKContent-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' -iHTTP/1.1 200 OKContent-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' -iHTTP/1.1 200 OKContent-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' -iHTTP/1.1 200 OKContent-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' -iHTTP/1.1 200 OKContent-Type: text/html; charset=utf-8......
hello 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 } }}'