如何巧妙设计SpringMVC API灰度发布?

2026-04-30 07:311阅读0评论建站教程
  • 内容介绍
  • 文章标签
  • 相关推荐
基于SpringMVC的API灰度方案

哎,说实话,搞API灰度发布这玩意儿,一开始真是头大。各种方案,各种配置,让人眼花缭乱。特别是咱们这种小公司, 人员有限,搞那些复杂的Istio、Service Mesh什么的,简直是mission impossible。后来想了想,与其追逐潮流,不如脚踏实地,利用现有的技术栈搞一套简单的、可控的方案。于是乎,就有了这个基于SpringMVC的API灰度发布方案。

背景与问题

在快速迭代的产品开发中,API接口的更新迭代是常态,只是,旧版本接口往往已经有大量用户依赖,无法马上停用,那么如何在不影响现有用户的前提下平滑引入新版本API?,太离谱了。

传统API版本管理方案及不足

以前我们都是怎么搞的呢?要么在URL上加版本号,要么在header里加个version参数。这种方式简单粗暴, 但问题也很多:URL一长看着就恶心; 我晕... header参数容易被忽略;最重要的是每次改版本都要上线新的代码!这对于频繁迭代的小团队来说简直是噩梦。

为什么需要灰度发布

所以说啊!我们需要一种更优雅的方式来管理API版本和进行平滑过渡。这就是灰度发布的用武之地。通过逐步将流量导向新版本API, 补救一下。 我们可以及时发现潜在的问题并进行修复,而不会影响到所有用户。

核心思想

我们的核心思想是:利用SpringMVC的底层机制对RequestMappingInfo进行定制化。简单来说就是给每个方法打个注解@PathRouterDecisionMaker,然后在请求处理过程中根据这个注解来决定走哪个版本的API,歇了吧...。

@PathRouterDecisionMaker 注解

@Target
@Retention
public @interface PathRouterDecisionMaker {
    Class decision; // 决策器类
    String resourceCondition default "";          // 资源条件
    int order default 0;                          // 施行顺序
}

实现原理

要说这个原理啊...其实挺复杂的...我尽量说清楚点吧,太治愈了。。

1、 AbstractRequestCondition:这是一个抽象类

实现了 RequestCondition 接口,并提供了一1些默认实现。它简化了自定义条件的实现过程。 2、RequestCondition:这是一个接口,定义了用于匹配请求的条件。它包含两个主要方法: - getMatchingCondition:返回与给定请求匹配的条件。- combine:将当前条件与其他条件组合。

2、 进入到AbstractHandlerMethodMapping#lookupHandlerMethod

扯后腿。 预加载是:通过MappingRegistry,将原API和灰度API的RequestMappingInfo信息,注册到mappingLookup这个Map里。 产品名称 功能 价格 某监控系统 实时监控、 告警 ¥999/年 某日志分析平台日志收集、分析¥1499/年

3、返回methodHandler

服务运行时获取methodHandler时会回调 WebRouterDeci 切记... sionCondition#getMatchingCondition 方法.

摆烂。 沿着后面的链路一直debug会回调到 WebRouterConstraintCondition#getMatchingCondition 方法

核心组件

  • WebMvcRegistrationsConfig:负责注册自定义的 RequestMappingHandlerMapping
  • WebRouterPathConstraintMatcher:负责提取 PathRouterDecisionMaker 注解元信息
  • WebRouterDecisionMakerDetection: 负责检测方法上的PathRouterDecisionMaker注解

代码分析

public interface RouterDecisionMaker { /** * 路由决策器的到头来决策方法 * @param pathPartRequest * @return 匹配返回的资源类型 */ boolean matches;}
这里面会逐个RequestMappingInfo校验是否匹配成功

具体步骤

