从 0 到 1 搭建高可用服务治理体系:路由、灰度、流量调度架构全解析

38 阅读30分钟

微服务架构的普及,让系统拆分更灵活、迭代效率更高,但也带来了新的挑战:分布式环境下服务实例数量激增、版本迭代风险不可控、流量波动对系统稳定性的冲击、多环境与多集群的流量管理复杂度指数级上升。服务治理正是解决这些问题的核心抓手,而路由、灰度、流量调度,是服务治理体系中最核心的三大能力,三者相辅相成,构成了对流量全生命周期的精准管控。

很多开发者在落地服务治理时,往往会陷入三个误区:一是将三大能力割裂看待,只做单点功能而无法形成闭环;二是只关注API层面的实现,忽略了底层架构的可扩展性与高可用性;三是盲目照搬开源组件,没有结合业务场景做适配,最终导致功能无法落地,甚至引入线上故障。

本文将从底层逻辑出发,拆解路由、灰度、流量调度的核心原理、架构设计与落地实现,帮助开发者建立完整的服务治理知识体系,同时提供可直接复用的架构方案与代码实现,解决生产环境中的实际问题。

一、服务治理的底层逻辑:三大核心能力的本质与协同关系

服务治理的核心,是对分布式系统中流量的全生命周期管控,从请求进入系统的那一刻起,到请求完成响应的全链路,都需要被精准、可控、可追溯的管理。

三大核心能力的本质

  1. 服务路由:流量的「精准导航系统」 本质是基于预设规则,将请求确定性地转发到符合条件的目标服务实例,解决的核心问题是「流量该往哪里去」。它是整个服务治理体系的基础,所有的流量管控能力,最终都要通过路由来落地。
  2. 灰度发布:风险可控的「版本试水平台」 本质是基于流量特征或比例,将部分流量渐进式地引导到新版本服务实例,通过小流量验证新版本的正确性与稳定性,逐步扩大流量范围,最终完成全量发布。它解决的核心问题是「如何把版本迭代的爆炸半径降到最低」,是保障业务连续性的核心手段。
  3. 流量调度:系统稳定性的「智能指挥中枢」 本质是基于系统实时状态、业务优先级与容灾策略,对流量进行动态的、自适应的分配与调度,解决的核心问题是「如何让流量分配最优,保障系统稳定性与资源利用率的平衡」。它是整个服务治理体系的动态调节能力,应对流量波动、系统故障、资源不均等突发场景。

三大能力的协同关系

路由是基础能力,为灰度和流量调度提供了流量转发的执行层;灰度是路由的核心业务场景,通过路由规则实现灰度流量的精准转发;流量调度是路由规则的动态生成与调整中枢,基于实时数据更新路由策略,实现自适应的流量管控。三者形成了「规则定义-规则执行-动态调优」的完整闭环,构成了服务治理体系的核心骨架。

二、服务路由:流量的精准导航系统,架构设计与落地实现

服务路由的核心价值,在于解决分布式系统中服务实例多集群、多机房、多版本、多环境的流量精准转发问题,是环境隔离、容灾切换、灰度发布等能力的底层基础。

服务路由的分层架构

在完整的微服务体系中,路由能力分为三层,每层有明确的职责边界与适用场景,三者协同实现全链路的路由管控。

  1. 接入层路由 部署在整个系统的最前端,一般为LVS、Nginx等四层/七层负载均衡设备,核心职责是机房级/集群级的流量转发,比如基于域名、路径、客户端IP的路由,将流量转发到对应的网关集群或业务集群。它的特点是性能极高,规则简单,适合做粗粒度的流量管控。
  2. 网关层路由 部署在接入层之后、业务服务之前,一般为Spring Cloud Gateway、APISIX、Kong等API网关,核心职责是API粒度、业务特征粒度的路由转发,比如基于请求参数、请求头、Cookie、用户特征的路由,同时集成了鉴权、限流、日志、协议转换等能力。它的特点是规则灵活,可扩展性强,适合做业务级的细粒度路由管控,也是灰度发布最常用的一层。
  3. 服务框架层(RPC层)路由 部署在微服务之间的RPC调用链路中,一般为Dubbo、Spring Cloud OpenFeign等RPC框架内置的路由能力,核心职责是服务间调用的全链路路由转发,比如基于服务元数据、调用方特征、透传的流量标记的路由,保障全链路的流量隔离。它的特点是和业务服务深度集成,能实现端到端的精准路由,是全链路灰度的核心基础。

服务路由的核心设计原则

  • 规则与执行分离:路由规则的配置、存储、推送,和路由规则的执行逻辑完全解耦,支持规则动态更新,不影响执行层性能。
  • 责任链模式:多个路由规则按照优先级组成路由链,依次执行,匹配失败则进入下一个规则,最终得到符合条件的实例列表,方便扩展新的路由规则。
  • 失败安全:当路由规则匹配失败、配置错误、目标实例不可用时,必须有降级策略,比如回退到默认的负载均衡策略,不能导致请求失败。
  • 可观测性:所有路由规则的执行、匹配结果、转发行为,都必须有完整的日志、指标、链路追踪,方便问题排查与规则调优。

服务路由的核心实现原理与代码示例

路由的核心执行逻辑基于责任链模式实现,每个路由规则为独立的处理单元,按照优先级依次执行,对服务实例列表进行过滤,最终得到符合所有规则的实例列表。

1. 核心数据模型

MySQL路由规则表(兼容MySQL 8.0)

