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.
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
- Ingress Controller
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
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.
Synchronize the configuration to APISIX:
adc sync -f adc.yaml
Create a Kubernetes manifest file for a route where traffic-split
is enabled:
- Gateway API
- APISIX CRD
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: ingress-apisix
name: traffic-split-plugin-config
spec:
plugins:
- name: traffic-split
config:
rules:
- match:
- vars:
- ["arg_name", "==", "jack"]
- ["http_user-id", ">", "23"]
- ["http_apisix-key", "~~", "[a-z]+"]
weighted_upstreams:
- upstream:
scheme: https
pass_host: node
nodes:
httpbin.org: 1
weight: 3
- weight: 2
---
apiVersion: v1
kind: Service
metadata:
namespace: ingress-apisix
name: mockapi7-external-domain
spec:
type: ExternalName
externalName: mock.api7.ai
---
apiVersion: apisix.apache.org/v1alpha1
kind: BackendTrafficPolicy
metadata:
namespace: ingress-apisix
name: passhost-node
spec:
targetRefs:
- name: mockapi7-external-domain
kind: Service
group: ""
passHost: node
scheme: https
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: ingress-apisix
name: traffic-split-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /headers
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: traffic-split-plugin-config
backendRefs:
- name: mockapi7-external-domain
port: 443
weight: 1
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: ingress-apisix
name: mockapi7-external-domain
spec:
scheme: https
passHost: node
externalNodes:
- type: Domain
name: mock.api7.ai
weight: 1
port: 443
---
apiVersion: apisix.apache.org/v2
kind: ApisixPluginConfig
metadata:
namespace: ingress-apisix
name: traffic-split-plugin-config
spec:
ingressClassName: apisix
plugins:
- name: traffic-split
enable: true
config:
rules:
- match:
- vars:
- ["arg_name", "==", "jack"]
- ["http_user-id", ">", "23"]
- ["http_apisix-key", "~~", "[a-z]+"]
weighted_upstreams:
- upstream:
scheme: https
pass_host: node
nodes:
httpbin.org:443: 1
weight: 3
- weight: 2
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: ingress-apisix
name: traffic-label-route
spec:
ingressClassName: apisix
http:
- name: traffic-label-route
match:
paths:
- /headers
plugin_config_name: traffic-split-plugin-config
upstreams:
- name: mockapi7-external-domain
❶ 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.
Apply the configuration to your cluster:
kubectl apply -f traffic-split.yaml
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 10 consecutive requests that do not satisfy the conditions to verify:
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
You should see a response similar to the following:
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 weights, using the enterprise traffic-label
plugin.
Create a route as such:
- Admin API
- ADC
- Ingress Controller
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",
"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.
services:
- name: httpbin Service
routes:
- uris:
- /headers
name: traffic-label-route
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.
Synchronize the configuration to APISIX:
adc sync -f adc.yaml
Create a Kubernetes manifest file for a route where traffic-label
is enabled:
- Gateway API
- APISIX CRD
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: ingress-apisix
name: traffic-label-plugin-config
spec:
plugins:
- name: traffic-label
config:
rules:
- match:
- [uri, "==", "/headers"]
actions:
- set_headers:
X-Server-Id: 100
weight: 3
- set_headers:
X-API-Version: v2
weight: 2
- weight: 5
---
apiVersion: v1
kind: Service
metadata:
namespace: ingress-apisix
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: ingress-apisix
name: traffic-label-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /headers
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: traffic-label-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
weight: 1
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: ingress-apisix
name: httpbin-external-domain
spec:
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixPluginConfig
metadata:
namespace: ingress-apisix
name: traffic-label-plugin-config
spec:
ingressClassName: apisix
plugins:
- name: traffic-label
enable: true
config:
rules:
- match:
- [uri, "==", "/headers"]
actions:
- set_headers:
X-Server-Id: 100
weight: 3
- set_headers:
X-API-Version: v2
weight: 2
- weight: 5
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: ingress-apisix
name: traffic-label-route
spec:
ingressClassName: apisix
http:
- name: traffic-label-route
match:
paths:
- /headers
plugin_config_name: traffic-label-plugin-config
upstreams:
- name: httpbin-external-domain
❶ 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.
Apply the configuration to your cluster:
kubectl apply -f traffic-label.yaml
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
- Ingress Controller
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.
services:
- name: httpbin Service
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.
Synchronize the configuration to APISIX:
adc sync -f adc.yaml
Create a Kubernetes manifest file for a route where workflow
is enabled:
- Gateway API
- APISIX CRD
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: ingress-apisix
name: workflow-plugin-config
spec:
plugins:
- name: workflow
config:
rules:
- case:
- [uri, "==", "/anything/rate-limit"]
- [arg_env, "==", "v1"]
actions:
- - limit-count
- count: 1
time_window: 60
rejected_code: 429
---
apiVersion: v1
kind: Service
metadata:
namespace: ingress-apisix
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: ingress-apisix
name: workflow-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: PathPrefix
value: /anything/*
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: workflow-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
weight: 1
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: ingress-apisix
name: httpbin-external-domain
spec:
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixPluginConfig
metadata:
namespace: ingress-apisix
name: workflow-plugin-config
spec:
ingressClassName: apisix
plugins:
- name: workflow
enable: true
config:
rules:
- case:
- [uri, "==", "/anything/rate-limit"]
- [arg_env, "==", "v1"]
actions:
- - limit-count
- count: 1
time_window: 60
rejected_code: 429
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: ingress-apisix
name: workflow-route
spec:
ingressClassName: apisix
http:
- name: workflow-route
match:
paths:
- /anything/*
plugin_config_name: workflow-plugin-config
upstreams:
- name: httpbin-external-domain
❶ 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.
Apply the configuration to your cluster:
kubectl apply -f workflow.yaml
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.