public class RouterPathRequest { private final String pattern; private final String url; private final Map pathVariables; private final RouterPatternKey routerPatternKey; private final String routeCondition; private final HttpServletRequest request; public RouterPathRequest { = request; = pattern; = pathVariables; = url; = routerPatternKey; = routeCondition;} public static RouterPathRequest build { return new RouterPathRequest; } //...getter&setter}
@RestControllerpublic class ConstraintController { @PathRouterDecisionMaker @GetMapping public String test { return "非灰度:老API.."; } @PathRouterDecisionMaker @GetMapping public String test2 { return "灰度:新API.."; }}

遇到的问题及解决方案

本系管理统主要采用java技术开发服务端主要用到了spring+springmvc+mybatis+dubbo前端采用easyui数据库采用mysql灰度引擎当前采用java和lua两种技术开发本人也是出于这种冲动写下了一套灰度系统将其开源出来希望能够为小公司提供一种解决方案希望能够作为大公司灰度更好参考
那么是否有比较好解决方案协助我们完成同名同方法同参数列表的API灰度动态路由方案呢?
Java 微服务仍使用 RestTemplate/Feign 调用 SCG-Mesh 网关地址网关按请求头 x-env gray 动态路由至灰度版本Sidecar注入需根据springprofilesactive 的实际运行值动态决策而非构建时静态配置该方案允许 Java 服务在不修改代码不注入 Envoy Sidecar 的前提下通过 SCG-Mesh 统一出口流量并与 Istio 控制平面协同完成服务发现TLS 终止与策略治理.

基于SpringMVC的API灰度方案

哎,说实话,搞API灰度发布这玩意儿,一开始真是头大。各种方案,各种配置,让人眼花缭乱。特别是咱们这种小公司, 人员有限,搞那些复杂的Istio、Service Mesh什么的,简直是mission impossible。后来想了想,与其追逐潮流,不如脚踏实地,利用现有的技术栈搞一套简单的、可控的方案。于是乎,就有了这个基于SpringMVC的API灰度发布方案。

背景与问题

在快速迭代的产品开发中,API接口的更新迭代是常态,只是,旧版本接口往往已经有大量用户依赖,无法马上停用,那么如何在不影响现有用户的前提下平滑引入新版本API?,太离谱了。

传统API版本管理方案及不足

以前我们都是怎么搞的呢?要么在URL上加版本号,要么在header里加个version参数。这种方式简单粗暴, 但问题也很多:URL一长看着就恶心; 我晕... header参数容易被忽略;最重要的是每次改版本都要上线新的代码!这对于频繁迭代的小团队来说简直是噩梦。

为什么需要灰度发布

所以说啊!我们需要一种更优雅的方式来管理API版本和进行平滑过渡。这就是灰度发布的用武之地。通过逐步将流量导向新版本API, 补救一下。 我们可以及时发现潜在的问题并进行修复,而不会影响到所有用户。

核心思想

我们的核心思想是:利用SpringMVC的底层机制对RequestMappingInfo进行定制化。简单来说就是给每个方法打个注解@PathRouterDecisionMaker,然后在请求处理过程中根据这个注解来决定走哪个版本的API,歇了吧...。

@PathRouterDecisionMaker 注解

@Target
@Retention
public @interface PathRouterDecisionMaker {
    Class decision; // 决策器类
    String resourceCondition default "";          // 资源条件
    int order default 0;                          // 施行顺序
}

实现原理

要说这个原理啊...其实挺复杂的...我尽量说清楚点吧,太治愈了。。

1、 AbstractRequestCondition:这是一个抽象类

实现了 RequestCondition 接口,并提供了一1些默认实现。它简化了自定义条件的实现过程。 2、RequestCondition:这是一个接口,定义了用于匹配请求的条件。它包含两个主要方法: - getMatchingCondition:返回与给定请求匹配的条件。- combine:将当前条件与其他条件组合。

2、 进入到AbstractHandlerMethodMapping#lookupHandlerMethod

扯后腿。 预加载是:通过MappingRegistry,将原API和灰度API的RequestMappingInfo信息,注册到mappingLookup这个Map里。 产品名称 功能 价格 某监控系统 实时监控、 告警 ¥999/年 某日志分析平台日志收集、分析¥1499/年

3、返回methodHandler

服务运行时获取methodHandler时会回调 WebRouterDeci 切记... sionCondition#getMatchingCondition 方法.

摆烂。 沿着后面的链路一直debug会回调到 WebRouterConstraintCondition#getMatchingCondition 方法

核心组件

  • WebMvcRegistrationsConfig:负责注册自定义的 RequestMappingHandlerMapping
  • WebRouterPathConstraintMatcher:负责提取 PathRouterDecisionMaker 注解元信息
  • WebRouterDecisionMakerDetection: 负责检测方法上的PathRouterDecisionMaker注解

代码分析

public interface RouterDecisionMaker { /** * 路由决策器的到头来决策方法 * @param pathPartRequest * @return 匹配返回的资源类型 */ boolean matches;}
这里面会逐个RequestMappingInfo校验是否匹配成功

具体步骤

public class RouterPathRequest { private final String pattern; private final String url; private final Map pathVariables; private final RouterPatternKey routerPatternKey; private final String routeCondition; private final HttpServletRequest request; public RouterPathRequest { = request; = pattern; = pathVariables; = url; = routerPatternKey; = routeCondition;} public static RouterPathRequest build { return new RouterPathRequest; } //...getter&setter}
@RestControllerpublic class ConstraintController { @PathRouterDecisionMaker @GetMapping public String test { return "非灰度:老API.."; } @PathRouterDecisionMaker @GetMapping public String test2 { return "灰度:新API.."; }}

遇到的问题及解决方案

本系管理统主要采用java技术开发服务端主要用到了spring+springmvc+mybatis+dubbo前端采用easyui数据库采用mysql灰度引擎当前采用java和lua两种技术开发本人也是出于这种冲动写下了一套灰度系统将其开源出来希望能够为小公司提供一种解决方案希望能够作为大公司灰度更好参考
那么是否有比较好解决方案协助我们完成同名同方法同参数列表的API灰度动态路由方案呢?
Java 微服务仍使用 RestTemplate/Feign 调用 SCG-Mesh 网关地址网关按请求头 x-env gray 动态路由至灰度版本Sidecar注入需根据springprofilesactive 的实际运行值动态决策而非构建时静态配置该方案允许 Java 服务在不修改代码不注入 Envoy Sidecar 的前提下通过 SCG-Mesh 统一出口流量并与 Istio 控制平面协同完成服务发现TLS 终止与策略治理.