Manage Traffic Conditionally
Conditional traffic management refers to the ability to execute specific actions based on certain characteristics of each request, such as request headers, URI parameters, or URI paths. This enables one to implement dynamic and flexible routing, rate limiting, or other actions, allowing for granular control and customization of the gateway's behavior based on the specific needs of the application or system.
This guide will show you how you can manage traffic conditionally in APISIX using the traffic-split, traffic-label, and workflow plugins.
Prerequisite(s)
- Install Docker.
- Install cURL to send requests to the services for validation.
- Follow the Getting Started tutorial to start a new APISIX instance in Docker or on Kubernetes.
Forward Traffic Conditionally and Proportionally
In this section, you will learn how to forward traffic to different upstream services conditionally and by weights, using the traffic-split plugin.
Create a route as such:
- Admin API
- ADC
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
}
}
}'
❶ Execute the plugin to redirect traffic only when the request contains a URL parameter name=jack, a header user-id with its value greater than 23, and another header apisix-key with its value composed of lowercase English letters.
services:
- name: Mock Service
labels:
docs-example: conditional-traffic-management
routes:
- uris:
- /headers
name: 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:
- host: mock.api7.ai
port: 443
weight: 1
❶ Execute the plugin to redirect traffic only when the request contains a URL parameter name=jack, a header user-id with its value greater than 23, and another header apisix-key with its value composed of lowercase English letters.
ADC reconciles services as desired state. The label selector limits this example to its own service. Preview the scoped changes and confirm that they contain no unintended updates or deletions:
adc diff -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=conditional-traffic-management
Synchronize the reviewed service configuration:
adc sync -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=conditional-traffic-management
If conditions are satisfied, 60% of the traffic should be directed to httpbin.org and the other 40% should be directed to mock.api7.ai. If conditions are not satisfied, all traffic should be directed to mock.api7.ai.
Send 10 consecutive requests that satisfy all conditions to verify:
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
You should see a response similar to the following:
httpbin.org: 6, mock.api7.ai: 4
Send two batches in which name and user-id satisfy their conditions, but apisix-key contains a digit or uppercase letters:
for apisix_key in hello123 HelloApisix; do
resp=$(seq 10 | xargs -I{} curl "http://127.0.0.1:9080/headers?name=jack" -H 'user-id: 30' -H "apisix-key: ${apisix_key}" -sL)
count_httpbin=$(echo "$resp" | grep "httpbin.org" | wc -l)
count_mockapi7=$(echo "$resp" | grep "mock.api7.ai" | wc -l)
echo "${apisix_key}: httpbin.org: ${count_httpbin}, mock.api7.ai: ${count_mockapi7}"
done
Each batch should use the route's default upstream because the anchored regex requires the entire header value to contain only lowercase English letters:
hello123: httpbin.org: 0, mock.api7.ai: 10
HelloApisix: httpbin.org: 0, mock.api7.ai: 10
A common use case of the traffic-split plugin is to implement release strategies, such as canary release and blue-green deployment. For more plugin usage, see the plugin doc.
Label Traffic by Headers Conditionally and Proportionally
In this section, you will learn how to add headers to requests conditionally and by weight using the traffic-label plugin.
Create a route as such:
- Admin API
- ADC
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "traffic-label-route",
"uri":"/headers",
"priority": 10,
"plugins":{
"traffic-label": {
"rules": [
{
"match": [
["uri", "==", "/headers"]
],
"actions": [
{
"set_headers": {
"X-Server-Id": 100
},
"weight": 3
},
{
"set_headers": {
"X-API-Version": "v2"
},
"weight": 2
},
{
"weight": 5
}
]
}
]
}
},
"upstream":{
"type":"roundrobin",
"nodes":{
"httpbin.org:80":1
}
}
}'
❶ 30% of the requests should have the X-Server-Id: 100 request header.
❷ 20% of the requests should have the X-API-Version: v2 request header.
❸ 50% of the requests should not have any action performed on them.
Give this example its own service name. ADC matches a service by name, so reusing httpbin Service updates that existing service even when the label is different. If you previously synchronized this example, delete the older traffic-label-route route on httpbin Service after synchronizing, and leave every other route on that service in place.
services:
- name: Traffic Label Service
labels:
docs-example: conditional-traffic-label
routes:
- uris:
- /headers
name: traffic-label-route
priority: 10
plugins:
traffic-label:
rules:
- match:
- [uri, "==", "/headers"]
actions:
- set_headers:
X-Server-Id: 100
weight: 3
- set_headers:
X-API-Version: v2
weight: 2
- weight: 5
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
❶ 30% of the requests should have the X-Server-Id: 100 request header.
❷ 20% of the requests should have the X-API-Version: v2 request header.
❸ 50% of the requests should not have any action performed on them.
Preview only this example's labeled service and confirm that the diff contains no unintended updates or deletions:
adc diff -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=conditional-traffic-label
Synchronize the reviewed service configuration:
adc sync -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=conditional-traffic-label
This route uses priority 10 so APISIX selects it ahead of the earlier traffic-split-route when both routes match /headers.
❶ 30% of the requests should have the X-Server-Id: 100 request header.
❷ 20% of the requests should have the X-API-Version: v2 request header.
❸ 50% of the requests should not have any action performed on them.
Generate 50 consecutive requests to verify the weighted actions:
resp=$(seq 50 | xargs -I{} curl "http://127.0.0.1:9080/headers" -sL) && \
count_w3=$(echo "$resp" | grep "X-Server-Id" | wc -l) && \
count_w2=$(echo "$resp" | grep "X-API-Version" | wc -l) && \
echo X-Server-Id: $count_w3, X-API-Version: $count_w2
The response shows that headers are added to requests in a weighted manner:
X-Server-Id: 15, X-API-Version: 10
For more plugin usage, see the plugin doc.
Rate Limit Traffic Conditionally
In this section, you will learn how to implement rate limiting by count conditionally, using the workflow plugin.
Create a route as such:
- Admin API
- ADC
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "workflow-route",
"uri": "/anything/*",
"plugins":{
"workflow":{
"rules":[
{
"case":[
["uri", "==", "/anything/rate-limit"],
["arg_env", "==", "v1"]
],
"actions":[
[
"limit-count",
{
"count":1,
"time_window":60,
"rejected_code":429
}
]
]
}
]
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org": 1
}
}
}'
❶ Execute the plugin to rate limit only when the request's URI path is /anything/rate-limit and it contains a URL parameter env=v1.
❷ Apply rate limiting when the rule is matched.
Give this example its own service name. ADC matches a service by name, so reusing httpbin Service updates that existing service even when the label is different. If you previously synchronized this example, delete the older workflow-route route on httpbin Service after synchronizing, and leave every other route on that service in place.
services:
- name: Conditional Rate Limit Service
labels:
docs-example: conditional-rate-limit
routes:
- uris:
- /anything/*
name: workflow-route
plugins:
workflow:
rules:
- case:
- [uri, "==", "/anything/rate-limit"]
- [arg_env, "==", "v1"]
actions:
- - limit-count
- count: 1
time_window: 60
rejected_code: 429
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
❶ Execute the plugin to rate limit only when the request's URI path is /anything/rate-limit and it contains a URL parameter env=v1.
❷ Apply rate limiting when the rule is matched.
Preview only this example's labeled service and confirm that the diff contains no unintended updates or deletions:
adc diff -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=conditional-rate-limit
Synchronize the reviewed service configuration:
adc sync -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=conditional-rate-limit
Generate two consecutive requests that match the condition:
curl -i "http://127.0.0.1:9080/anything/rate-limit?env=v1"
You should receive an HTTP/1.1 200 OK response and an HTTP 429 Too Many Requests response.
Generate requests that do not match the condition:
curl -i "http://127.0.0.1:9080/anything/anything?env=v1"
You should receive HTTP/1.1 200 OK responses for all requests, as they are not rate limited.
For more plugin usage, see the plugin doc.
Next Steps
You have now learned the various ways APISIX supports conditional traffic management, using the traffic-split, traffic-label, and workflow plugins.
To meet more complex conditional traffic management requirements, see how you can create a custom plugin, or use serverless function plugins to execute custom code at runtime.