CREATE TABLE `route_rule` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `rule_id` varchar(64NOT NULL COMMENT '规则唯一标识',
  `rule_name` varchar(128NOT NULL COMMENT '规则名称',
  `rule_type` varchar(32NOT NULL COMMENT '规则类型:PARAM-参数路由,IDC-机房路由,WEIGHT-权重路由',
  `priority` int NOT NULL DEFAULT '0' COMMENT '规则优先级,数值越小优先级越高',
  `match_conditions` json NOT NULL COMMENT '匹配条件,JSON格式',
  `target_conditions` json NOT NULL COMMENT '目标实例条件,JSON格式',
  `enabled` tinyint NOT NULL DEFAULT '1' COMMENT '是否启用:0-禁用,1-启用',
  `effect_start_time` datetime DEFAULT NULL COMMENT '生效开始时间',
  `effect_end_time` datetime DEFAULT NULL COMMENT '生效结束时间',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除:0-未删除,1-已删除',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_rule_id` (`rule_id`),
  KEY `idx_rule_type` (`rule_type`),
  KEY `idx_enabled` (`enabled`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='路由规则表';

路由规则实体类(MyBatis Plus + Swagger3)

package com.jam.demo.route.entity;

import com.baomidou.mybatisplus.annotation.*;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.Map;

/**
 * 路由规则实体
 * @author ken
 */
@Data
@TableName("route_rule")
@Schema(description = "路由规则实体")
public class RouteRule {

    @TableId(type = IdType.AUTO)
    @Schema(description = "主键ID")
    private Long id;

    @Schema(description = "规则唯一标识", requiredMode = Schema.RequiredMode.REQUIRED)
    private String ruleId;

    @Schema(description = "规则名称", requiredMode = Schema.RequiredMode.REQUIRED)
    private String ruleName;

    @Schema(description = "规则类型", requiredMode = Schema.RequiredMode.REQUIRED)
    private String ruleType;

    @Schema(description = "规则优先级")
    private Integer priority;

    @Schema(description = "匹配条件,JSON格式")
    private Map<String, Object> matchConditions;

    @Schema(description = "目标实例条件,JSON格式")
    private Map<String, String> targetConditions;

    @Schema(description = "是否启用")
    private Boolean enabled;

    @Schema(description = "生效开始时间")
    private LocalDateTime effectStartTime;

    @Schema(description = "生效结束时间")
    private LocalDateTime effectEndTime;

    @TableField(fill = FieldFill.INSERT)
    @Schema(description = "创建时间")
    private LocalDateTime createTime;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    @Schema(description = "更新时间")
    private LocalDateTime updateTime;

    @TableLogic
    @Schema(description = "是否删除")
    private Integer deleted;
}

服务实例元数据实体

package com.jam.demo.route.entity;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.Map;

/**
 * 服务实例元数据
 * @author ken
 */
@Data
@Schema(description = "服务实例实体")
public class ServiceInstance {

    @Schema(description = "实例ID", requiredMode = Schema.RequiredMode.REQUIRED)
    private String instanceId;

    @Schema(description = "服务名称", requiredMode = Schema.RequiredMode.REQUIRED)
    private String serviceName;

    @Schema(description = "实例IP地址", requiredMode = Schema.RequiredMode.REQUIRED)
    private String ip;

    @Schema(description = "实例端口", requiredMode = Schema.RequiredMode.REQUIRED)
    private int port;

    @Schema(description = "实例所属机房")
    private String idc;

    @Schema(description = "实例所属可用区")
    private String zone;

    @Schema(description = "实例版本号")
    private String version;

    @Schema(description = "实例权重,默认100")
    private int weight = 100;

    @Schema(description = "实例健康状态")
    private boolean healthy;

    @Schema(description = "实例自定义元数据")
    private Map<String, String> metadata;
}

路由上下文实体

package com.jam.demo.route.entity;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
import java.util.Map;

/**
 * 路由上下文,承载路由执行过程中的所有请求信息与状态
 * @author ken
 */
@Data
@Schema(description = "路由上下文实体")
public class RouteContext {

    @Schema(description = "请求唯一标识", requiredMode = Schema.RequiredMode.REQUIRED)
    private String requestId;

    @Schema(description = "请求头集合")
    private Map<String, String> headers;

    @Schema(description = "请求参数集合")
    private Map<String, Object> params;

    @Schema(description = "调用方应用名称")
    private String callerAppName;

    @Schema(description = "目标服务名称", requiredMode = Schema.RequiredMode.REQUIRED)
    private String targetServiceName;

    @Schema(description = "当前可用的服务实例列表")
    private List<ServiceInstance> availableInstances;

    @Schema(description = "路由过滤后的服务实例列表")
    private List<ServiceInstance> filteredInstances;

    @Schema(description = "路由执行是否终止")
    private boolean routeTerminated;

    @Schema(description = "路由终止原因")
    private String terminateReason;
}

2. 路由责任链核心实现

路由处理器顶层接口

package com.jam.demo.route.handler;

import com.jam.demo.route.entity.RouteContext;

/**
 * 路由处理器接口,责任链模式核心
 * @author ken
 */
public interface RouteHandler {

    /**
     * 获取路由处理器的优先级,数值越小优先级越高
     * @return 优先级数值
     */
    int getOrder();

    /**
     * 执行路由过滤逻辑
     * @param context 路由上下文
     */
    void handle(RouteContext context);

    /**
     * 判断当前处理器是否需要执行
     * @param context 路由上下文
     * @return 是否需要执行
     */
    default boolean shouldHandle(RouteContext context) {
        return !context.isRouteTerminated();
    }
}

抽象路由处理器,封装公共逻辑

package com.jam.demo.route.handler;

import com.jam.demo.route.entity.RouteContext;
import com.jam.demo.route.entity.ServiceInstance;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import java.util.List;

/**
 * 抽象路由处理器,实现公共逻辑
 * @author ken
 */
@Slf4j
public abstract class AbstractRouteHandler implements RouteHandler {

    @Override
    public void handle(RouteContext context) {
        if (!shouldHandle(context)) {
            return;
        }
        List<ServiceInstance> currentInstances = context.getFilteredInstances();
        if (CollectionUtils.isEmpty(currentInstances)) {
            currentInstances = context.getAvailableInstances();
        }
        if (CollectionUtils.isEmpty(currentInstances)) {
            context.setRouteTerminated(true);
            context.setTerminateReason("无可用的服务实例");
            return;
        }
        List<ServiceInstance> filteredInstances = doFilter(context, currentInstances);
        if (CollectionUtils.isEmpty(filteredInstances)) {
            log.warn("路由处理器[{}]过滤后无可用实例,跳过当前规则"this.getClass().getSimpleName());
            return;
        }
        context.setFilteredInstances(filteredInstances);
        doAfterHandle(context);
    }

    /**
     * 执行具体的过滤逻辑,由子类实现
     * @param context 路由上下文
     * @param instances 待过滤的服务实例列表
     * @return 过滤后的服务实例列表
     */
    protected abstract List<ServiceInstance> doFilter(RouteContext context, List<ServiceInstance> instances);

    /**
     * 路由处理完成后的后置逻辑,可选实现
     * @param context 路由上下文
     */
    protected void doAfterHandle(RouteContext context) {

    }
}

同机房优先路由处理器(生产环境核心常用实现)

package com.jam.demo.route.handler;

import com.jam.demo.route.entity.RouteContext;
import com.jam.demo.route.entity.ServiceInstance;
import org.springframework.util.StringUtils;
import org.springframework.core.Ordered;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 同机房优先路由处理器
 * @author ken
 */
public class IdcPriorityRouteHandler extends AbstractRouteHandler {

    private static final String HEADER_IDC_KEY = "x-client-idc";

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE + 10;
    }

    @Override
    protected List<ServiceInstance> doFilter(RouteContext context, List<ServiceInstance> instances) {
        String clientIdc = context.getHeaders().get(HEADER_IDC_KEY);
        if (!StringUtils.hasText(clientIdc)) {
            return instances;
        }
        List<ServiceInstance> sameIdcInstances = instances.stream()
                .filter(instance -> clientIdc.equals(instance.getIdc()))
                .collect(Collectors.toList());
        return sameIdcInstances.isEmpty() ? instances : sameIdcInstances;
    }
}

参数路由处理器(灰度发布核心依赖实现)

package com.jam.demo.route.handler;

import com.jam.demo.route.entity.RouteContext;
import com.jam.demo.route.entity.RouteRule;
import com.jam.demo.route.entity.ServiceInstance;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.core.Ordered;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 参数路由处理器
 * @author ken
 */
public class ParamRouteHandler extends AbstractRouteHandler {

    private final List<RouteRule> routeRules;

    public ParamRouteHandler(List<RouteRule> routeRules) {
        this.routeRules = routeRules;
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE + 20;
    }

    @Override
    protected List<ServiceInstance> doFilter(RouteContext context, List<ServiceInstance> instances) {
        if (ObjectUtils.isEmpty(routeRules)) {
            return instances;
        }
        for (RouteRule rule : routeRules) {
            if (!rule.getEnabled()) {
                continue;
            }
            boolean matchResult = matchRule(context, rule);
            if (matchResult) {
                return filterInstanceByRule(instances, rule);
            }
        }
        return instances;
    }

    /**
     * 匹配路由规则
     */
    private boolean matchRule(RouteContext context, RouteRule rule) {
        Map<String, Object> matchConditions = rule.getMatchConditions();
        if (ObjectUtils.isEmpty(matchConditions)) {
            return false;
        }
        for (Map.Entry<String, Object> entry : matchConditions.entrySet()) {
            String paramKey = entry.getKey();
            Object expectValue = entry.getValue();
            Object actualValue = context.getParams().get(paramKey);
            if (ObjectUtils.isEmpty(actualValue)) {
                actualValue = context.getHeaders().get(paramKey);
            }
            if (ObjectUtils.isEmpty(actualValue)) {
                return false;
            }
            if (!expectValue.equals(actualValue)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 根据规则过滤服务实例
     */
    private List<ServiceInstance> filterInstanceByRule(List<ServiceInstance> instances, RouteRule rule) {
        Map<String, String> targetConditions = rule.getTargetConditions();
        if (ObjectUtils.isEmpty(targetConditions)) {
            return instances;
        }
        return instances.stream()
                .filter(instance -> {
                    for (Map.Entry<String, String> entry : targetConditions.entrySet()) {
                        String key = entry.getKey();
                        String expectValue = entry.getValue();
                        String actualValue = instance.getMetadata().get(key);
                        if (!StringUtils.hasText(actualValue)) {
                            if ("version".equals(key)) {
                                actualValue = instance.getVersion();
                            } else if ("idc".equals(key)) {
                                actualValue = instance.getIdc();
                            }
                        }
                        if (!expectValue.equals(actualValue)) {
                            return false;
                        }
                    }
                    return true;
                })
                .collect(Collectors.toList());
    }
}

路由链执行器,负责组装并执行完整路由链

package com.jam.demo.route.executor;

import com.jam.demo.route.entity.RouteContext;
import com.jam.demo.route.handler.RouteHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import org.springframework.stereotype.Component;
import java.util.Comparator;
import java.util.List;

/**
 * 路由链执行器
 * @author ken
 */
@Slf4j
@Component
public class RouteChainExecutor {

    private final List<RouteHandler> routeHandlers;

    public RouteChainExecutor(List<RouteHandler> routeHandlers) {
        this.routeHandlers = routeHandlers.stream()
                .sorted(Comparator.comparingInt(RouteHandler::getOrder))
                .toList();
    }

    /**
     * 执行路由链
     * @param context 路由上下文
     */
    public void execute(RouteContext context) {
        if (CollectionUtils.isEmpty(routeHandlers)) {
            context.setFilteredInstances(context.getAvailableInstances());
            return;
        }
        for (RouteHandler handler : routeHandlers) {
            handler.handle(context);
            if (context.isRouteTerminated()) {
                log.error("路由执行终止,requestId:{}, 原因:{}", context.getRequestId(), context.getTerminateReason());
                break;
            }
        }
    }
}

路由的容灾降级策略

  • 规则降级:当路由规则配置错误,导致过滤后无可用实例时,自动跳过该规则,回退到上一级的实例列表。
  • 全链路降级:当路由中心不可用时,所有路由规则自动失效,回退到默认的轮询负载均衡策略,保证请求正常处理。
  • 熔断降级:当某个路由规则的匹配耗时过长,或者错误率过高时,自动熔断该规则,避免影响整体请求性能。

三、灰度发布:风险可控的版本迭代,全链路灰度架构设计

灰度发布是降低版本迭代风险的核心手段,在正式全量发布前,通过小流量验证新版本的正确性与稳定性,将故障的影响范围控制在最小范围内。

易混淆发布模式的明确区分

很多开发者会混淆灰度、蓝绿、滚动发布的边界,三者的核心差异与适用场景如下:

  • 滚动发布:逐个重启服务实例替换为新版本,过程中老版本与新版本实例同时存在,流量自动负载到所有实例。优点是资源成本低,缺点是发布过程中版本共存,无法控制流量比例,出问题影响范围不可控,回滚速度慢。
  • 蓝绿发布:部署两套完全相同的集群,蓝集群跑老版本,绿集群跑新版本,验证通过后一次性将所有流量切换到绿集群。优点是发布过程无感知,回滚速度快,缺点是资源成本翻倍,无法做小流量验证,出问题影响全量流量。
  • 灰度发布(金丝雀发布):先部署少量新版本实例,将小比例的流量引导到新版本,验证通过后逐步扩大新版本的实例数量与流量比例,最终完成全量发布。优点是风险可控,爆炸半径小,支持精细化的流量控制,缺点是架构复杂度高,需要全链路的流量透传能力。

灰度发布的核心业务场景

  1. 流量比例灰度:按固定比例将流量分配到新版本,比如先切5%的流量到新版本,验证稳定后逐步提升到20%、50%、100%,适合无用户特征的通用业务场景。
  2. 白名单灰度:将指定的用户、IP、应用纳入白名单,只有白名单内的请求会转发到新版本,适合内部测试、特定用户群体验证的场景,是最安全的灰度方式。
  3. 用户特征灰度:基于用户的等级、地域、设备类型、业务属性等特征,将符合条件的用户流量转发到新版本,适合分地域、分用户群体的渐进式发布。
  4. 接口粒度灰度:只针对指定的接口进行灰度,其他接口仍然走老版本,适合单接口迭代、风险较高的接口变更场景,将影响范围控制到单个接口。
  5. 全链路灰度:在整个微服务调用链路中,灰度流量始终只会转发到对应版本的灰度实例,不会跨版本调用到基线版本的实例,保证灰度环境的完全隔离,适合多服务联动迭代的复杂场景,是目前生产环境最主流的灰度方案。

全链路灰度的核心架构设计

全链路灰度的核心原理是流量标记的全链路透传 + 基于标记的路由转发,整个架构分为三个核心环节,实现端到端的灰度流量隔离。

  1. 流量标记生成与注入:在流量入口(网关层),根据灰度规则判断请求是否属于灰度流量,若是则给请求打上唯一的灰度标记,并将标记透传到后续的全链路调用中。
  2. 灰度标记的全链路透传:在RPC调用、消息队列、异步线程等所有链路中,将灰度标记从上游透传到下游,保证整个链路中标记不丢失。
  3. 基于灰度标记的路由转发:每个服务的RPC客户端,根据透传过来的灰度标记,将请求转发到对应标记的服务实例,实现全链路的灰度隔离。

全链路灰度的核心实现代码示例

1. 灰度规则数据模型

MySQL灰度规则表

CREATE TABLE `gray_rule` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `rule_id` varchar(64NOT NULL COMMENT '规则唯一标识',
  `rule_name` varchar(128NOT NULL COMMENT '规则名称',
  `service_name` varchar(64NOT NULL COMMENT '目标服务名称',
  `gray_version` varchar(32NOT NULL COMMENT '灰度版本号',
  `gray_tag` varchar(32NOT NULL COMMENT '灰度标记',
  `rule_type` varchar(32NOT NULL COMMENT '规则类型:RATIO-比例灰度,WHITELIST-白名单灰度,PARAM-参数灰度',
  `gray_ratio` int DEFAULT '0' COMMENT '灰度流量比例,0-100',
  `whitelist` json DEFAULT NULL COMMENT '白名单列表,JSON格式',
  `match_conditions` json DEFAULT NULL COMMENT '参数匹配条件,JSON格式',
  `enabled` tinyint NOT NULL DEFAULT '1' COMMENT '是否启用:0-禁用,1-启用',
  `effect_start_time` datetime DEFAULT NULL COMMENT '生效开始时间',
  `effect_end_time` datetime DEFAULT NULL COMMENT '生效结束时间',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除:0-未删除,1-已删除',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_rule_id` (`rule_id`),
  KEY `idx_service_name` (`service_name`),
  KEY `idx_enabled` (`enabled`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='灰度规则表';

灰度规则实体类

package com.jam.demo.gray.entity;

import com.baomidou.mybatisplus.annotation.*;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

/**
 * 灰度规则实体
 * @author ken
 */
@Data
@TableName("gray_rule")
@Schema(description = "灰度规则实体")
public class GrayRule {

    @TableId(type = IdType.AUTO)
    @Schema(description = "主键ID")
    private Long id;

    @Schema(description = "规则唯一标识", requiredMode = Schema.RequiredMode.REQUIRED)
    private String ruleId;

    @Schema(description = "规则名称", requiredMode = Schema.RequiredMode.REQUIRED)
    private String ruleName;

    @Schema(description = "目标服务名称", requiredMode = Schema.RequiredMode.REQUIRED)
    private String serviceName;

    @Schema(description = "灰度版本号", requiredMode = Schema.RequiredMode.REQUIRED)
    private String grayVersion;

    @Schema(description = "灰度标记", requiredMode = Schema.RequiredMode.REQUIRED)
    private String grayTag;

    @Schema(description = "规则类型", requiredMode = Schema.RequiredMode.REQUIRED)
    private String ruleType;

    @Schema(description = "灰度流量比例")
    private Integer grayRatio;

    @Schema(description = "白名单列表")
    private List<String> whitelist;

    @Schema(description = "参数匹配条件")
    private Map<String, Object> matchConditions;

    @Schema(description = "是否启用")
    private Boolean enabled;

    @Schema(description = "生效开始时间")
    private LocalDateTime effectStartTime;

    @Schema(description = "生效结束时间")
    private LocalDateTime effectEndTime;

    @TableField(fill = FieldFill.INSERT)
    @Schema(description = "创建时间")
    private LocalDateTime createTime;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    @Schema(description = "更新时间")
    private LocalDateTime updateTime;

    @TableLogic
    @Schema(description = "是否删除")
    private Integer deleted;
}

2. 全链路流量上下文

用于存储并透传全链路的灰度标记、请求ID等信息,支持异步线程透传

package com.jam.demo.common.context;

import com.alibaba.ttl.TransmittableThreadLocal;
import org.springframework.util.ObjectUtils;

/**
 * 全链路流量上下文
 * @author ken
 */
public class TrafficContext {

    private static final TransmittableThreadLocal<TrafficContextCONTEXT_HOLDER = new TransmittableThreadLocal<>();

    private String grayTag;

    private String requestId;

    private String userId;

    public static TrafficContext getContext() {
        TrafficContext context = CONTEXT_HOLDER.get();
        if (ObjectUtils.isEmpty(context)) {
            context = new TrafficContext();
            CONTEXT_HOLDER.set(context);
        }
        return context;
    }

    public static void setGrayTag(String grayTag) {
        getContext().setGrayTag(grayTag);
    }

    public static String getGrayTag() {
        return getContext().getGrayTag();
    }

    public static void setRequestId(String requestId) {
        getContext().setRequestId(requestId);
    }

    public static String getRequestId() {
        return getContext().getRequestId();
    }

    public static void setUserId(String userId) {
        getContext().setUserId(userId);
    }

    public static String getUserId() {
        return getContext().getUserId();
    }

    public static void remove() {
        CONTEXT_HOLDER.remove();
    }

    public String getGrayTag() {
        return grayTag;
    }

    public void setGrayTag(String grayTag) {
        this.grayTag = grayTag;
    }

    public String getRequestId() {
        return requestId;
    }

    public void setRequestId(String requestId) {
        this.requestId = requestId;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }
}

3. 网关层灰度标记注入过滤器

基于Spring Cloud Gateway实现,匹配灰度规则并注入灰度标记到请求头

package com.jam.demo.gray.filter;

import com.jam.demo.gray.entity.GrayRule;
import com.jam.demo.gray.service.GrayRuleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 灰度标记全局过滤器
 * @author ken
 */
@Slf4j
@Component
public class GrayTagGlobalFilter implements GlobalFilter, Ordered {

    private static final String GRAY_TAG_HEADER = "x-gray-tag";
    private static final String USER_ID_HEADER = "x-user-id";
    private final GrayRuleService grayRuleService;

    public GrayTagGlobalFilter(GrayRuleService grayRuleService) {
        this.grayRuleService = grayRuleService;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String[] pathSegments = request.getPath().value().split("/");
        if (pathSegments.length < 2) {
            return chain.filter(exchange);
        }
        String serviceName = pathSegments[1];
        if (!StringUtils.hasText(serviceName)) {
            return chain.filter(exchange);
        }
        List<GrayRule> enabledRules = grayRuleService.getEnabledRulesByServiceName(serviceName);
        if (CollectionUtils.isEmpty(enabledRules)) {
            return chain.filter(exchange);
        }
        GrayRule matchedRule = matchGrayRule(request, enabledRules);
        if (matchedRule == null) {
            return chain.filter(exchange);
        }
        ServerHttpRequest.Builder requestBuilder = request.mutate();
        requestBuilder.header(GRAY_TAG_HEADER, matchedRule.getGrayTag());
        ServerWebExchange newExchange = exchange.mutate().request(requestBuilder.build()).build();
        return chain.filter(newExchange);
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE + 10;
    }

    /**
     * 匹配灰度规则
     */
    private GrayRule matchGrayRule(ServerHttpRequest request, List<GrayRule> rules) {
        for (GrayRule rule : rules) {
            boolean matchResult = switch (rule.getRuleType()) {
                case "WHITELIST" -> matchWhitelistRule(request, rule);
                case "RATIO" -> matchRatioRule(rule);
                case "PARAM" -> matchParamRule(request, rule);
                default -> false;
            };
            if (matchResult) {
                return rule;
            }
        }
        return null;
    }

    /**
     * 匹配白名单规则
     */
    private boolean matchWhitelistRule(ServerHttpRequest request, GrayRule rule) {
        List<String> whitelist = rule.getWhitelist();
        if (CollectionUtils.isEmpty(whitelist)) {
            return false;
        }
        String userId = request.getHeaders().getFirst(USER_ID_HEADER);
        if (!StringUtils.hasText(userId)) {
            return false;
        }
        return whitelist.contains(userId);
    }

    /**
     * 匹配比例规则
     */
    private boolean matchRatioRule(GrayRule rule) {
        int ratio = rule.getGrayRatio();
        if (ratio <= 0 || ratio > 100) {
            return false;
        }
        int random = ThreadLocalRandom.current().nextInt(100);
        return random < ratio;
    }

    /**
     * 匹配参数规则
     */
    private boolean matchParamRule(ServerHttpRequest request, GrayRule rule) {
        Map<String, Object> matchConditions = rule.getMatchConditions();
        if (ObjectUtils.isEmpty(matchConditions)) {
            return false;
        }
        for (Map.Entry<String, Object> entry : matchConditions.entrySet()) {
            String paramKey = entry.getKey();
            Object expectValue = entry.getValue();
            String actualValue = request.getHeaders().getFirst(paramKey);
            if (!StringUtils.hasText(actualValue)) {
                actualValue = request.getQueryParams().getFirst(paramKey);
            }
            if (!StringUtils.hasText(actualValue)) {
                return false;
            }
            if (!expectValue.equals(actualValue)) {
                return false;
            }
        }
        return true;
    }
}

4. Dubbo全链路灰度标记透传过滤器

实现RPC调用上下游的灰度标记透传,保证全链路标记不丢失

package com.jam.demo.gray.filter;

import com.jam.demo.common.context.TrafficContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
import org.springframework.core.Ordered;
import org.springframework.util.StringUtils;

/**
 * Dubbo灰度标记透传过滤器
 * @author ken
 */
@Slf4j
@Activate(group = {CommonConstants.CONSUMER, CommonConstants.PROVIDER}, order = Ordered.HIGHEST_PRECEDENCE)
public class DubboGrayTagFilter implements Filter {

    private static final String GRAY_TAG_KEY = "x-gray-tag";

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        RpcContext rpcContext = RpcContext.getServiceContext();
        if (rpcContext.isConsumerSide()) {
            String grayTag = TrafficContext.getGrayTag();
            if (StringUtils.hasText(grayTag)) {
                rpcContext.setAttachment(GRAY_TAG_KEY, grayTag);
            }
        }
        if (rpcContext.isProviderSide()) {
            String grayTag = rpcContext.getAttachment(GRAY_TAG_KEY);
            if (StringUtils.hasText(grayTag)) {
                TrafficContext.setGrayTag(grayTag);
            }
        }
        try {
            return invoker.invoke(invocation);
        } finally {
            if (rpcContext.isProviderSide()) {
                TrafficContext.remove();
            }
        }
    }
}

灰度发布的核心最佳实践

  • 发布流程管控:先完成功能测试、预发验证,再切白名单流量(内部员工、测试用户),再切小比例流量(5%),再逐步扩大比例,最后全量发布,全量后观察24小时无问题再下线老版本实例。
  • 可观测性隔离:灰度流量必须和基线流量做指标隔离,实时对比成功率、响应时间、错误率、业务指标等,一旦灰度指标异常,立即停止灰度,切回基线流量。
  • 一键回滚能力:必须支持一键禁用灰度规则,将所有流量切回基线版本,保证业务异常时能快速恢复。

四、流量调度:系统稳定性的指挥中枢,自适应调度架构实现

流量调度是路由能力的动态延伸,路由是基于静态规则的确定性转发,而流量调度是基于系统实时状态的动态、自适应的流量分配,是保障系统稳定性、提升资源利用率的核心手段。

流量调度的核心业务场景

  1. 跨机房/跨集群流量调度:当某个机房的集群负载过高、或者出现故障时,自动将部分流量调度到其他空闲的机房/集群,实现容灾与负载均衡,保障系统的可用性。
  2. 过载保护与流量削峰:当系统的负载超过阈值时,自动将非核心业务的流量调度到缓冲集群,或者进行限流降级,保障核心业务的可用性,应对流量洪峰。
  3. 热点流量调度:当某个接口、某个服务出现热点流量,导致实例负载不均时,自动将热点流量调度到空闲的实例,解决负载不均的问题,提升系统的整体性能。
  4. 成本优化的流量调度:基于业务的峰谷特性,在低峰期将流量调度到少数实例,关闭空闲实例,降低资源成本;在高峰期自动扩容,调度流量到新扩容的实例,保障性能。
  5. 容灾场景的流量调度:当某个可用区、机房出现故障时,自动将该机房的所有流量调度到其他正常的机房,实现异地多活的容灾能力。

流量调度的核心架构设计

流量调度架构分为四个核心模块,形成完整的闭环控制体系,实现自动化、自适应的流量管控。

  1. 指标采集模块:实时采集系统的各项指标,包括服务实例的CPU、内存、负载、响应时间、成功率、错误率、QPS等,还有集群、机房的整体指标,为调度决策提供数据支撑。
  2. 调度决策引擎:基于采集到的实时指标,结合预设的调度规则与算法,进行决策,生成对应的调度指令,比如调整路由权重、切换流量入口、扩容实例等。
  3. 规则执行模块:将调度决策引擎生成的指令,转化为具体的路由规则、负载均衡策略、扩容缩容指令,下发到对应的执行节点,执行调度操作。
  4. 效果反馈模块:调度执行后,实时采集系统的指标变化,反馈给调度决策引擎,判断调度是否达到预期效果,进行二次调整,形成完整的闭环。

自适应流量调度的核心实现

生产环境最成熟、最稳定的自适应调度算法是基于PID控制的流量调度算法,能根据系统的负载,自动调整实例的流量权重,避免系统过载,同时保证资源的充分利用。

1. 流量调度规则数据模型

MySQL流量调度规则表

CREATE TABLE `traffic_schedule_rule` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `rule_id` varchar(64NOT NULL COMMENT '规则唯一标识',
  `rule_name` varchar(128NOT NULL COMMENT '规则名称',
  `service_name` varchar(64NOT NULL COMMENT '目标服务名称',
  `rule_type` varchar(32NOT NULL COMMENT '规则类型:LOAD-负载调度,DISASTER-容灾调度,RATIO-比例调度',
  `target_load_threshold` double NOT NULL DEFAULT '0.7' COMMENT '目标负载阈值,0-1',
  `max_load_threshold` double NOT NULL DEFAULT '0.9' COMMENT '最大负载阈值,0-1',
  `min_load_threshold` double NOT NULL DEFAULT '0.3' COMMENT '最小负载阈值,0-1',
  `enabled` tinyint NOT NULL DEFAULT '1' COMMENT '是否启用:0-禁用,1-启用',
  `schedule_interval` int NOT NULL DEFAULT '10' COMMENT '调度执行间隔,单位秒',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除:0-未删除,1-已删除',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_rule_id` (`rule_id`),
  KEY `idx_service_name` (`service_name`),
  KEY `idx_enabled` (`enabled`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='流量调度规则表';

流量调度规则实体类

package com.jam.demo.traffic.entity;

import com.baomidou.mybatisplus.annotation.*;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;

/**
 * 流量调度规则实体
 * @author ken
 */
@Data
@TableName("traffic_schedule_rule")
@Schema(description = "流量调度规则实体")
public class TrafficScheduleRule {

    @TableId(type = IdType.AUTO)
    @Schema(description = "主键ID")
    private Long id;

    @Schema(description = "规则唯一标识", requiredMode = Schema.RequiredMode.REQUIRED)
    private String ruleId;

    @Schema(description = "规则名称", requiredMode = Schema.RequiredMode.REQUIRED)
    private String ruleName;

    @Schema(description = "目标服务名称", requiredMode = Schema.RequiredMode.REQUIRED)
    private String serviceName;

    @Schema(description = "规则类型", requiredMode = Schema.RequiredMode.REQUIRED)
    private String ruleType;

    @Schema(description = "目标负载阈值")
    private Double targetLoadThreshold;

    @Schema(description = "最大负载阈值")
    private Double maxLoadThreshold;

    @Schema(description = "最小负载阈值")
    private Double minLoadThreshold;

    @Schema(description = "是否启用")
    private Boolean enabled;

    @Schema(description = "调度执行间隔,单位秒")
    private Integer scheduleInterval;

    @TableField(fill = FieldFill.INSERT)
    @Schema(description = "创建时间")
    private LocalDateTime createTime;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    @Schema(description = "更新时间")
    private LocalDateTime updateTime;

    @TableLogic
    @Schema(description = "是否删除")
    private Integer deleted;
}

服务实例实时指标实体

package com.jam.demo.traffic.entity;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

/**
 * 服务实例实时指标实体
 * @author ken
 */
@Data
@Schema(description = "服务实例实时指标实体")
public class InstanceMetrics {

    @Schema(description = "实例ID", requiredMode = Schema.RequiredMode.REQUIRED)
    private String instanceId;

    @Schema(description = "服务名称", requiredMode = Schema.RequiredMode.REQUIRED)
    private String serviceName;

    @Schema(description = "系统CPU负载,0-1")
    private double systemLoad;

    @Schema(description = "JVM内存使用率,0-1")
    private double memoryUsage;

    @Schema(description = "实例QPS")
    private double qps;

    @Schema(description = "平均响应时间,单位ms")
    private double avgResponseTime;

    @Schema(description = "请求成功率,0-1")
    private double successRate;

    @Schema(description = "实例当前权重")
    private int currentWeight;

    @Schema(description = "实例是否健康")
    private boolean healthy;
}

2. 基于PID控制的自适应流量调度器实现

package com.jam.demo.traffic.scheduler;

import com.jam.demo.traffic.entity.InstanceMetrics;
import com.jam.demo.traffic.entity.TrafficScheduleRule;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 基于PID控制的自适应流量调度器
 * @author ken
 */
@Slf4j
@Component
public class PidAdaptiveTrafficScheduler {

    private static final double DEFAULT_KP = 0.5;
    private static final double DEFAULT_KI = 0.2;
    private static final double DEFAULT_KD = 0.1;
    private static final int MIN_WEIGHT = 10;
    private static final int MAX_WEIGHT = 100;
    private double integral = 0.0;
    private double previousError = 0.0;

    /**
     * 执行自适应流量调度,调整实例权重
     * @param rule 调度规则
     * @param metricsList 实例实时指标列表
     * @return 调整后的实例权重Map,key为实例ID,value为权重
     */
    public Map<String, Integer> schedule(TrafficScheduleRule rule, List<InstanceMetrics> metricsList) {
        if (CollectionUtils.isEmpty(metricsList)) {
            return Map.of();
        }
        double targetLoad = rule.getTargetLoadThreshold();
        Map<String, Integer> weightMap = metricsList.stream()
                .collect(Collectors.toMap(InstanceMetrics::getInstanceId, InstanceMetrics::getCurrentWeight));
        for (InstanceMetrics metrics : metricsList) {
            double currentLoad = metrics.getSystemLoad();
            double error = targetLoad - currentLoad;
            integral += error;
            double derivative = error - previousError;
            double output = DEFAULT_KP * error + DEFAULT_KI * integral + DEFAULT_KD * derivative;
            previousError = error;
            int currentWeight = metrics.getCurrentWeight();
            int newWeight = calculateNewWeight(currentWeight, output);
            weightMap.put(metrics.getInstanceId(), newWeight);
            log.info("实例[{}]调度调整,当前负载:{}, 目标负载:{}, 原权重:{}, 新权重:{}",
                    metrics.getInstanceId(), currentLoad, targetLoad, currentWeight, newWeight);
        }
        return weightMap;
    }

    /**
     * 计算新的权重,限制在最小和最大值之间
     */
    private int calculateNewWeight(int currentWeight, double output) {
        int newWeight = currentWeight + (int) Math.round(output);
        return Math.max(MIN_WEIGHT, Math.min(MAX_WEIGHT, newWeight));
    }

    /**
     * 重置PID控制器状态
     */
    public void reset() {
        this.integral = 0.0;
        this.previousError = 0.0;
    }
}

流量调度的核心最佳实践

  • 调度粒度控制:优先在实例级别调度,再到集群级别,最后到机房级别,粒度越细,调度越精准,对业务的影响越小。
  • 冷却时间设置:每次调度后,必须设置冷却时间,避免频繁调度导致系统震荡,一般设置为10-30秒。
  • 熔断保护机制:当调度执行后,系统指标没有改善甚至恶化时,立即停止调度,回退到默认的负载均衡策略,避免调度导致的系统故障。
  • 可观测性保障:所有的调度操作、调度前后的指标变化,都必须有完整的日志和指标,方便问题排查与算法调优。

五、全链路服务治理协同架构:三大能力的整合与流程闭环

路由、灰度、流量调度三者不是孤立的,只有有机整合,才能形成完整的全链路服务治理体系,实现对流量的全生命周期精准管控。

协同架构的核心执行流程

整个协同架构的执行流程分为五个核心环节,形成完整的闭环管控:

  1. 流量进入:用户请求进入系统,首先经过接入层路由,转发到对应的网关集群。
  2. 灰度规则匹配:网关层根据灰度规则,判断请求是否为灰度流量,若是则注入灰度标记,同时根据灰度规则生成路由规则。
  3. 流量调度决策:流量调度引擎根据系统实时指标,生成动态的路由权重与调度规则,下发到路由规则引擎。
  4. 路由规则执行:路由规则引擎整合静态路由规则、灰度路由规则、动态调度规则,按照优先级执行路由链,将请求转发到符合条件的服务实例。
  5. 指标反馈与闭环:请求处理过程中的所有指标,实时采集到监控系统,反馈给灰度规则引擎和流量调度引擎,进行规则的动态调整与优化。

协同架构的核心设计要点

  • 规则优先级管理:明确不同来源的路由规则的优先级,灰度规则优先级最高,其次是流量调度的动态规则,最后是静态的路由规则,避免规则冲突。
  • 统一的元数据体系:所有的服务实例、路由规则、灰度规则、调度规则,都基于统一的元数据模型,保证各个模块之间的数据一致性。
  • 统一的流量上下文:全链路使用统一的流量上下文,透传请求ID、灰度标记、用户特征等信息,保证整个链路的流量可追溯、可管控。
  • 统一的可观测体系:所有模块的日志、指标、链路追踪,都整合到统一的可观测平台,实现全链路的问题排查与监控告警。

六、落地最佳实践与避坑指南

1. 规则冲突问题

坑点:多个路由规则、灰度规则、调度规则同时生效,导致规则冲突,流量转发不符合预期,甚至出现请求失败。 解决方案:建立明确的规则优先级体系,规则执行前进行冲突检测,当出现冲突时,按照优先级高的规则执行,同时给出告警,通知管理员处理。

2. 全链路灰度标记丢失问题

坑点:在异步调用、消息队列、定时任务等场景中,灰度标记丢失,导致灰度流量调用到基线版本的服务,出现业务异常。 解决方案:使用TransmittableThreadLocal实现上下文的异步透传,在消息队列、定时任务中,将灰度标记存入消息体或任务参数中,消费时重新注入流量上下文,保证标记不丢失。

3. 流量调度导致的系统震荡问题

坑点:流量调度的频率过高,或者算法参数设置不合理,导致实例权重频繁调整,系统负载波动剧烈,出现震荡。 解决方案:设置调度冷却时间,限制调度的最大频率,优化PID算法的参数,加入死区控制,当负载偏差在阈值范围内时,不进行调度,避免频繁调整。

4. 路由规则的性能损耗问题

坑点:路由规则过多,匹配逻辑复杂,导致网关和RPC客户端的路由匹配耗时过长,影响请求的响应时间。 解决方案:对路由规则进行索引优化,按照服务名称、规则类型进行分组,只匹配当前服务对应的规则,同时对规则进行预编译,提升匹配效率,对于不启用的规则,直接过滤,不参与匹配。

5. 灰度发布的指标对比问题

坑点:灰度流量和基线流量的指标没有隔离,无法准确判断灰度版本的稳定性,导致灰度版本出现问题时,无法及时发现。 解决方案:基于灰度标记对指标进行打标,在监控平台中建立灰度和基线的独立监控大盘,实时对比各项核心指标,设置异常告警,一旦灰度指标异常,立即触发告警,甚至自动回滚。

6. 容灾场景的路由降级问题

坑点:当注册中心、配置中心不可用时,路由规则无法加载,导致流量无法转发,出现系统雪崩。 解决方案:路由规则本地缓存,当配置中心不可用时,使用本地缓存的规则继续执行,同时设置降级策略,当所有规则都无法匹配时,回退到默认的负载均衡策略,保证请求正常处理。

七、项目核心依赖配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.4</version>
        <relativePath/>
    </parent>
    <groupId>com.jam</groupId>
    <artifactId>service-governance-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>service-governance-demo</name>
    <description>服务治理路由、灰度、流量调度演示项目</description>
    <properties>
        <java.version>17</java.version>
        <spring-cloud.version>2023.0.1</spring-cloud.version>
        <dubbo.version>3.3.0</dubbo.version>
        <mybatis-plus.version>3.5.7</mybatis-plus.version>
        <fastjson2.version>2.0.52</fastjson2.version>
        <guava.version>33.1.0-jre</guava.version>
        <transmittable-thread-local.version>2.14.2</transmittable-thread-local.version>
        <springdoc.version>2.5.0</springdoc.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>${dubbo.version}</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
            <version>${fastjson2.version}</version>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>${guava.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>transmittable-thread-local</artifactId>
            <version>${transmittable-thread-local.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
            <version>${springdoc.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.32</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

总结

服务治理是微服务架构的核心基石,而路由、灰度、流量调度,是服务治理体系中最核心的三大能力。路由解决了流量往哪里去的问题,是所有流量管控的基础;灰度解决了版本迭代的风险问题,是业务快速迭代的保障;流量调度解决了系统稳定性的问题,是应对流量波动与故障的核心手段。