Spring Cloud Gateway灰度发布

Spring Cloud Gateway灰度发布


一、灰度发布场景实现方案

1. 基于Header的灰度路由
@Configuration
public class GrayReleaseConfig {
    @Bean
    public RouteLocator grayRoute(RouteLocatorBuilder builder) {
        return builder.routes()
            // 灰度路由(带特定header的请求路由到v2版本)
            .route("gray_v2", r -> r.header("X-Gray-Release", "true")
                .and().path("/product/**")
                .filters(f -> f.rewritePath("/product/(?<segment>.*)", "/v2/product/${segment}"))
                .uri("lb://product-service")
                .metadata("version", "v2"))
            
            // 默认路由(其他请求走v1版本)
            .route("default_v1", r -> r.path("/product/**")
                .filters(f -> f.rewritePath("/product/(?<segment>.*)", "/v1/product/${segment}"))
                .uri("lb://product-service"))
            .build();
    }
}
2. 基于权重的灰度分流
@Configuration
public class WeightedRouteConfig {
    @Bean
    public RouteLocator weightedRoutes(RouteLocatorBuilder builder) {
        return builder.routes()
            // v1版本接收80%流量
            .route("weight_v1", r -> r.weight("product-service", 80)
                .and().path("/order/**")
                .uri("lb://product-service/v1"))
            
            // v2版本接收20%流量
            .route("weight_v2", r -> r.weight("product-service", 20)
                .and().path("/order/**")
                .uri("lb://product-service/v2"))
            .build();
    }
}
3. 基于用户ID的灰度分组
@Component
@Order(-1)
public class GrayUserFilter implements GlobalFilter {
    private final Set<String> grayUsers = Set.of("1001", "1002", "1005");

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String userId = exchange.getRequest().getHeaders().getFirst("X-User-ID");
        if (grayUsers.contains(userId)) {
            exchange.getAttributes().put("GRAY_TAG", true);
        }
        return chain.filter(exchange);
    }
}

// 在路由配置中使用自定义断言
@Bean
public RouteLocator userGrayRoute(RouteLocatorBuilder builder) {
    return builder.routes()
        .route(r -> r.asyncPredicate(exchange -> 
                Mono.just(exchange.getAttribute("GRAY_TAG") == Boolean.TRUE)
            )
            .uri("lb://product-service/v2"))
        .build();
}

二、请求链路追踪场景实现方案

1. 集成Sleuth+Zipkin
# application.yml
spring:
  sleuth:
    sampler:
      probability: 1.0
  zipkin:
    base-url: http://zipkin-server:9411
    sender:
      type: web
2. 自定义TraceID透传过滤器
@Component
public class TraceFilter implements GlobalFilter {
    private static final String TRACE_HEADER = "X-Trace-ID";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String traceId = exchange.getRequest().getHeaders().getFirst(TRACE_HEADER);
        if (traceId == null) {
            traceId = UUID.randomUUID().toString();
        }
        
        // 向下游传递
        ServerHttpRequest request = exchange.getRequest().mutate()
                .header(TRACE_HEADER, traceId)
                .build();
                
        // 记录到日志上下文
        MDC.put("traceId", traceId);
        
        return chain.filter(exchange.mutate().request(request).build())
                .doFinally(signalType -> MDC.remove("traceId"));
    }
}
3. 请求日志增强配置
@Configuration
public class LoggingConfig {
    @Bean
    public GlobalFilter loggingFilter() {
        return (exchange, chain) -> {
            long startTime = System.currentTimeMillis();
            return chain.filter(exchange).doFinally(signal -> {
                String path = exchange.getRequest().getPath().toString();
                int status = exchange.getResponse().getStatusCode() != null ? 
                    exchange.getResponse().getStatusCode().value() : 500;
                log.info("[{}] {} {} {}ms", 
                    exchange.getAttribute("traceId"),
                    exchange.getRequest().getMethod(), 
                    path,
                    System.currentTimeMillis() - startTime);
            });
        };
    }
}
4. 全链路监控看板(需配合Prometheus+Grafana)
@Configuration
public class MetricsConfig {
    @Bean
    public GatewayMetricsFilter metricsFilter(MeterRegistry registry) {
        return new GatewayMetricsFilter(registry);
    }

    @Bean
    public RouteLocator monitoredRoutes(RouteLocatorBuilder builder) {
        return builder.routes()
            .route("monitored_route", r -> r.path("/api/**")
                .filters(f -> f.prefixPath("/monitored")
                    .filter(metricsFilter()))
                .uri("lb://monitored-service"))
            .build();
    }
}

三、关键实现要点说明

  1. 灰度发布核心逻辑

    • 使用Weight谓词时需确保所有分组的weight总和为100
    • 灰度标识建议采用请求头而非Cookie(避免浏览器缓存影响)
  2. 链路追踪最佳实践

    // 在logback-spring.xml中配置traceId显示
    <pattern>%d{yyyy-MM-dd HH:mm:ss} [%X{traceId}] %-5level %logger{36} - %msg%n</pattern>
    
  3. 性能优化建议

    // 异步线程上下文传递
    @Bean
    public ReactorHook traceContextHook() {
        return new ReactorHook() {
            @Override
            public Context onOperator(Context ctx) {
                return ctx.put("traceId", MDC.get("traceId"));
            }
        };
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值