一.背景
Hystrix是Netlifx的开源容错框架,是对抗雪崩的利器。具有服务降级、服务融合、依赖隔离、监控等功能。

虽然现在已经不再官方维护Hystrix,并且有了阿里巴巴Sentinel等新的框架选择,但是从组件成熟度和应用案例来看,仍然有很多项目在继续使用Hystrix,我的项目就是其中之一。所以,我想和大家分享一下我个人对Hystrix的实践经验。
二。经验总结
2.1隔离策略的选择
Hystrix提供了两种资源隔离策略,线程池和信号量。它们之间的异同如下:
当请求的服务网络开销相对较高,或者请求相对较好时,我们最好使用线程隔离策略。这样的策略可以保证大量的容器线程可用,不会因为服务原因而一直阻塞或等待,快速失败返回;
在使用缓存时,可以使用信号量隔离策略,因为这种服务响应速度快,不会占用容器线程太长时间,也减少了线程切换的一些开销,提高了服务效率。
使用哪种策略要根据业务场景综合评估。通常,建议使用线程池隔离。
2.2线程池大小和超时设置
在线程池隔离策略下,线程池大小和超时时间的设置非常重要,直接影响系统服务的响应性。如果线程池太大,会造成资源浪费和线程切换。如果设置太小,不支持用户的请求,请求将被排队。但是,如果超时设置得太长,一些长时间请求将阻塞线程,导致其他正常请求排队等待。如果设置太短,会吹掉太多正常请求。
Hystrix的官方建议如下:
也就是说,它被转换成以下计算公式:
线程大小=服务TP99响应时间*每秒请求数+冗余缓冲区值超时= 1000/秒。比如一个服务TP99每秒会收到30个请求,那么每个请求的响应时间是200ms,可以根据上面的公式计算出来:
线程池大小= 0.2 * 30+4= 10,超时= 300毫秒
2.3注释覆盖
在实际开发中,您可能会遇到这样的情况:外部调用方法将Hystrix注释与其他注释一起使用,例如查询方法加缓存注释。此时,应特别注意注释之间的执行顺序,以避免意外结果:
批注无效。此时,Hystrix注释部分的执行在最外层。因为Hystrix内部执行是通过ProceedingJoinPoint.getTarget获取目标对象,并通过反射调用直接在目标对象方法上执行,这就造成了中间其他注释逻辑的丢失。通过指定注释执行顺序@Order解决方案,可以确保Hystrix注释在最内层执行。由于缓存异常,查询方法失败。如果Hystrix注释段的执行在最外层,那么Hystrix fuse管理的方法逻辑除了第三方服务的远程调用之外,还包括缓存调用逻辑。如果缓存调用异常,会被统计为整个方法异常,导致整个方法熔断。2.4服务的异常处理
让我们给你时间看看下面的代码,看看有没有问题:
@ HystrixCommandpublic User queryUserById { if){ throw new biz exception;}结果结果;请尝试{ result = user facade . query byid;} catch { log.error} if){ return result . get data;}返回null}Hystrix会根据运行过程中调用请求的成功率或失败率信息来决定是否开启每个命令依赖型保险丝。如果打开,后续请求将被拒绝。可以看出,异常的控制对Hystrix的运行效果影响很大。
回头看上面的例子,你会发现两个异常处理问题:
非系统调用的异常失败,如无效的参数验证、过时的异常处理、非法的参数验证等。不应该影响熔丝逻辑,也不应该算作故障统计。优化建议是将参数check放在远程调用封装方法之外,或者封装到HystrixBadRequestException中抛出。因为在Hystrix内部逻辑中,HystrixBadRequestException异常默认不统计为失败统计。try-catch远程调用的异常处理。try-catch远程服务的直接调用会直接“吞下”异常,直接导致Hystrix无法获取网络异常等服务不可用异常。建议在处理完catch日志记录后抛出异常。2.5回退方法
Hystrix通过添加回退方法来支持优雅的服务降级,以便在依赖服务调用时返回默认值。但是,回退的使用也有很多需要注意的地方,可以总结如下:
访问级别、参数等。回退方法的应该与相应的依赖服务一致。对于触发回退的异常实例,可以通过回退方法增加Throwable类型参数。回退方法中执行的逻辑尽可能的轻,比如使用本地缓存或者静态默认值来避免远程调用。如果fallback方法中有远程调用,建议用Hystrix将其包装起来,并确保与主命令线程池隔离。对于写操作的远程调用,不建议使用回退来降级写服务的调用失败,可以直接抛给方法调用端进行业务判断。2.6 groupKey、commandKey、threadPoolKey
在Hystrix的开发过程中,你一定见过这三个键,但是很多人并不了解这三个键的意义以及对Hystrix的作用,尤其是threadPooKey,所以这里总结一下:
GroupKey可以通过group key对命令方式进行分组,方便进行Hystrix数据统计、报警和仪表板显示。通常根据远程服务的业务类型来区分,例如定义一个组密钥的帐户服务和定义另一个组密钥的订单服务。由默认值@HystrixCommand批注标记的方法的类名。commandKey的特定命令方法的标识名,常用于设置命令的动态参数。默认值是由@HystrixCommand批注批注的方法名。ThreadPoolKey用于标识该命令所属的线程池。具有相同threadPoolKey的命令使用相同的线程池。如果未指定该键,则默认值为groupKey,即由@HystrixCommand批注标记的方法的类名。在实际项目中,我们会建议尽量用threadPoolKey来指定线程池,而不是用groupKey默认的方式来划分,因为会有某个命令需要和同组的其他命令进行线程隔离的场景,以避免相互影响。2.7参数优先级
默认情况下,Hystrix提供四个级别的参数值配置方法:
全局默认值Hystrix自己的代码默认值,即写死在源代码中的值,在用户没有配置任何参数的情况下生效。例子:执行。isolation . thread . timeoutinmillseconds超时的全局默认值为1000,动态全局默认参数为单位毫秒。此配置参数可以更改全局默认值。示例:属性名hy strix . command . default . execution . isolation . thread . timeoutinmilliseconds实例初始值fuse实例初始值设置的超时值。配置这些参数后,将不再使用默认值。也就是写在代码注释中的属性值。示例:@HystrixProperty动态实例参数可以动态调整一个fuse实例的参数值示例:属性名hystrix . command . hystrixcommandkey . execution . isolation . thread . timeoutinmilliseconds设置的超时值的优先级关系:
动态实例参数>实例初始值>动态全局默认参数>全局默认值
2.8基于组态中心的参数动态配置

