Master Traffic Coloring & Gray Release with Spring Cloud Gateway
This article explains how to use Spring Cloud Gateway for traffic coloring and gray release, enabling you to tag requests, route VIP or test traffic to new service versions gradually, and ensure safe, controlled rollouts in production environments.
What is traffic coloring? Why do we need it?
Traffic coloring means adding an identity tag to a request so that all services in the call chain can recognize it. For example, when launching a new feature for a VIP user, the gateway adds X-Traffic-Tag: vip to the request header, allowing downstream services such as order, payment, and logging to handle the request differently.
Traffic coloring breaks the limitation of treating all traffic the same; with tags, gray release, A/B testing, and environment isolation become possible.
What is gray release?
Based on traffic‑coloring tags, gray release lets a portion of traffic run the new version while the rest stays on the old version, verifying stability step by step.
Pre‑release: only internal test accounts (tag test) use the new version.
Early stage: 5% of VIP users (tag vip) use the new version.
Mid stage: increase to 30‑50% of VIP users.
Full release: after confirming stability, all users switch to the new version.
If a problem appears (e.g., VIP users encounter order failures), the gray rule can be turned off, rolling all traffic back to the old version with minimal impact.
By proportion: route 10% of traffic based on user‑ID modulo.
By scenario: only the "new user registration" API goes to the new version.
By device: iOS users first, Android later.
Implement traffic coloring + gray release
The process consists of three steps: request coloring → gray routing → effect verification.
Project dependencies
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- Service discovery if routing to a registry -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>Step 1: Implement traffic coloring
Use a GlobalFilter with order -1 to ensure it runs before authentication or rate‑limiting filters. The filter extracts the user type from request parameters or cookies and injects the appropriate X-Traffic-Tag header.
@Configuration
public class TrafficDyeFilterConfig {
// Order(-1) ensures the filter runs first
@Bean
@Order(-1)
public GlobalFilter trafficDyeFilter() {
return (exchange, chain) -> {
// 1. Get user type from request
String userType = getUserTypeFromRequest(exchange);
// 2. Determine traffic tag
String trafficTag = getTrafficTagByUserType(userType);
// 3. Inject tag into request header
exchange.getRequest().mutate()
.header("X-Traffic-Tag", trafficTag)
.build();
// 4. Continue filter chain
return chain.filter(exchange);
};
}
private String getUserTypeFromRequest(ServerWebExchange exchange) {
List<String> userTypeParams = exchange.getRequest().getQueryParams().get("userType");
if (userTypeParams != null && !userTypeParams.isEmpty()) {
return userTypeParams.get(0);
}
return "normal";
}
private String getTrafficTagByUserType(String userType) {
switch (userType) {
case "vip":
return "vip";
case "test":
return "test";
default:
return "normal";
}
}
}Key notes : Order(-1) is crucial so the tag is available to downstream filters.
The tag is placed in the request header X-Traffic-Tag, which downstream services can read via request.getHeader("X-Traffic-Tag").
To add more complex rules (e.g., user‑ID modulo), extend getUserTypeFromRequest.
Step 2: Implement gray routing
Create a custom RoutePredicateFactory that checks the X-Traffic-Tag header against an allowed list (e.g., ["vip", "test"]). If the tag matches, the request is routed to the new service version.
@Configuration
public class GrayRoutePredicateFactory extends AbstractRoutePredicateFactory<GrayRoutePredicateFactory.Config> {
private static final String TRAFFIC_TAG_HEADER = "X-Traffic-Tag";
public GrayRoutePredicateFactory() {
super(Config.class);
}
public static class Config {
@NotEmpty
private List<String> allowTags;
public List<String> getAllowTags() { return allowTags; }
public void setAllowTags(List<String> allowTags) { this.allowTags = allowTags; }
}
@Override
public List<String> shortcutFieldOrder() {
return Collections.singletonList("allowTags");
}
@Override
public GatewayPredicate apply(Config config) {
return exchange -> {
List<String> trafficTags = exchange.getRequest().getHeaders().get(TRAFFIC_TAG_HEADER);
if (trafficTags == null || trafficTags.isEmpty()) {
return false;
}
String trafficTag = trafficTags.get(0);
return config.getAllowTags().contains(trafficTag);
};
}
@Override
public String toString() {
return "GrayRoutePredicate{allowTags=" + config.getAllowTags() + "}";
}
}Configure gateway routes
spring:
cloud:
gateway:
routes:
# Gray route for VIP/test tags → new version
- id: gray_route_v2
uri: lb://order-service-v2
predicates:
- name: GrayRoute
args:
allowTags[0]: vip
allowTags[1]: test
- Path=/api/order/**
filters:
- RewritePath=/api/(?<segment>.*),/${segment}
# Normal route → old version
- id: normal_route_v1
uri: lb://order-service-v1
predicates:
- Path=/api/order/**
filters:
- RewritePath=/api/(?<segment>.*),/${segment}Key notes :
Use lb:// to enable load‑balancing via the service registry.
Place the gray route before the normal route so it matches first.
For percentage‑based gray releases, add modulo logic inside the predicate factory.
Step 3: Verify the effect
After deploying the code and configuration, send a request such as http://gateway-ip:port/api/order/create?userType=vip. The request should be forwarded to order-service-v2. Use tools like Postman to confirm routing.
Production considerations
1. Tag propagation
Ensure X-Traffic-Tag is passed through the entire call chain (e.g., gateway → order service → inventory service). When using OpenFeign, add a request interceptor to forward the header; with Dubbo, implement a similar filter.
@Component
public class FeignTrafficTagInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
String trafficTag = TrafficTagContextHolder.get();
if (trafficTag != null) {
template.header("X-Traffic-Tag", trafficTag);
}
}
}2. Dynamic gray rule adjustment
Store gray rules (allowed tags, percentages) in Nacos configuration. Let the gateway listen for changes and update the predicate factory without restarting.
3. Fast rollback
Add a gray switch in Nacos (e.g., gray.switch=false). The custom predicate should first check this switch; if disabled, all traffic routes to the old version.
Final thoughts
The gateway is more than a simple forwarder; it is the traffic control center. By using traffic coloring and gray release, you can safely roll out new features, limit risk, and demonstrate mastery of Spring Cloud Gateway in interviews.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
