traffic-split
注:由于加权循环算法(特别是在重置 wrr 状态时)的缺点,因此每个上游之间的比率可能不太准确。
目前在 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 流量权重值。例如:
创建一个路由并启用 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
}
}
}'
缺少 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-8
hello 1980
$ curl http://127.0.0.1:9080/index.html -i
HTTP/1.1 200 OK
Content-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' -i
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
......
world 1981
match
规则匹配失败,所有请求都命中 route
上配置的 1980 端口 upstream :
match
中可以设置多个 vars
规则,vars
中的多个表达式之间是 and
的关系, 多个 vars
规则之间是 or
的关系;只要其中一个 vars 规则通过,则整个 match
通过。
示例 1:只配置了一个 vars
规则, vars
中的多个表达式是 and
的关系。在 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
端口的服务。
$ 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
中的多个表达式是 and
的关系, 多个 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": [
{
["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
}
}
}'
测试插件:
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
在请求 5 次后,3 次命中 1981
端口的服务,2 次命中 1980
端口的服务。
$ 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
当你想去掉 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
}
}'