Hystrix默认使用Archaius实现动态设置,Archaius默认加载classpath下的config.properties文件。通过在配置文件中添加相应的属性键值,可以动态控制Hystrix的行为。在分布式项目中使用组态中心进行统一配置管理是标准的,因此需要在组态中心的基础上扩展实现Hystrix参数的动态配置功能。
通过跟踪hystrixCommand的创建,发现Hystrix最终是通过HystrixDynamicProperties实现类根据参数属性名获取值,Hystrix本身提供了HystrixDynamicProperties类的扩展机制。参见HystrixPlugins类的367行代码,其中显示Hystrix提供了四种扩展方法:
基于Java SPI机制的Archaius动态属性通过系统参数Hystrix扩展了实现类Hystrix。插件。Hystrixdynamics属性。实施。Hystrix内置了基于Java SPI机制的基于System.getProperty. 2.8.1的Hystrixdynamics属性的实现
基于spi机制的扩展实现依赖于两个类,分别是Hystrixdynamics Properties和HystrixDynamicProperty,其中HystrixDynamicProperties类是需要实现的HystrixDynamicProperties的扩展spi接口,提供了多种获取动态属性的方法。接口定义如下:
公共接口HystrixDynamicProperties { public HystrixDynamicProperty getString;public HystrixDynamicProperty get integer;public HystrixDynamicProperty get long;public HystrixDynamicProperty get boolean;}而HystrixDynamicProperty类专门表示一个参数属性,具有动态改变的能力。该接口定义如下:
公共接口HystrixDynamicProperty扩展了HystrixProperty { public String getName;public void add callback;addCallback方法是实现属性动态变化的核心。正如注释中所解释的,它注册了回调方法,以便在属性改变时动态刷新属性。这种动态刷新逻辑已经在Hystrix内部实现。对于我们来说,只需要在自定义扩展时保存回调,然后在配置中心发生变化时触发对应属性对象的回调方法。
实现步骤如下:
1.定义HystrixDynamicProperty实现类。
完成动态属性类的自定义实现,包括String/Integer/Long/Boolean四种类型的动态属性状态实现。
如上面HystrixDynamicProperty类的描述中所述,当配置中心属性发生变化时,需要保存回调并触发这些属性的回调方法,从而实现属性的动态变化。这个逻辑可以参考观察者模式来设计和实现。
代码如下:
私有抽象静态类CustomDynamicProperty实现HystrixDynamicProperty,property observer { protected final String name;受保护的最终T默认值;受保护列表回调;受保护的CustomDynamicProperty { this . name = propName;this . default value = default value;PropertyObserverManager.add} @覆盖公共字符串getName { return name} @ Override public void add callback { if callbacks = new ArrayList;this . callbacks . add;} @覆盖公共字符串keyName { return name} @ Override public void update { if . equals)){ for { r . run;} } } }私有静态类StringDynamicProperty扩展CustomDynamicProperty { protected StringDynamicProperty { super;} @覆盖公共字符串get { return config manager . getstring;} }私有静态类IntegerDynamicProperty扩展了CustomDynamicProperty { protected IntegerDynamicProperty { super;} @Override公共整数get { String config value = config manager . get;if){ return integer . value of;}返回defaultValue} }私有静态类LongDynamicProperty扩展了customdynamic property { protected long dynamic property { super;} @覆盖public Long get { String config value = config manager . get;if){ return long . value of;}返回defaultValue} }私有静态类BooleanDynamicProperty扩展了CustomDynamicProperty { protected BooleanDynamicProperty { super;} @覆盖public Boolean get { String config value = config manager . get;if){ return boolean . value of;}返回defaultValue} } config manager类默认暂时是配置中心的配置管理类,提供参数采集、参数监听器等功能。PropertyObserver类和PropertyObserverManager类是参照观察者模式的定义实现的,负责观察者的注册和通知管理,从而完成动态属性和配置中心变更通知的联动。这两个类实现起来相对简单,所以不再赘述。
2.定义HystrixDynamicProperties的实现类
基于步骤1中定义的HystrixDynamicProperty的扩展类,完成HystrixDynamicProperty的定制。代码如下:
公共类DemoHystrixDynamicProperties实现HystrixDynamicProperties { @ Override public HystrixDynamicProperty getString { return new string dynamicproperty;} @ Override public HystrixDynamicProperty getInteger { return new IntegerDynamicProperty;} @ Override public HystrixDynamicProperty get long { return new long dynamicproperty;} @ Override public HystrixDynamicProperty get boolean { return new BooleanDynamicProperty;}}3.寄存器SPI实现类
在meta-INF/services/中添加一个名为com . Netflix . hy strix . strategy . properties . hystrixdynamicproperties的文本文件,该文件包含步骤2中hystrixdynamics属性的自定义实现类的完整路径名。
2.8.2基于默认Archaius的扩展
Hystrix默认通过Archaius实现动态参数获取,Archaius本身也提供了用户自定义的参数获取方式,即PolledConfigurationSource接口和abstractionscheduler类,其中PolledConfigurationSource接口代表配置获取源,abstractionscheduler类代表配置定时刷新机制。
实现步骤如下:
1.创建配置采集源:
公共类CustomCfgConfigurationSource实现PolledConfigurationSource { private final static String ConFIG _ KEY _ PREFIX = " hy strix ";@Override public PollResult poll引发异常{ Map map = load返回poll result . create full;}私有映射加载抛出异常{ Map map = new HashMapSet keys = ConfigManager.keysfor { if){ map . put);} }返回图;}}它的实现非常简单。核心实现是poll方法,它遍历配置中心中以hystrix开头的所有配置参数,并将其返回保存。
2.定义配置刷新方法:
公共类CustomCfgPollingScheduler扩展AbstractPollingScheduler { private final static Logger Logger = Logger factory . get Logger;private final静态字符串ConFIG _ KEY _ PREFIX = " hystrix@ Override public void start polling { super . start polling;//config manager . addlistener { @ Override public void event received { String name = item . getname;if){ String new value = item . getvalue;//add modify if | | changeeventtype . item _ updated . equals){ addorchangeproperty;}//删除else if){ delete property;} else { logger.error} } } });} private void addOrChangeProperty { if){ config . add property;} else { Object old value = config . getproperty;if { if){ config . set property;} }否则if { config . set property;} } } private void delete property { if){ config . clear property;} } @ override protected void schedule {//ignore operation } @ override public void stop {//ignore operation } } AbstractBillingscheduler类的默认需求是定义一个调度任务来实现定时刷新配置,其方法schedule和stop方法分别是启动调度任务和结束任务。
但是,与实际项目相对应的是,定期刷新的方式并不是实时的。其次,每次都要检查配置中心是否全部修改过,逻辑复杂。因此,此处使用ConfigManager.addListener来增加对配置中心的监控。
3.定义和初始化自动配置:
dynamic configuration dynamic configuration = new dynamic configuration,new CustomCfgPollingScheduler);ConfigurationManager.install最后,您只需要在容器启动时执行上面的初始化脚本。
细心的同学可能会发现,在上述步骤的第3步中,install to Hystrix配置管理类的最后“安装”是DynamicConfiguration类的实现,而第2步中的定时刷新类也比较笨拙,所以他们在想是否可以继续简化上述方案。只需要实现一个定制的“动态配置”,它包括配置源获取和监控配置修改功能。实现如下:

