扩展Zuul实现敏捷开发的小小技巧
分析下目前遇到的痛点
你在开发工作的是否遇到这个问题,微服务模块划分过细,基础模块依赖的比较多?
比如你要进行微服务开发则需要启动以下基础模块
- 注册中心(eureka)
- 配置中心(spring cloud config)
- 网关(zuul)
- 认证中心(oauth)
- ...
如上图红色标注的服务模块,而你需要编码或者需要的只有那么一个业务微服务模块,本地启动这么微服务模块对开发机器的要求性能较高,并且影响开发效率。
为了解决这种问题,团队一般都会把通用的基础模块部,提供统一的开发环境,方便大家开发,如上图 只需要考虑你的业务模块(serviceA、serviceB) 即可,提高开发效率。
这种统一开发基础环境问题存在小小的问题,比如当开发A维护serviceA,开发B维护serviceB 不会出现冲突;如果开发A、B同时维护一个模块时候,就会出现冲突如下图所示:A的本地请求会被路由到B正在开发的biz-service,而不是目标的A的biz-service,因为zuul 是更具服务名称进行路由的
解决原理
重写网关的转发规则,其实就是重写ribbon的路由规则,根据客户端不同的用户请求(可以根据入参不同区分)路由到对应的微服务上,所以对应的微服务上要打上标签。biz-service A开发版本,biz-service B开发版本
代码实现
1. 在微服务的eureka客户端声明版本所属
eureka: instance: metadata-map: version: v1.0 # A的开发版本
2. 自定义ribbon 的断言
/** * @author lengleng * @date 2018/10/16 * <p> * 路由微服务断言 * <p> * 1. eureka metadata 存在版本定义时候进行判断 * 2. 不存在 metadata 直接返回true */ @Slf4j public class MetadataCanaryRuleHandler extends ZoneAvoidanceRule { @Override public AbstractServerPredicate getPredicate() { return new AbstractServerPredicate() { @Override public boolean apply(PredicateKey predicateKey) { String targetVersion = RibbonVersionHolder.getContext(); RibbonVersionHolder.clearContext(); if (StrUtil.isBlank(targetVersion)) { log.debug("客户端未配置目标版本直接路由"); return true; } DiscoveryEnabledServer server = (DiscoveryEnabledServer) predicateKey.getServer(); final Map<String, String> metadata = server.getInstanceInfo().getMetadata(); if (StrUtil.isBlank(metadata.get(SecurityConstants.VERSION))) { log.debug("当前微服务{} 未配置版本直接路由"); return true; } if (metadata.get(SecurityConstants.VERSION).equals(targetVersion)) { return true; } else { log.debug("当前微服务{} 版本为{},目标版本{} 匹配失败", server.getInstanceInfo().getAppName() , metadata.get(SecurityConstants.VERSION), targetVersion); return false; } } }; } }
3. 版本上下文TTL
public class RibbonVersionHolder { private static final ThreadLocal<String> context = new TransmittableThreadLocal<>(); public static String getContext() { return context.get(); } public static void setContext(String value) { context.set(value); } public static void clearContext() { context.remove(); } }
4. 初始化ribbon 路由配置
@Configuration @ConditionalOnClass(DiscoveryEnabledNIWSServerList.class) @AutoConfigureBefore(RibbonClientConfiguration.class) @ConditionalOnProperty(value = "zuul.ribbon.metadata.enabled") public class RibbonMetaFilterAutoConfiguration { @Bean @ConditionalOnMissingBean @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public ZoneAvoidanceRule metadataAwareRule() { return new MetadataCanaryRuleHandler(); } }
5. 客户端调用时 header 传入版本,以 axios 为例
axios.interceptors.request.use(config => { NProgress.start() // start progress bar if (store.getters.access_token) { config.headers['Authorization'] = 'Bearer ' + token config.headers['version'] = 'v1.0' // 开发人员自己的版本标志,对应eureka metadata 配置 } return config }
总结
- 扩展ribbon 的路由规则,根据客户端来去不同版本的服务,也可以理解为灰度发布。
- 生产环境可以借助Kong、Traefik 集合zuul 来实现灰度发布
- 代码请参考微服务权限框架pig的灰度发布功能,已经全部开源
- 关于pig:
基于Spring Cloud、oAuth2.0开发基于Vue前后分离的开发平台,支持账号、短信、SSO等多种登录,提供配套视频开发教程。
https://gitee.com/log4j/pig
相关推荐
匡文韬 2020-08-18
黄河敏捷开发 2020-08-17
hraceo 2020-07-24
wuxiaaa 2020-07-01
匡文韬 2020-06-21
coolham 2020-06-08
黄河敏捷开发 2020-06-05
黄河敏捷开发 2020-06-02
黄河敏捷开发 2020-05-25
hufeng00 2020-05-22
园搬家测试账号 2020-05-01
uuussseeennn 2020-04-23
xiangnan0 2020-04-23
uuussseeennn 2020-04-22
星空下的程序猿 2020-04-18
uuussseeennn 2020-04-15
yca 2020-04-14
uuussseeennn 2020-03-27