Spring Cloud Gateway双版本实现的最大复用性方案咨询
嘿,这个场景我太熟悉了——既要遵守公司“禁用RC/Beta版”的规定赶生产,又得提前做2.0版本的方案,还要避免重复造轮子,直接搞两个独立项目绝对是给自己挖维护坑!咱们用模块化拆分+适配器模式就能完美解决,核心就是把「业务逻辑」和「SCG版本相关的适配代码」彻底拆分开,具体方案如下:
模块化复用方案:核心逻辑共享,版本适配隔离
1. 抽离与SCG版本无关的核心业务模块
首先创建一个独立的gateway-core模块,把所有和SCG版本无关的业务逻辑都塞进去:
- 限流的业务规则(比如基于用户/IP的阈值计算、令牌桶算法实现)
- 认证授权的核心逻辑(token解析、权限校验、用户信息获取)
- 通用的日志处理、异常统一封装、路由规则的业务解析(比如从配置中心拉取路由后的通用处理)
- 注意:这个模块绝对不能依赖任何SCG的API,所有方法的入参出参都用自定义的POJO或者通用接口,比如定义
AuthResult、LimitResult,而不是直接用SCG的ServerWebExchange。
2. 为两个SCG版本分别实现适配器模块
接下来针对两个版本做“适配层”,把核心模块的逻辑转换成SCG能识别的Filter形式:
针对SCG 1.0.1.RELEASE的适配器(gateway-adapter-scg1x)
- 依赖
gateway-core和SCG 1.x的官方依赖(比如spring-cloud-starter-gateway的1.0.1版本) - 实现SCG 1.x的
GatewayFilter或GlobalFilter,在Filter里调用gateway-core的方法,比如在认证Filter里:public class Scg1xAuthFilter implements GlobalFilter { private final AuthService authService; // 从gateway-core注入 @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 把SCG的请求上下文转换成core模块能接受的参数 String token = exchange.getRequest().getHeaders().getFirst("Authorization"); AuthResult result = authService.validateToken(token); if (!result.isValid()) { return exchange.getResponse().setComplete(); // 处理未授权 } // 把用户信息放入上下文,继续执行链路 exchange.getAttributes().put("userInfo", result.getUserInfo()); return chain.filter(exchange); } } - 这里只做“适配”,不写业务逻辑,所有业务判断都交给
gateway-core。
针对SCG 2.0.0.RC1的适配器(gateway-adapter-scg2x)
- 依赖
gateway-core和SCG 2.x的官方依赖 - 利用SCG 2.x的特性,比如实现
GatewayFilterFactory,这样可以直接在YML里配置使用:public class Scg2xAuthGatewayFilterFactory extends AbstractGatewayFilterFactory<Scg2xAuthGatewayFilterFactory.Config> { private final AuthService authService; @Override public GatewayFilter apply(Config config) { return (exchange, chain) -> { // 同样调用gateway-core的认证逻辑 String token = exchange.getRequest().getHeaders().getFirst("Authorization"); AuthResult result = authService.validateToken(token); if (!result.isValid()) { return exchange.getResponse().setComplete(); } return chain.filter(exchange); }; } // 定义配置类,对应YML里的配置项 public static class Config { // 比如是否开启认证等配置 private boolean enabled; // getter/setter } } - 对于SCG 2.x支持的YML配置,把核心逻辑包装成对应的Factory,最大化利用新版本的特性,同时不重复写业务代码。
3. 轻量启动模块:一键切换版本
最后创建两个极简的启动模块,作为不同版本的入口:
gateway-boot-scg1x:只依赖gateway-adapter-scg1x,包含SCG 1.x的基础配置(比如路由定义)和启动类,没有任何业务逻辑。gateway-boot-scg2x:只依赖gateway-adapter-scg2x,包含SCG 2.x的YML配置(比如用适配器模块的FilterFactory配置路由)和启动类。
想要切换版本?直接启动对应的boot模块就行,或者在构建时指定对应的profile,完全不用改核心代码。
关键优化细节
- 用BOM统一版本管理:在父POM里引入Spring Cloud对应的BOM(SCG 1.x对应Finchley版本,2.x对应Greenwich),适配器模块分别继承对应的BOM,避免版本冲突。
- 抽象通用请求上下文:如果两个版本的
ServerWebExchange差异较大,可以在gateway-core里定义一个GatewayRequestContext接口,适配器模块负责把SCG的上下文转换成这个通用接口,让核心模块彻底脱离SCG依赖。 - 配置复用:把通用配置(比如认证中心地址、限流阈值)放到
gateway-core的application-common.yml里,两个boot模块通过spring.profiles.include=common引入,版本专属配置放在各自的boot模块中。 - 测试复用:
gateway-core的单元测试只测业务逻辑,适配器模块的测试只测适配逻辑,这样大部分测试代码可以共享,不用重复编写。
为什么不搞两个独立项目?
- 重复代码噩梦:如果搞两个项目,核心的认证、限流逻辑要写两遍,后期改需求得同时改两个项目,极易出现不一致。
- 切换成本高:想测新版本功能,得复制粘贴代码,或者手动同步修改,效率极低。
- 升级困难:等SCG 2.x正式版发布后,两个独立项目都要修改升级,而用模块化方案只需要更新适配器模块的依赖版本即可。
内容的提问来源于stack exchange,提问作者Sam
相关产品推荐
相关产品推荐

