traffic-split
This is done by configuring match
, which are custom rules for splitting traffic, and weighted_upstreams
which is a set of Upstreams to direct traffic to.
When a request is matched based on the match
attribute configuration, it will be directed to the Upstreams based on their configured weights
. You can also omit using the match
attribute and direct all traffic based on weighted_upstreams
.
note
The traffic ratio between Upstream services may be less accurate since round robin algorithm is used to direct traffic (especially when the state is reset).
note
Some of the configuration fields supported in Upstream are not supported in weighted_upstreams.upstream. These fields are service_name
, discovery_type
, checks
, retries
, retry_timeout
, desc
, scheme
, labels
, create_time
, and update_time
.
As a workaround, you can create an Upstream object and configure it in weighted_upstreams.upstream_id
to achieve these functionalities.
IMPORTANT
In the match
attribute configuration, the expression in variable is related as AND whereas multiple variables are related by OR.
If only the weight
attribute is configured, it corresponds to the weight of the Upstream service configured on the Route or Service. You can see this in action below.
You can configure the Plugin on a Route as shown below:
Alternatively, you can configure upstream_id
if you have already configured an Upstream object:
curl http://127.0.0.1:9180/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
}
}
}'
tip
Configure via upstream_id
to reuse Upstream’s health detection, retires, and other functions.
note
The examples below shows different use cases for using the traffic-split
Plugin.
This is the process of gradually rolling out a release by splitting an increasing percentage of traffic to the new release until all traffic is directed to the new release.
To set this up, you can configure the weight
attribute of your weighted_upstreams
as shown below:
curl http://127.0.0.1:9180/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
}
}
}'
Here, the weights are in the ratio 3:2 which means that 60% of the traffic reaches the Upstream service running on :1981
(Plugin’s Upstream) and 40% reaches the service running on :1980
which is the Route’s Upstream service.
Now to test this configuration, if you make 5 requests, 3 will hit one service and 2 will hit the other:
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
In this setup, user traffic is shifted from the “green” (production) environment to the “blue” (staging) environment once the new changes have been tested and accepted within the blue environment.
To set this up, you can configure match
rules based on the request headers as shown below:
curl http://127.0.0.1:9180/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
}
}
}'
Here, if the request comes with a release
header with value new_release
it is directed to the new Upstream.
Now if you send a request with new_release
as the value for the release
header, it will be directed to one Upstream and other requests will be directed to another 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
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
You can also make custom releases by configuring rules and setting weights.
After the rules are matched, 60% of the traffic hit the Upstream on port 1981
and 40% hit the one on 1980
.
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
If the rule fails to match, then the request is directed to the service on 1980
:
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
In the example below, multiple vars
rules are configured and they have an OR relationship.
curl http://127.0.0.1:9180/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
}
}
In the example below, both the vars
rules are matched. After the rules are matched, 60% of the traffic is directed to the service on 1981
and 40% to the service on 1980
:
curl 'http://127.0.0.1:9080/index.html?name=jack&name2=rose' \
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
In the example below, the second vars
rule fail to match. But since it is an OR relationship, the rules are matched and traffic is directed as configured:
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
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
...
hello 1980
In the example below the required headers are missing and both the vars
rules fail to match and the request is directed to the default Upstream of the Route (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
You can achieve one-to-one correspondence between rules and Upstream by configuring multiple rules
:
For example, when the request header x-api-id
is equal to 1
it should be directed to Upstream on port 1981
and if it is equal to 2
it should be directed to Upstream on port 1982
. And in other cases, it should default to the Upstream on port 1980
. You can configure this as shown below:
curl http://127.0.0.1:9180/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
}
}
}'
Now, when the request header x-api-id
is equal to 1
, it will hit the Upstream on 1981
:
curl http://127.0.0.1:9080/hello -H 'x-api-id: 1'
1981
If request header x-api-id
is equal to 2
, it will hit the Upstream on 1982
:
curl http://127.0.0.1:9080/hello -H 'x-api-id: 2'
1982
curl http://127.0.0.1:9080/hello -H 'x-api-id: 3'
1980
To disable the traffic-split
Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect.
curl http://127.0.0.1:9180/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
}
}'