【灰度发布(二)】服务端灰度流量API网关业务实现

一.灰度标签元数据定义

1.上行流量(Nginx携带)的灰度标记Header定义

  X-Tags: gray-V1.0

2.以业务服务为例,灰度标签元数据定义如下

  灰度标签Key:grayTag

  特殊灰度调用标签:grayPriorityTag(特殊标志,作用后续会有说明)

spring:
  cloud:
    nacos:
      discovery:
        metadata:
            # 灰度标签   标记灰度示例
            grayTag: "gray-V1.0"
            # 灰度调用优先级 特殊服务需要优先调用灰度服务
            grayPriorityTag: "1"

3. 核心逻辑:识别到携带灰度标记(X-Tags)的灰度请求后,与下游的目标服务进行路由规则匹配

 

二.API Gateway网关灰度过滤器

@Slf4j
@Component
public class GatewayLoadBalancerClientFilter extends LoadBalancerClientFilter {

    public GatewayLoadBalancerClientFilter(LoadBalancerClient loadBalancer, LoadBalancerProperties properties) {
        super(loadBalancer, properties);
    }

    @Override
    protected ServiceInstance choose(ServerWebExchange exchange) {
        URI uri = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
        String routeId = exchange.getAttribute(GatewayConstant.ROUTE_ID);
        if (this.loadBalancer instanceof RibbonLoadBalancerClient) {
            String serviceId = ((URI) exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR)).getHost();
            HttpHeaders headers = exchange.getRequest().getHeaders();
            String tags = headers.getFirst(GatewayConstant.X_TAGS);
            if(StringUtils.hasText(tags)) {
                RibbonLoadBalancerClient client = (RibbonLoadBalancerClient) this.loadBalancer;
                exchange.getAttributes().put(GatewayConstant.X_TAGS, tags);
                log.info("执行灰度路由 uri:{} routeId:{} serviceId:{},tags:{}",uri,routeId, serviceId, tags);
                return client.choose(serviceId,tags);
            }
        }
        if(log.isDebugEnabled()) {
            log.debug("执行正常路由...:{} routeId:{}", uri,routeId);
        }
        return super.choose(exchange);
    }
}

 

三.网关Ribbon灰度路由规则重写

@Slf4j
public class GateWayLoadBalancerRule extends AbstractLoadBalancerRule {

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {

    }

    @Override
    public Server choose(Object key) {
        if (log.isDebugEnabled()) {
            log.debug("开始执行网关负责均衡规则:{}", key);
        }
        try {
            DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer();
            List<Instance> instances = loadBalancer.getReachableServers().stream().map(server -> ((NacosServer) server).getInstance())
                    .collect(Collectors.toList());
            if (CollectionUtils.isEmpty(instances)) {
                log.warn("服务实例不存在:{}", loadBalancer.getName());
                return null;
            }
            if (ObjectUtils.isEmpty(key) || "default".equals(key.toString())) {
                Instance instance = ExtendBalancer.getHostByRandomWeight2(instances);
                if (log.isDebugEnabled()) {
                    log.debug("执行正常流量实例调度信息:{}", JSON.toJSONString(instance));
                }
                return new NacosServer(instance);
            } else {
                Instance instance = chooseGrayInstance(instances, key.toString());
                return new NacosServer(instance);
            }
        } catch (Exception e) {
            log.warn("获取目标节点异常:{}", e.getMessage(), e);
            return null;
        }
    }

    private Instance chooseGrayInstance(List<Instance> instances, String grayTag) {
        List<Instance> grayInstances = instances.stream()
                .filter(instance -> grayTag.equals(instance.getMetadata().get("grayTag")))
                .collect(Collectors.toList());
        if (!grayInstances.isEmpty()) {
            Instance instance = ExtendBalancer.getHostByRandomWeight2(grayInstances);
            if (log.isDebugEnabled()) {
                log.debug("执行灰度负载均衡调用,目标实例信息:{}", instance.getInstanceId());
            }
            return instance;
        } else {
            Instance instance = ExtendBalancer.getHostByRandomWeight2(instances);
            if (log.isDebugEnabled()) {
                log.info("灰度负载均衡调用,没有灰度实例 默认调度正常示例信息:{}", instance.getInstanceId());
            }
            return instance;
        }
    }

}

 

posted @ 2025-08-22 20:26  听风是雨  阅读(18)  评论(0)    收藏  举报
/* 看板娘 */