Weight断言仅支持静态整数权重,无法感知Nacos实例元数据、动态上下文或透传逻辑,易致路由不一致、热更新失效等问题。

为什么不能只用 Weight 断言做业务权重灰度
Spring Cloud Gateway 内置的 Weight 断言(如 Weight=groupA, 80)只支持静态分组与固定整数权重,无法感知 Nacos 中服务实例的实时元数据(比如 version=v2.1、weight=35)、不支持按请求上下文动态调整(如用户等级越高,灰度流量权重越大),更无法联动下游 Feign 调用时的透传逻辑。硬套它来做“业务权重”,上线后容易出现:路由结果不一致、权重配置热更新失效、新旧实例混选等问题。
自定义 GlobalFilter 中如何读取并解析 Nacos 实例权重
关键不是“写过滤器”,而是“在过滤器里拿到带权重的实例列表”。Nacos 2.2+ 的 NacosServiceDiscovery 支持通过 getInstances(String serviceId) 拉取全部实例,并从每个 ServiceInstance 的 getMetadata() 中提取自定义字段:
- 确保 Nacos 控制台或服务启动时,为灰度实例正确设置元数据,例如:
gray-weight=70、version=v2.1、env=gray - 在过滤器中调用
discovery.getInstances("user-service"),遍历结果,过滤出metadata.containsKey("gray-weight")的实例 - 把
metadata.get("gray-weight")解析为int,注意空值和非数字异常处理——这里漏掉 try-catch 是线上 500 的常见原因 - 不要缓存整个实例列表到静态变量;应结合
@RefreshScope或监听 Nacos 配置变更事件来触发本地实例缓存刷新
权重路由的核心算法:区间随机法比轮询更可控
直接用 ThreadLocalRandom.current().nextInt(100) 生成 0–99 的随机数,再按累积权重区间匹配,比简单加权轮询(Weighted Round Robin)更能反映真实流量比例,也避免因实例上下线导致的权重漂移:
以 CLI 为先的表单构建器,采用 BYOK 理念。通过JSON Schema 生成美观的 HTML 表单,使用您自己的 SMTP 服务器处理提交,存储响应...
int totalWeight = instances.stream().mapToInt(i -> Integer.parseInt(i.getMetadata().get("gray-weight"))).sum();
int rand = ThreadLocalRandom.current().nextInt(totalWeight);
int cursor = 0;
for (ServiceInstance instance : instances) {
int weight = Integer.parseInt(instance.getMetadata().get("gray-weight"));
if (rand
- 务必校验
totalWeight > 0,否则nextInt(0)会抛IllegalArgumentException - 如果所有实例都没配
gray-weight,应 fallback 到默认负载均衡器(如RoundRobinLoadBalancer),而不是直接报错 - 这个逻辑必须放在
filter()方法内执行,不能提前“预选”并缓存实例——因为 Gateway 是响应式流,每个请求上下文独立
怎么把选中的灰度实例透传给后续 Filter 和下游服务
Gateway 默认的 NettyRoutingFilter 依赖 ServerWebExchange 中的 uri 字段做转发,而你选的是 ServiceInstance。必须手动构造目标 URI 并替换:
- 用
UriComponentsBuilder.fromHttpUrl("http://" + instance.getHost() + ":" + instance.getPort())构建原始地址 - 调用
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, uri)替换请求 URL - 若需下游服务识别灰度来源,记得用
AddRequestHeader过滤器追加标识,例如:X-Gray-Source: gateway-weight - 别忘了清除
lb://协议前缀带来的自动负载均衡干扰——在路由配置中显式禁用:设置spring.cloud.gateway.httpclient.pool.max-idle-time=-1可能引发连接复用混乱,实际应靠 URI 替换来绕过 LB
真正难的不是代码写几行,而是当 Nacos 实例元数据没更新、权重字段拼错成 gery-weight、或者某个灰度实例健康检查失败却仍被计入权重总和时,日志里看不到任何提示——建议在过滤器开头加一条 trace 级日志:log.trace("Gray weight routing: service={}, candidates={}, totalWeight={}", serviceId, candidates.size(), totalWeight);










