# yyc-common-gray 基于 Nacos 的**版本灰度**与**跨 Group 服务发现**公共组件,供 Feign + Ribbon 消费者与 Spring Cloud Gateway 复用。 ## 能力概览 | 能力 | 场景 | 核心类 | |------|------|--------| | 版本灰度(Ribbon) | 微服务通过 Feign 调用同 Group 服务 | `GrayRibbonLoadBalancerRule`、`GrayFeignRequestInterceptor` | | 跨 Group 发现(Ribbon) | 消费者与提供方不在同一 Nacos Group | `NacosCrossGroupServerList` | | 跨 Group 工具(Gateway) | 路由 URI 编解码、直接查 Nacos 选实例 | `NacosGroupServiceIdCodec`、`NacosCrossGroupInstanceSelector` | 引入依赖后通过 `META-INF/spring.factories` 自动装配,无需手动 `@Import`。 ## 依赖引入 ```xml net.yyc.common yyc-common-gray ${yyc-common.version} ``` 传递依赖:`yyc-common-core`、`spring-cloud-starter-alibaba-nacos-discovery`、`feign-core`。 适用栈:Spring Cloud Alibaba **2.2.x** + Netflix Ribbon(`@FeignClient` + `ribbon` 负载均衡)。 ## 版本灰度(Feign / Ribbon) ### 开关 ```yaml gray: rule: enabled: true ``` 开启后注册: - `ribbonLoadBalancerRule` → `GrayRibbonLoadBalancerRule`:按请求头 `VERSION` 与实例元数据 `VERSION` 匹配;无头或未匹配时随机选实例。 - `grayFeignRequestInterceptor`:将当前 HTTP 请求的 `VERSION` 头透传到下游 Feign 调用。 ### 约定 - 请求头 / 元数据键名:`VERSION`(见 `CommonConstants.VERSION`)。 - 实例需在 Nacos 注册元数据中携带对应 `VERSION` 字段。 ### 注意 - 非 Web 场景(定时任务、消息消费等)无 `HttpServletRequest` 时,规则会退化为随机选实例,与改造前行为一致。 - **不要**再配置 `NFLoadBalancerRuleClassName`(如 `NacosRule`),会与 `ribbonLoadBalancerRule` Bean 冲突。 ## 跨 Nacos Group(Feign / Ribbon) 官方 `NacosServerList` 固定使用消费者自身的 `spring.cloud.nacos.discovery.group`,无法通过 `{serviceId}.ribbon.nacos.group` 指定目标 Group。本模块用 `NacosCrossGroupServerList` 覆盖实例拉取逻辑。 ### 配置 为每个 `@FeignClient` 的 `value`(即 Ribbon `clientName` / `serviceId`)指定目标 Group: ```yaml yyc: nacos: cross-group: gulop-gig-biz: cso_gig ``` 未配置时 fallback 到 `spring.cloud.nacos.discovery.group`,与同 Group 服务行为一致。 ### 示例 消费者 `hnqz-upms-biz`(Group `cso_hnqz`)调用 `gulop-gig-biz`(Group `cso_gig`): ```java @FeignClient(value = "gulop-gig-biz") public interface GigUserApiClient { ... } ``` ```yaml spring: cloud: nacos: discovery: group: cso_hnqz yyc: nacos: cross-group: gulop-gig-biz: cso_gig gray: rule: enabled: true ``` ### 自动配置说明 | 组件 | 作用 | |------|------| | `NacosCrossGroupEnvironmentPostProcessor` | 启动早期排除 `RibbonNacosAutoConfiguration`,避免与自定义 `@RibbonClients` 重复注册 | | `NacosCrossGroupRibbonAutoConfiguration` | `@RibbonClients(defaultConfiguration = NacosCrossGroupRibbonClientConfiguration.class)` | | `NacosCrossGroupRibbonClientConfiguration` | 注册 `NacosCrossGroupServerList` 替代默认 `NacosServerList` | 激活条件:`@ConditionalOnRibbonNacos`(项目启用 Ribbon + Nacos 时)。 ## 跨 Nacos Group(Gateway 共享工具) 网关侧不经过 Ribbon,使用 `net.yyc.common.gray.nacos` 包中的工具类(`yyc-gateway-biz` 等业务模块引用本 jar 即可)。 ### 服务名编解码 Java `URI` 的 host 不允许 `@`、`_` 等字符,跨 Group 路由使用: | 形态 | 示例 | |------|------| | Nacos serviceId | `cso_gig@@gulop-gig-biz` | | 路由 URI host(存储) | `cso-gig--gulop-gig-biz`(group 中 `_` → `-`) | `NacosGroupServiceIdCodec` 提供 `encodeLbUri`、`parseFromUriHost`、`parseFromNacosServiceId` 等方法,并处理 `%40%40` 被 URI 解析器编码的情况。 ### 实例选择 存在 `org.springframework.cloud.gateway.filter.GlobalFilter` 时,自动注册 `NacosCrossGroupInstanceSelector` Bean,按 **group + serviceName** 调用 Nacos `selectInstances`,并支持 `VERSION` 头灰度(与 Ribbon 规则语义一致)。 业务侧(如 `NacosGroupGrayLoadBalancer`)注入该 Bean 并传入版本头即可。 ## 架构关系 ```mermaid flowchart TB subgraph feign [Feign 消费者] REQ[HTTP 请求 VERSION] INT[GrayFeignRequestInterceptor] SL[NacosCrossGroupServerList] RULE[GrayRibbonLoadBalancerRule] REQ --> INT INT --> RULE SL --> RULE SL -->|"yyc.nacos.cross-group.{serviceId}"| Nacos[(Nacos)] end subgraph gw [Gateway] CODEC[NacosGroupServiceIdCodec] SEL[NacosCrossGroupInstanceSelector] CODEC --> SEL SEL --> Nacos end ``` ## 包结构 ``` net.yyc.common.gray ├── GrayRibbonLoadBalancerConfiguration # gray.rule.enabled 灰度规则与 Feign 拦截器 ├── feign/ │ └── GrayFeignRequestInterceptor ├── rule/ │ └── GrayRibbonLoadBalancerRule ├── ribbon/ │ ├── NacosCrossGroupEnvironmentPostProcessor │ ├── NacosCrossGroupRibbonAutoConfiguration │ ├── NacosCrossGroupRibbonClientConfiguration │ └── NacosCrossGroupServerList └── nacos/ ├── NacosGroupService ├── NacosGroupServiceIdCodec ├── NacosCrossGroupInstanceSelector └── NacosCrossGroupGatewayAutoConfiguration ``` ## 常见问题 **Q: 配置了 `yyc.nacos.cross-group.gulop-gig-biz` 仍拉不到实例?** A: 确认已引入本模块且未排除 `NacosCrossGroupRibbonAutoConfiguration`;查看日志中 `Nacos cross-group server list` 的 `targetGroup` 是否为预期值。 **Q: Gateway 需要单独配 `yyc.nacos.cross-group` 吗?** A: 不需要。网关跨 Group 在路由 URI 中编码 `group--service`,由 `NacosCrossGroupInstanceSelector` 解析后查询;与 Feign 的 YAML 键是两套机制。 **Q: 与 `yyc-common-gateway` 的关系?** A: `yyc-common-gateway` 提供 `GrayLoadBalancer`、`VersionGrayLoadBalancer` 等 Gateway 灰度接口;本模块提供 Nacos 跨 Group 底层能力与 Ribbon 侧完整链路。网关业务模块通常同时依赖两者。 ## 构建 ```bash cd yyc-common-parent mvn -pl yyc-common-gray -am install ``` 发布新版本后,通过 `yyc-common-bom` 统一升级各服务的 `yyc-common-gray` 坐标。