共计 5344 个字符,预计需要花费 14 分钟才能阅读完成。
前言
feign 是一个杰出的 Http 申请客户端封装框架,feign-hystrix 是整个框架体系里的其中一个模块,用来集成 hystrix 熔断器的,feign 和 hystrix 这两个我的项目都是 Netflix 开源的(openfeign 已独立迭代)。在 spring boot 我的项目中,能够应用 spring-cloud-starter-openfeign 模块,无缝集成 feign 和 hystrix。然而,hystrix 默认采纳的 Archaius 来驱动 hystrix 的配置零碎,无缝集成的同时,也会把 archaius-core 给引入进来。archaius 是一个配置核心我的项目,相似 spring cloud config 和 apollo,如果 archaius 只是作为 hystrix 配置的驱动,我的项目启动时会打印烦人的正告日志,提醒你没有配置任何动静配置源。当我的项目里曾经采纳了 apollo 时,能够间接剔除掉 Archaius,他们的功能定位高度重合了。间接剔除依赖,会导致本来配置在 spring 中的配置不失效,博主也是在不小心剔除后,遇到了配置不失效的问题,才有了本篇博文,记录下过程。只有稍加改变,联合 apollo 配置动静下发能力,能够做到 hystrix 的配置实时动静失效。
- feign:https://github.com/OpenFeign/…
- hystrix:https://github.com/Netflix/Hy…
- archaius:https://github.com/Netflix/ar…
- apollo:https://github.com/ctripcorp/…
archaius 正告日志
2020-12-10 11:19:41.766 WARN 12835 --- [main] c.n.c.sources.URLConfigurationSource : No URLs will be polled as dynamic configuration sources.
2020-12-10 11:19:41.766 INFO 12835 --- [main] c.n.c.sources.URLConfigurationSource : To enable URLs as dynamic configuration sources, define System property archaius.configurationSource.additionalUrls or make config.properties available on classpath.
2020-12-10 11:19:41.772 WARN 12835 --- [main] c.n.c.sources.URLConfigurationSource : No URLs will be polled as dynamic configuration sources.
2020-12-10 11:19:41.772 INFO 12835 --- [main] c.n.c.sources.URLConfigurationSource : To enable URLs as dynamic configuration sources, define System property archaius.configurationSource.additionalUrls or make config.properties available on classpath.
咱们遇到的问题
在一次系统优化重构中,博主给整个我的项目来了一个 360 的大瘦身,把所有的未应用的依赖通通给挪走了。其中就包含了 spring-cloud-starter-openfeign 模块的 archaius-core 依赖。因为咱们曾经应用了 apollo 配置核心,archaius 在这个我的项目里显得很多余,而且还会打印烦人的正告日志。所以就间接排除了,如:
implementation ('org.springframework.cloud:spring-cloud-starter-openfeign'){exclude(module:"archaius-core")
}
为此,专门理解了下 archaius 的来历,并且针对 feign 的熔断器的 Fallback 能力进行了测试,所有运行失常。上线一周后,问题裸露进去了,共事反馈,hystrix 的配置如同不失效了。景象是,本来设置的 hystrix 线程执行不超时,却产生了很多执行一秒就超时了,咱们的要害配置如下(这不是一个很好的配置示范,前面会调整更细粒度管制):
# 禁止执行超时
hystrix.command.default.execution.timeout.enabled = false
直观感觉就是这个配置不失效了,联想到 archaius-core 被移除,所以先立马复原了依赖,从新打包上线,问题解决。就这?为了彻底搞清楚 Hystrix 的配置加载过程,咱们对 feign 整合 hystrix 进行了全面的理解。
hystrix 在 feign 中的加载过程
在 spring-cloud-starter-openfeign 的封装下,应用起来非常简单,然而外部的加载流程非常复杂。所以博主也不打算全面铺开来说这块内容,有机会会独立一篇来说。这里依据咱们上文遇到的禁用执行超时不失效的问题,博主总结了加载流程中的几个要害的中央:
Feign 和 Hystrix 的桥接器 Feign-Hystrix
这个我的项目是 feign 和 hystrix 的桥接器,通过这样的一个桥接器,将两个框架的 api 能力整合在了一起,上面简要论述下,加载过程要害类的作用:
- SetterFactory:承载了结构 HystrixCommand 实例的所有的配置的接口,有一个默认实现 Default,在上面会用到,是自定义配置实现的突破口
- HystrixInvocationHandler:这是一个实现了 JDK 代理接口类,用来代理 Feign 最终的执行,HystrixCommand 类就是在这个实例里被结构执行的,应用的构造方法正是带入参 Setter 的构造方法,集成方会实现 SetterFactory 来结构 Setter。调试程序时咱们将端点打进这个类里,就能够看到配置加载的状况
spring boot 主动加载 hystrix
@Configuration
@ConditionalOnClass({HystrixCommand.class, HystrixFeign.class})
protected static class HystrixFeignConfiguration {
@Bean
@Scope("prototype")
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "feign.hystrix.enabled")
public Feign.Builder feignHystrixBuilder() {return HystrixFeign.builder();
}
}
这里是 Hystrix 在 feign 框架下加载的总入口。这个默认的构建器 Builder 中,有一个默认实现的 SetterFactory,这个 SetterFactory 专门负责传递参数给 Hystrix 初始化 HystrixCommand 用。能够看到这里 Bean 的实例化加上了 @ConditionalOnMissingBean 条件束缚,既咱们能够自定义实现 Hystrix 的结构器,笼罩这里的实现,在自定义的结构器中,能够通过自定义实现 SetterFactory,来注入任意的配置。这个是实现 Hystrix 配置自定义加载的形式之一,不过不举荐,没必要毁坏 spirng 现有的这种构造,而且代码也会比拟简短 (上面{…} 省略了一百多行配置解决代码,用来兼容 Hystrix 现有配置定义),看起来如下:
Hystrix 的动静兜底配置
配置是 hystrix 的外围,各种策略的抉择执行都须要配置来驱动,所以,尽管在利用层面不须要太多的配置设置,然而必要的配置 hystrix 都会填充一个默认值,比方,hystrix 默认执行超时设置的 1s。Hystrix 中的配置有三个档次的加载优先级,如:
- 最先加载 Setter:Setter 是用户传递给 Hystrix 结构器的,所以优先级别最高
- 其次加载动静配置源:如果必要的配置在 Setter 里没有找到,则在动静配置源中获取
- 最初加载默认配置:如果动静配置源中也没有找到配置,则采纳默认的配置
其中动静配置源,有一个基于 SystemProperties 的配置实现 HystrixDynamicPropertiesSystemProperties。HystrixCommand 在实例化时,如果用户没有给到具体的配置,Hystrix 每次都会去 SystemProperties 中寻找配置。也就是说,咱们能够通过 - D 参数注入任意 Hystrix 的配置参数,都会失效。有了这个个性,能够非常简单的联合 apollo,达到 hystrix 配置动静失效的成果,而且所有配置兼容 Hystrix 本来的配置。
apollo 配置驱动 Hystrix
实现这个性能的要害是。零碎初始化时,将 hystrix.command 前缀相干的配置从 apollo 中获取到而后通通注入 SystemProperties。配置更新时,同时更新 SystemProperties 中的配置即可,非常简单,用代码谈话:
/**
* @author kl (http://kailing.pub)
* @since 2020/12/10
*/
@Slf4j
@Configuration
@AutoConfigureBefore(value = {FeignClientsConfiguration.class, FeignAutoConfiguration.class})
public class HystrixConfiguration{
public static final String DYNAMIC_TAG = "dynamic.";
public static final String DYNAMIC_PREFIX = DYNAMIC_TAG + "hystrix.command.";
public static final String PREFIX = "hystrix.command.";
@ApolloConfig
private Config config;
@PostConstruct
public void initHystrix(){
this.config.addChangeListener(event -> this.loadHystrixConfig(event.changedKeys()),
null,
Sets.newHashSet(DYNAMIC_PREFIX)
);
this.loadHystrixConfig(config.getPropertyNames());
}
private void loadHystrixConfig(Set<String> configkyes) {
configkyes.forEach(key -> {if (StringUtils.containsIgnoreCase(key, PREFIX)) {String value = config.getProperty(key, null);
String realKey = key.replaceAll(DYNAMIC_TAG,"").trim();
System.setProperty(realKey, value);
log.info("Hystrix config: {}={}", key, value);
}
});
}
}
这里留神一个问题:为啥这里多设计了一个 dynamic. 前缀的配置,这是因为博主在测试过程中触发了 apollo 配置监听器暗藏的问题,导致 Apollo 的动静监听器不失效了。Apollo 配置加载是以 SystemProperties 为最高优先级的,当配置发生变化时,apollo 会将 SystemProperties 笼罩到配置之后,才比拟本次配置公布是否有更新。因为咱们一开始就将相干的配置加载到 SystemProperties 里了,所以每次变更都会被笼罩成之前的值,导致更新判断生效,始终进不了监听器。如果想要动静更新,就须要保护一份 apollo 的配置和 SystemProperties 里的映射关系,而不能保持一致,这样每次批改 apollo 时,就能够将保护映射关系的前缀去掉,而后将值动静更新到 SystemProperties。目前的设计里,既反对原生的所有配置一次性加载,也反对 dynamic. 前缀拼装原有配置动静加载
配置示例
# 初始化时一次性加载
hystrix.command.default.execution.timeout.enabled = true
#每次批改动静失效
dynamic.hystrix.command.default.execution.timeout.enabled = true
结语
Feign-hystrix 的配置,有了 Apollo,还用 Archaius 吗?当然不必,采纳 apollo 实现计划,既兼容了所有原生配置,还能够做到动静失效,岂不美哉。