traffic-split
traffic-split 插件根据条件和/或权重将流量引导至各种上游服务。它提供了一种动态且灵活的方法来实现发布策略和管理流量。
示例
以下示例展示了使用 traffic-split 插件的不同用例。
实现金丝雀发布
以下示例演示了如何使用此插件实现金丝雀发布。
金丝雀发布是一种渐进式部署,其中增加的流量百分比被引导至新版本,从而允许进行受控和监控的推出。此方法可确保在完全重定向所有流量之前,尽早识别并解决新版本中的任何潜在问题或错误。
创建一个路由并配置 traffic-split 插件,规则如下:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"uri": "/headers",
"id": "traffic-split-route",
"plugins": {
"traffic-split": {
"rules": [
{
"weighted_upstreams": [
{
"upstream": {
"type": "roundrobin",
"scheme": "https",
"pass_host": "node",
"nodes": {
"httpbin.org:443":1
}
},
"weight": 3
},
{
"weight": 2
}
]
}
]
}
},
"upstream": {
"type": "roundrobin",
"scheme": "https",
"pass_host": "node",
"nodes": {
"mock.api7.ai:443":1
}
}
}'
每个上游的流量比例 由该上游的权重相对于所有上游的总权重决定。这里,总权重计算为:3 + 2 = 5。
因此:
❶ 预计 60% 的流量将转发到 httpbin.org。
❷ 预计 40% 的流量将转发到 mock.api7.ai。
向路由发送 10 个连续请求以进行验证:
resp=$(seq 10 | xargs -I{} curl "http://127.0.0.1:9080/headers" -sL) && \
count_httpbin=$(echo "$resp" | grep "httpbin.org" | wc -l) && \
count_mockapi7=$(echo "$resp" | grep "mock.api7.ai" | wc -l) && \
echo httpbin.org: $count_httpbin, mock.api7.ai: $count_mockapi7
你应该看到类似于以下的响应:
httpbin.org: 6, mock.api7.ai: 4
相应地调整上游权重以完成金丝雀发布。
实现蓝绿部署
以下示例演示了如何使用此插件实现蓝绿部署。
蓝绿部署是一种部署策略,涉及维护两个相同的环境:蓝色 和 绿色。蓝色环境指的是当前的生产部署,绿色环境指的是新部署。一旦绿色环境经过测试准备好进行生产,流量将被路由到绿色环境,使其成为新的生产部署。
创建一个路由并配置 traffic-split 插件,规则如下:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"uri": "/headers",
"id": "traffic-split-route",
"plugins": {
"traffic-split": {
"rules": [
{
"match": [
{
"vars": [
["http_release","==","new_release"]
]
}
],
"weighted_upstreams": [
{
"upstream": {
"type": "roundrobin",
"scheme": "https",
"pass_host": "node",
"nodes": {
"httpbin.org:443":1
}
}
}
]
}
]
}
},
"upstream": {
"type": "roundrobin",
"scheme": "https",
"pass_host": "node",
"nodes": {
"mock.api7.ai:443":1
}
}
}'
❶ 仅当请求包含请求头 release: new_release 时才执行插件以重定向流量。
向带有 release 请求头的路由发送请求:
curl "http://127.0.0.1:9080/headers" -H 'release: new_release'
你应该看到类似于以下的响应:
{
"headers": {
"Accept": "*/*",
"Host": "httpbin.org",
...
}
}
向不带任何附加请求头的路由发送请求:
curl "http://127.0.0.1:9080/headers"
你应该看到类似于以下的响应:
{
"headers": {
"accept": "*/*",
"host": "mock.api7.ai",
...
}
}
使用 APISIX 表达式定义 POST 请求的匹配条件
以下示例演示了如何使用 APISIX 表达式 在规则中定义匹配条件,以便在满足 POST 请求的特定条件时有条件地执行插件。
创建一个路由并配置 traffic-split 插件,规则如下:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"uri": "/post",
"methods": ["POST"],
"id": "traffic-split-route",
"plugins": {
"traffic-split": {
"rules": [
{
"match": [
{
"vars": [
["post_arg_id", "==", "1"]
]
}
],
"weighted_upstreams": [
{
"upstream": {
"type": "roundrobin",
"scheme": "https",
"pass_host": "node",
"nodes": {
"httpbin.org:443":1
}
}
}
]
}
]
}
},
"upstream": {
"type": "roundrobin",
"scheme": "https",
"pass_host": "node",
"nodes": {
"mock.api7.ai:443":1
}
}
}'
发送正文中带有 id=1 的 POST 请求:
curl "http://127.0.0.1:9080/post" -X POST \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'id=1'
❶ 你也可以在 Content-Type 中指定字符集,例如 Content-Type: application/x-www-form-urlencoded;charset=UTF-8。
你应该看到类似于以下的响应:
{
"args": {},
"data": "",
"files": {},
"form": {
"id": "1"
},
"headers": {
"Accept": "*/*",
"Content-Length": "4",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
...
},
...
}
发送正文中不带 id=1 的 POST 请求:
curl "http://127.0.0.1:9080/post" -X POST \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'random=string'
你应该看到请求被转发到了 mock.api7.ai。
使用 APISIX 表达式定义 AND 匹配条件
以下示例演示了如何使用 APISIX 表达式 在规则中定义匹配条件,以便在满足多个条件时有条件地执行插件。
创建一个路由并配置 traffic-split 插件,匹配规则如下:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"uri": "/headers",
"id": "traffic-split-route",
"plugins": {
"traffic-split": {
"rules": [
{
"match": [
{
"vars": [
["arg_name","==","jack"],
["http_user-id",">","23"],
["http_apisix-key","~~","[a-z]+"]
]
}
],
"weighted_upstreams": [
{
"upstream": {
"type": "roundrobin",
"scheme": "https",
"pass_host": "node",
"nodes": {
"httpbin.org:443":1
}
},
"weight": 3
},
{
"weight": 2
}
]
}
]
}
},
"upstream": {
"type": "roundrobin",
"scheme": "https",
"pass_host": "node",
"nodes": {
"mock.api7.ai:443":1
}
}
}'
❶ 仅当满足所有三个条件时才执行插件以重定向流量。
如果满足条件,60% 的流量应被引导至 httpbin.org,另外 40% 应被引导至 mock.api7.ai。如果不满足条件,所有流量应被引导至 mock.api7.ai。
发送 10 个满足所有条件的连续请求以进行验证:
resp=$(seq 10 | xargs -I{} curl "http://127.0.0.1:9080/headers?name=jack" -H 'user-id: 30' -H 'apisix-key: helloapisix' -sL) && \
count_httpbin=$(echo "$resp" | grep "httpbin.org" | wc -l) && \
count_mockapi7=$(echo "$resp" | grep "mock.api7.ai" | wc -l) && \
echo httpbin.org: $count_httpbin, mock.api7.ai: $count_mockapi7
你应该看到类似于以下的响应:
httpbin.org: 6, mock.api7.ai: 4
发送 10 个不满足条件的连续请求以进行验证:
resp=$(seq 10 | xargs -I{} curl "http://127.0.0.1:9080/headers?name=random" -sL) && \
count_httpbin=$(echo "$resp" | grep "httpbin.org" | wc -l) && \
count_mockapi7=$(echo "$resp" | grep "mock.api7.ai" | wc -l) && \
echo httpbin.org: $count_httpbin, mock.api7.ai: $count_mockapi7
你应该看到类似于以下的响应:
httpbin.org: 0, mock.api7.ai: 10
使用 APISIX 表达式定义 OR 匹配条件
以下示例演示了如何使用 APISIX 表达式 在规则中定义匹配条件,以便在满足任一组条件时有条件地执行插件。
创建一个路由并配置 traffic-split 插件,匹配规则如下:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"uri": "/headers",
"id": "traffic-split-route",
"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": {
"type": "roundrobin",
"scheme": "https",
"pass_host": "node",
"nodes": {
"httpbin.org:443":1
}
},
"weight": 3
},
{
"weight": 2
}
]
}
]
}
},
"upstream": {
"type": "roundrobin",
"scheme": "https",
"pass_host": "node",
"nodes": {
"mock.api7.ai:443":1
}
}
}'
❶ 和 ❷:当满足任一组条件时执行插件以重定向流量。
或者,你也可以在这些条件的 APISIX 表达式 中使用 OR 运算符。
如果满足条件,60% 的流量应被引导至 httpbin.org,另外 40% 应被引导至 mock.api7.ai。如果不满足条件,所有流量应被引导至 mock.api7.ai。
发送 10 个满足第二组条件的连续请求以进行验证:
resp=$(seq 10 | xargs -I{} curl "http://127.0.0.1:9080/headers?name2=rose" -H 'user-id:30' -H 'apisix-key2: helloapisix' -sL) && \
count_httpbin=$(echo "$resp" | grep "httpbin.org" | wc -l) && \
count_mockapi7=$(echo "$resp" | grep "mock.api7.ai" | wc -l) && \
echo httpbin.org: $count_httpbin, mock.api7.ai: $count_mockapi7
你应该看到类似于以下的响应:
httpbin.org: 6, mock.api7.ai: 4
发送 10 个不满足任一组条件的连续请求以进行验证:
resp=$(seq 10 | xargs -I{} curl "http://127.0.0.1:9080/headers?name=random" -sL) && \
count_httpbin=$(echo "$resp" | grep "httpbin.org" | wc -l) && \
count_mockapi7=$(echo "$resp" | grep "mock.api7.ai" | wc -l) && \
echo httpbin.org: $count_httpbin, mock.api7.ai: $count_mockapi7
你应该看到类似于以下的响应:
httpbin.org: 0, mock.api7.ai: 10
为不同的上游配置不同的规则
以下示例演示了如何在规则集和上游之间设置一对一的映射。
创建一个路由并配置 traffic-split 插件,匹配规则如下:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"uri": "/headers",
"id": "traffic-split-route",
"plugins": {
"traffic-split": {
"rules": [
{
"match": [
{
"vars": [
["http_x-api-id","==","1"]
]
}
],
"weighted_upstreams": [
{
"upstream": {
"type": "roundrobin",
"scheme": "https",
"pass_host": "node",
"nodes": {
"httpbin.org:443":1
}
},
"weight": 1
}
]
},
{
"match": [
{
"vars": [
["http_x-api-id","==","2"]
]
}
],
"weighted_upstreams": [
{
"upstream": {
"type": "roundrobin",
"scheme": "https",
"pass_host": "node",
"nodes": {
"mock.api7.ai:443":1
}
},
"weight": 1
}
]
}
]
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"postman-echo.com:443": 1
},
"scheme": "https",
"pass_host": "node"
}
}'
❶ 仅当请求包含请求头 x-api-id: 1 时才执行插件以重定向流量。
❷ 仅当请求包含请求头 x-api-id: 2 时才执行插件以重定向流量。
发 送带有请求头 x-api-id: 1 的请求:
curl "http://127.0.0.1:9080/headers" -H 'x-api-id: 1'
你应该看到类似于以下的 HTTP/1.1 200 OK 响应:
{
"headers": {
"Accept": "*/*",
"Host": "httpbin.org",
...
}
}
发送带有请求头 x-api-id: 2 的请求:
curl "http://127.0.0.1:9080/headers" -H 'x-api-id: 2'
你应该看到类似于以下的 HTTP/1.1 200 OK 响应:
{
"headers": {
"accept": "*/*",
"host": "mock.api7.ai",
...
}
}
发送不带任何附加请求头的请求:
curl "http://127.0.0.1:9080/headers"
你应该看到类似于以下的响应:
{
"headers": {
"accept": "*/*",
"host": "postman-echo.com",
...
}
}