公共类CustomCfgDynamicConfiguration扩展了ConcurrentMapConfiguration { private final static Logger Logger = Logger factory . get Logger;private final静态字符串ConFIG _ KEY _ PREFIX = " hystrixpublic CustomCfgDynamicConfiguration { super;负载;initEvent}private void Load { set keys = config manager . keys;for { if){ map . put);}}}Private void init事件{ config manager }的回调处理,同步Hystrix的配置参数变化。addlistener { @ override public void事件收到{ string name = item.getnameif){ String new value = item . getvalue;//add modify if | | changeeventtype . item _ updated . equals){ addorchangeproperty;}//删除else if){ delete property;} else { logger.error} } } });}private void addorchangeProperty { if){ this . Add property;} else { Object old value = this . getproperty;if { if){ this . set property;} } else if { this.setProperty}}}private void Delete property { if){ this . clear property;}}}最后通过configuration manager . install);只需“安装”实现即可。
第三,写在最后。
笔者结合实际项目,总结分享了Hystrix的使用方法,包括隔离策略、线程池设置、参数优先级等知识点的讲解,以及标注叠加、异常处理、参数动态配置等具体问题的解决方法。希望对大家有帮助。
原文链接:file/tupian/20220910/s _ _ biz = mzi 4 njy 4 MTU 5 NW = = mid = 2247490535 idx = 3 sn = e 232 f 4d 66 a 832d 7d 05 e 1181 E3 a9 ce 3a
如果你觉得这篇文章对你有帮助,可以转发关注支持。


