微服务架构的普及,让系统拆分更灵活、迭代效率更高,但也带来了新的挑战:分布式环境下服务实例数量激增、版本迭代风险不可控、流量波动对系统稳定性的冲击、多环境与多集群的流量管理复杂度指数级上升。服务治理正是解决这些问题的核心抓手,而路由、灰度、流量调度,是服务治理体系中最核心的三大能力,三者相辅相成,构成了对流量全生命周期的精准管控。
很多开发者在落地服务治理时,往往会陷入三个误区:一是将三大能力割裂看待,只做单点功能而无法形成闭环;二是只关注API层面的实现,忽略了底层架构的可扩展性与高可用性;三是盲目照搬开源组件,没有结合业务场景做适配,最终导致功能无法落地,甚至引入线上故障。
本文将从底层逻辑出发,拆解路由、灰度、流量调度的核心原理、架构设计与落地实现,帮助开发者建立完整的服务治理知识体系,同时提供可直接复用的架构方案与代码实现,解决生产环境中的实际问题。
一、服务治理的底层逻辑:三大核心能力的本质与协同关系
服务治理的核心,是对分布式系统中流量的全生命周期管控,从请求进入系统的那一刻起,到请求完成响应的全链路,都需要被精准、可控、可追溯的管理。
三大核心能力的本质
- 服务路由:流量的「精准导航系统」 本质是基于预设规则,将请求确定性地转发到符合条件的目标服务实例,解决的核心问题是「流量该往哪里去」。它是整个服务治理体系的基础,所有的流量管控能力,最终都要通过路由来落地。
- 灰度发布:风险可控的「版本试水平台」 本质是基于流量特征或比例,将部分流量渐进式地引导到新版本服务实例,通过小流量验证新版本的正确性与稳定性,逐步扩大流量范围,最终完成全量发布。它解决的核心问题是「如何把版本迭代的爆炸半径降到最低」,是保障业务连续性的核心手段。
- 流量调度:系统稳定性的「智能指挥中枢」 本质是基于系统实时状态、业务优先级与容灾策略,对流量进行动态的、自适应的分配与调度,解决的核心问题是「如何让流量分配最优,保障系统稳定性与资源利用率的平衡」。它是整个服务治理体系的动态调节能力,应对流量波动、系统故障、资源不均等突发场景。
三大能力的协同关系
路由是基础能力,为灰度和流量调度提供了流量转发的执行层;灰度是路由的核心业务场景,通过路由规则实现灰度流量的精准转发;流量调度是路由规则的动态生成与调整中枢,基于实时数据更新路由策略,实现自适应的流量管控。三者形成了「规则定义-规则执行-动态调优」的完整闭环,构成了服务治理体系的核心骨架。
二、服务路由:流量的精准导航系统,架构设计与落地实现
服务路由的核心价值,在于解决分布式系统中服务实例多集群、多机房、多版本、多环境的流量精准转发问题,是环境隔离、容灾切换、灰度发布等能力的底层基础。
服务路由的分层架构
在完整的微服务体系中,路由能力分为三层,每层有明确的职责边界与适用场景,三者协同实现全链路的路由管控。
- 接入层路由 部署在整个系统的最前端,一般为LVS、Nginx等四层/七层负载均衡设备,核心职责是机房级/集群级的流量转发,比如基于域名、路径、客户端IP的路由,将流量转发到对应的网关集群或业务集群。它的特点是性能极高,规则简单,适合做粗粒度的流量管控。
- 网关层路由 部署在接入层之后、业务服务之前,一般为Spring Cloud Gateway、APISIX、Kong等API网关,核心职责是API粒度、业务特征粒度的路由转发,比如基于请求参数、请求头、Cookie、用户特征的路由,同时集成了鉴权、限流、日志、协议转换等能力。它的特点是规则灵活,可扩展性强,适合做业务级的细粒度路由管控,也是灰度发布最常用的一层。
- 服务框架层(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(64) NOT NULL COMMENT '规则唯一标识',
`rule_name` varchar(128) NOT NULL COMMENT '规则名称',
`rule_type` varchar(32) NOT 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;
}
}
}
}
路由的容灾降级策略
- 规则降级:当路由规则配置错误,导致过滤后无可用实例时,自动跳过该规则,回退到上一级的实例列表。
- 全链路降级:当路由中心不可用时,所有路由规则自动失效,回退到默认的轮询负载均衡策略,保证请求正常处理。
- 熔断降级:当某个路由规则的匹配耗时过长,或者错误率过高时,自动熔断该规则,避免影响整体请求性能。
三、灰度发布:风险可控的版本迭代,全链路灰度架构设计
灰度发布是降低版本迭代风险的核心手段,在正式全量发布前,通过小流量验证新版本的正确性与稳定性,将故障的影响范围控制在最小范围内。
易混淆发布模式的明确区分
很多开发者会混淆灰度、蓝绿、滚动发布的边界,三者的核心差异与适用场景如下:
- 滚动发布:逐个重启服务实例替换为新版本,过程中老版本与新版本实例同时存在,流量自动负载到所有实例。优点是资源成本低,缺点是发布过程中版本共存,无法控制流量比例,出问题影响范围不可控,回滚速度慢。
- 蓝绿发布:部署两套完全相同的集群,蓝集群跑老版本,绿集群跑新版本,验证通过后一次性将所有流量切换到绿集群。优点是发布过程无感知,回滚速度快,缺点是资源成本翻倍,无法做小流量验证,出问题影响全量流量。
- 灰度发布(金丝雀发布):先部署少量新版本实例,将小比例的流量引导到新版本,验证通过后逐步扩大新版本的实例数量与流量比例,最终完成全量发布。优点是风险可控,爆炸半径小,支持精细化的流量控制,缺点是架构复杂度高,需要全链路的流量透传能力。
灰度发布的核心业务场景
- 流量比例灰度:按固定比例将流量分配到新版本,比如先切5%的流量到新版本,验证稳定后逐步提升到20%、50%、100%,适合无用户特征的通用业务场景。
- 白名单灰度:将指定的用户、IP、应用纳入白名单,只有白名单内的请求会转发到新版本,适合内部测试、特定用户群体验证的场景,是最安全的灰度方式。
- 用户特征灰度:基于用户的等级、地域、设备类型、业务属性等特征,将符合条件的用户流量转发到新版本,适合分地域、分用户群体的渐进式发布。
- 接口粒度灰度:只针对指定的接口进行灰度,其他接口仍然走老版本,适合单接口迭代、风险较高的接口变更场景,将影响范围控制到单个接口。
- 全链路灰度:在整个微服务调用链路中,灰度流量始终只会转发到对应版本的灰度实例,不会跨版本调用到基线版本的实例,保证灰度环境的完全隔离,适合多服务联动迭代的复杂场景,是目前生产环境最主流的灰度方案。
全链路灰度的核心架构设计
全链路灰度的核心原理是流量标记的全链路透传 + 基于标记的路由转发,整个架构分为三个核心环节,实现端到端的灰度流量隔离。
- 流量标记生成与注入:在流量入口(网关层),根据灰度规则判断请求是否属于灰度流量,若是则给请求打上唯一的灰度标记,并将标记透传到后续的全链路调用中。
- 灰度标记的全链路透传:在RPC调用、消息队列、异步线程等所有链路中,将灰度标记从上游透传到下游,保证整个链路中标记不丢失。
- 基于灰度标记的路由转发:每个服务的RPC客户端,根据透传过来的灰度标记,将请求转发到对应标记的服务实例,实现全链路的灰度隔离。
全链路灰度的核心实现代码示例
1. 灰度规则数据模型
MySQL灰度规则表
CREATE TABLE `gray_rule` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`rule_id` varchar(64) NOT NULL COMMENT '规则唯一标识',
`rule_name` varchar(128) NOT NULL COMMENT '规则名称',
`service_name` varchar(64) NOT NULL COMMENT '目标服务名称',
`gray_version` varchar(32) NOT NULL COMMENT '灰度版本号',
`gray_tag` varchar(32) NOT NULL COMMENT '灰度标记',
`rule_type` varchar(32) NOT 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<TrafficContext> CONTEXT_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小时无问题再下线老版本实例。
- 可观测性隔离:灰度流量必须和基线流量做指标隔离,实时对比成功率、响应时间、错误率、业务指标等,一旦灰度指标异常,立即停止灰度,切回基线流量。
- 一键回滚能力:必须支持一键禁用灰度规则,将所有流量切回基线版本,保证业务异常时能快速恢复。
四、流量调度:系统稳定性的指挥中枢,自适应调度架构实现
流量调度是路由能力的动态延伸,路由是基于静态规则的确定性转发,而流量调度是基于系统实时状态的动态、自适应的流量分配,是保障系统稳定性、提升资源利用率的核心手段。
流量调度的核心业务场景
- 跨机房/跨集群流量调度:当某个机房的集群负载过高、或者出现故障时,自动将部分流量调度到其他空闲的机房/集群,实现容灾与负载均衡,保障系统的可用性。
- 过载保护与流量削峰:当系统的负载超过阈值时,自动将非核心业务的流量调度到缓冲集群,或者进行限流降级,保障核心业务的可用性,应对流量洪峰。
- 热点流量调度:当某个接口、某个服务出现热点流量,导致实例负载不均时,自动将热点流量调度到空闲的实例,解决负载不均的问题,提升系统的整体性能。
- 成本优化的流量调度:基于业务的峰谷特性,在低峰期将流量调度到少数实例,关闭空闲实例,降低资源成本;在高峰期自动扩容,调度流量到新扩容的实例,保障性能。
- 容灾场景的流量调度:当某个可用区、机房出现故障时,自动将该机房的所有流量调度到其他正常的机房,实现异地多活的容灾能力。
流量调度的核心架构设计
流量调度架构分为四个核心模块,形成完整的闭环控制体系,实现自动化、自适应的流量管控。
- 指标采集模块:实时采集系统的各项指标,包括服务实例的CPU、内存、负载、响应时间、成功率、错误率、QPS等,还有集群、机房的整体指标,为调度决策提供数据支撑。
- 调度决策引擎:基于采集到的实时指标,结合预设的调度规则与算法,进行决策,生成对应的调度指令,比如调整路由权重、切换流量入口、扩容实例等。
- 规则执行模块:将调度决策引擎生成的指令,转化为具体的路由规则、负载均衡策略、扩容缩容指令,下发到对应的执行节点,执行调度操作。
- 效果反馈模块:调度执行后,实时采集系统的指标变化,反馈给调度决策引擎,判断调度是否达到预期效果,进行二次调整,形成完整的闭环。
自适应流量调度的核心实现
生产环境最成熟、最稳定的自适应调度算法是基于PID控制的流量调度算法,能根据系统的负载,自动调整实例的流量权重,避免系统过载,同时保证资源的充分利用。
1. 流量调度规则数据模型
MySQL流量调度规则表
CREATE TABLE `traffic_schedule_rule` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`rule_id` varchar(64) NOT NULL COMMENT '规则唯一标识',
`rule_name` varchar(128) NOT NULL COMMENT '规则名称',
`service_name` varchar(64) NOT NULL COMMENT '目标服务名称',
`rule_type` varchar(32) NOT 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秒。
- 熔断保护机制:当调度执行后,系统指标没有改善甚至恶化时,立即停止调度,回退到默认的负载均衡策略,避免调度导致的系统故障。
- 可观测性保障:所有的调度操作、调度前后的指标变化,都必须有完整的日志和指标,方便问题排查与算法调优。
五、全链路服务治理协同架构:三大能力的整合与流程闭环
路由、灰度、流量调度三者不是孤立的,只有有机整合,才能形成完整的全链路服务治理体系,实现对流量的全生命周期精准管控。
协同架构的核心执行流程
整个协同架构的执行流程分为五个核心环节,形成完整的闭环管控:
- 流量进入:用户请求进入系统,首先经过接入层路由,转发到对应的网关集群。
- 灰度规则匹配:网关层根据灰度规则,判断请求是否为灰度流量,若是则注入灰度标记,同时根据灰度规则生成路由规则。
- 流量调度决策:流量调度引擎根据系统实时指标,生成动态的路由权重与调度规则,下发到路由规则引擎。
- 路由规则执行:路由规则引擎整合静态路由规则、灰度路由规则、动态调度规则,按照优先级执行路由链,将请求转发到符合条件的服务实例。
- 指标反馈与闭环:请求处理过程中的所有指标,实时采集到监控系统,反馈给灰度规则引擎和流量调度引擎,进行规则的动态调整与优化。
协同架构的核心设计要点
- 规则优先级管理:明确不同来源的路由规则的优先级,灰度规则优先级最高,其次是流量调度的动态规则,最后是静态的路由规则,避免规则冲突。
- 统一的元数据体系:所有的服务实例、路由规则、灰度规则、调度规则,都基于统一的元数据模型,保证各个模块之间的数据一致性。
- 统一的流量上下文:全链路使用统一的流量上下文,透传请求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>
总结
服务治理是微服务架构的核心基石,而路由、灰度、流量调度,是服务治理体系中最核心的三大能力。路由解决了流量往哪里去的问题,是所有流量管控的基础;灰度解决了版本迭代的风险问题,是业务快速迭代的保障;流量调度解决了系统稳定性的问题,是应对流量波动与故障的核心手段。