灰度发布:网关权重路由与版本标识思路
灰度发布(金丝雀发布)指让新版本先承接一小部分流量,验证稳定后再逐步扩大比例,最后全量切换。相比一次性发布,灰度能把故障影响限制在可控范围内,配合监控随时回滚。落地通常需要三样东西:流量入口(网关/负载均衡)、实例分组与版本标识、可观测与回滚手段。
思路一:网关权重路由
Spring Cloud Gateway 提供 Weight 路由谓词(WeightRoutePredicateFactory):给多条路由打同一个分组名并分配权重,网关按权重比例转发。
spring:
cloud:
gateway:
routes:
- id: order-v1
uri: lb://order-service
predicates:
- Path=/order/**
- Weight=gray, 90 # 分组 gray,90% 流量
- id: order-v2
uri: lb://order-v2-service # 新版本独立部署的服务
predicates:
- Path=/order/**
- Weight=gray, 10 # 10% 流量先验证新版本
调整权重即可平滑扩缩灰度比例;一旦异常,把 v2 权重改回 0 即可快速回滚。
思路二:版本标识(metadata)路由
把“灰度”语义下沉到注册中心:同属一个服务名,但不同实例在 Nacos 的元数据(metadata)里打上版本号,例如 version=1.0(稳定)、version=gray(灰度)。
- 在 Nacos 控制台给指定实例添加元数据,或通过注册配置(spring.cloud.nacos.discovery.metadata.version=gray)打标。
- 网关或调用方拿到请求头(如
x-version: gray)后,只选择 metadata 匹配的实例转发。
配合 Spring Cloud LoadBalancer,可自定义 ServiceInstanceListSupplier 实现“按版本过滤再负载均衡”,官方也提供基于 hint 的选择机制(spring.cloud.loadbalancer.hint 与请求头配合),具体实现以官方文档为准。
// 简化示意:自定义实例列表供应商,按 metadata.version 过滤
public class VersionInstanceListSupplier extends DelegatingServiceInstanceListSupplier {
@Override
public Flux<List<ServiceInstance>> get() {
String expect = RequestContext.get("x-version"); // 从上下文取版本要求
return delegate.get()
.map(list -> list.stream()
.filter(i -> expect.equals(i.getMetadata().get("version")))
.collect(Collectors.toList()));
}
}
分流维度选择
| 维度 | 适用场景 | 说明 |
|---|---|---|
| 随机百分比 | 新功能验证、容量试跑 | 实现最简单,无法指定特定用户 |
| 按用户/租户 | 大客户试点、按账号白名单 | 用 userId 取模或名单判断,体验稳定 |
| 按请求头/参数 | 联调、内部预览 | 测试同学带灰度头即可进灰度链路 |
原则:用户维度的灰度尽量“粘滞”,同一用户始终命中同一版本,避免会话内行为在 v1/v2 间抖动。
配套要求
灰度发布不是“路由切一刀”就结束。配套条件包括:灰度期间全链路日志可检索(携带版本号标签)、指标按版本对比、配置独立(灰度实例读取灰度配置)、以及自动化回滚预案。版本标识建议统一命名规范(如 version=canary-20250601),避免多套灰度相互覆盖。
小结
网关权重路由适合“服务级新老并存”,版本标识路由适合“同服务实例分组路由”。生产上两者常结合:先用 metadata+请求头做定向灰度,验证通过后再用网关权重逐步放量,全量后下线旧实例。