likes
comments
collection
share

好家伙, 灰度发布 还这么多细节

作者站长头像
站长
· 阅读数 34

这前天开个会 结果就说 灰度发布 优先级高,行, 同志们说得对。我这很迷茫啊,网关代码在别的部门那管,就没见过,只能靠猜想。。。。难受 算了,先干活

背景

其实就是优雅关机 流量切换不能有临时的接口不可用(ribbon 要有重试机制,服务通知要即时,可以看阿里云有一个 讲流量切换的 视频),然后就是 升级v2之后需要先验证一下,然后流量再切到v2,防止业务不可用的情况(具体原因没了解,应该是被用户说了。。。。)

思考题 带着问题看

看完这篇文章 你还有哪些部分是 有疑问的 我先说

  1. nacos 配置实时生效 你说实时就实时啊,底层根据什么做的,如果一台业务服务 网络临时不好,后面恢复了,怎么补偿的?
  2. aop @After @AfterReturning @AfterThrowing 你说是分开的 你怎么证明的?底下怎么执行的
  3. aop 中的threadlocal 你说 不清除 会对系统有影响,什么影响,有什么现象?为什么?
  4. 多线程 操作一个对象,会有数据一致性问题,除了你这里的 不可变类 还有别的办法么? 有什么区别呢,有什么应用场景呢?

同志们 想了解哪个 评论区告诉下 我后面整理 发出来

技术调研

灰度发布这个东西 我之前没写过,只是听说,我之前的理解 给大家画个图

好家伙, 灰度发布 还这么多细节

百度一下 灰度发布,什么玩意,你告诉我改改gateway 改改feign,v2项目启动 带上元数据,就这?闭环呢?

基础规范

  1. 涉及到 版本的选择,那之后 元数据 每个都加上version: 具体版本号 这不就行了,避免了改元数据 可能导致的问题

需要确定问题

  1. 开发流程 后台发布灰度之后 前端先测试 灰度 再上线,确定了想法,统一了思路,才能开始干活嘛 (问前端大哥)

之前有一版方案 参考下

参考之后 感觉之前的方案有点过于复杂,有一些无用功,进行缩减

好家伙, 灰度发布 还这么多细节

思考

其实灰度发布 变为了 配置的统一管理

现在需要解决的问题

  1. 配置文件的管理 从2个方案进行选择 (实现 配置实时生效, spring.cloud.nacos.config.shared-configs 如果要添加 你让我每个服务 添加重新发版 我不愿意
  2. 网关的修改
  3. feign的修改

调研

配置实时生效 nacos

@NacosConfigurationProperties(dataId = "gray.yaml", prefix = "xxx.gray", autoRefreshed = true)

本来想用bus 但是问了一句现在的环境 还不能用,先用这个注解,让其自动更新,feign 灰度配置 实时生效

spring.cloud.nacos.config.shared-configs jar包里添加监听 公共文件

我第一印象 如果nacos 开发者想让别人扩展他 会加一个@ConditionalOnMissingBean 这样我只要定义了 @Bean 就能 覆盖旧的了。 找一找

@Bean
public NacosPropertySourceLocator nacosPropertySourceLocator(
      NacosConfigManager nacosConfigManager) {
   return new NacosPropertySourceLocator(nacosConfigManager);
}
@Override
public PropertySource<?> locate(Environment env) {
   ... 省略

   CompositePropertySource composite = new CompositePropertySource(
         NACOS_PROPERTY_SOURCE_NAME);
    //这个是读取 公共文件的
   loadSharedConfiguration(composite);
   loadExtConfiguration(composite);
   loadApplicationConfiguration(composite, dataIdPrefix, nacosConfigProperties, env);
   return composite;
}
//你告诉我 直接获取的 配置文件的sharedConfigs  没给我留什么活口啊,怎么办呢
List<NacosConfigProperties.Config> sharedConfigs = nacosConfigProperties
      .getSharedConfigs();

办法

  1. 直接在我们自己 灰度发布的组件jar中 使用这个nacos 对象进行指定文件加载
  2. 让我们在这个nacos Config加载之前 我往sharedConfigs 属性中 添加一个 gray.yaml
  3. 直接用 nacos 开源的监听方式 监听

第一种办法 可以看到 这个nacos 类都是private 人家本来就没想让你自己用

第二种办法 可以看到 人家@Order(0) 本来就是让先执行的。。。没必要硬改人家

第三种办法 可行 可以搞,但是需要考虑 如果nacos 连接不上,会不会导致超时,代码走不动。

办法肯定有,设置一个超时时间,然后日志打印下,方便让开发人员 定位问题。

我是默认超时时间 5s ,想想 如果超时 没获取,会不会影响 操作,后面会自己补齐么? 这个需要看代码了

如果超时 就会报一个 NacosException 进行拦截处理

行 配置文件 nacos 这块先这样,后面再继续改

说到这了,可能有人会想

会想 @Order Ordered 接口哪个优先级更高

这里就不测试了,结论是 Ordered 接口的优先生效, 最后才是 @Order

网关的修改

feign 改进

这部分 改动的功能

  1. 支持请求头的传递
  2. 灰度发布

支持请求头的传递

百度下

@Slf4j
public class CustomFeignRequestInterceptor implements RequestInterceptor {

    /**
     * 这里可以实现对请求的拦截,对请求添加一些额外信息之类的
     *
     * @param requestTemplate
     */
    @Override
    public void apply(RequestTemplate requestTemplate) {
        // 1. obtain request
        final ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

        // 2. 兼容hystrix限流后,获取不到ServletRequestAttributes的问题(使拦截器直接失效)
        if (Objects.isNull(attributes)) {
            log.error("MyFeignRequestInterceptor is invalid!");
            return;
        }
        HttpServletRequest request = attributes.getRequest();

        // 2. obtain request headers,and put it into openFeign RequestTemplate
        Enumeration<String> headerNames = request.getHeaderNames();
        if (Objects.nonNull(headerNames)) {
            while (headerNames.hasMoreElements()) {
                String name = headerNames.nextElement();
                String value = request.getHeader(name);
                requestTemplate.header(name, value);
            }
        }
    }
}

feign 灰度发布 需要实现的功能

//判断灰度参数 是否存在 不存在直接return

//调用 远程 判断 灰度是否生效

//存在就继续往下走

//@FeignClient 先判断是否有 url,如果有 直接 使用url 不让负载均衡生效

//name value 都会触发负载均衡

//获取 调用的服务列表 根据元数据 进行过滤出可以调用的服务

//如果没有 直接随机选一个

//如果有 从 灰度服务列表 选一个具体服务

//进行ip:port 替换

代码之后放码云

feign 修改 需要注意

feign threadlocal 别忘了删除

因为 @After @AfterReturning @AfterThrowing 是分开的,所以3个方法都要remove

@After(value = "anyMehtod()")
public void after(){
    RibbonParameters.remove();
}

@AfterReturning(pointcut="anyMehtod()",returning = "rst")
public void afterRunning(Response rst){
    RibbonParameters.remove();
}

@AfterThrowing(value = "anyMehtod()")
public void afterThrowing(){
    RibbonParameters.remove();
}

feign 选择服务的时候 兼容指定url 就不能走选举了

除此之外 异常情况 大不了随机拿一个 也别报错

需要解决的细节

  1. 配置一致性 到模块 添加一个模块的概念 方便配置的扩展性
  2. 多线程数据一致性问题 因为一个类有多个属性,在多线程操作的时候 可能读写同时存在的情况下,可能导致 属性 分别属于不同的对象? 怎么办呢? 使用不可变类
  3. 怎么知道有没有 走灰度? 目前1.0 不是关键功能 先推迟
  4. nacos 配置错误 直接置为false 日志打印,但是不影响系统。 外面做一层拦截 打印错误日志 如果报错 就恢复为默认的灰度配置,关闭灰度
  5. nacos 配置解析 有可能原来规定是map 结果你给我传int 没事,instanceof 判断

不可变类 粘贴一个例子

@Data
public final class WholeProperties {
    /**
     * 灰度发布 开关 true 是开启 false 是关闭
     */
    private final boolean grayFlag;
    /**
     * 现在整体 的所有数据
     */
    private final Map<String, Object> map;

    /**
     * 本模块 的属性
     */
    private final Map<String, Object> customParamMap;

    public WholeProperties(boolean grayFlag, Map<String, Object> map, Map<String, Object> customParamMap) {
        this.grayFlag = ObjectUtils.isEmpty(grayFlag) ? false : grayFlag;
        this.map = ObjectUtils.isEmpty(map) ? new ConcurrentHashMap<>() : map;
        this.customParamMap = ObjectUtils.isEmpty(customParamMap) ? new ConcurrentHashMap<>() : customParamMap;
        ;
    }



}

写组件的时候 额外注意的

  1. 版本管理 请使用 ,不要手改,求求了
  2. 上线的版本号 必须是releases ,哎 一言难尽啊,一句话忘说就搞事

总结

feign 的改动基本就是这些,交流可以加wx yuyezhiji

转载自:https://juejin.cn/post/7138248134215335973
评论
请登录