千万不要觉得学数学不重要,认为买菜都用不到。但是,但是,但是:它能决定你在哪儿买菜(毕竟拉分一般都靠数学)。
–> 返回专栏总目录 <–
代码下载地址:https://github.com/f641385712/netflix-learning
目录
前言
Hystrix
提供了插件机制(SPI机制)来提升自身的扩展性,提高弹性。这里所指的插件包括:
HystrixConcurrencyStrategy
:获取并发相关类HystrixEventNotifier
:事件通知类HystrixMetricsPublisher
:度量信息类HystrixPropertiesStrategy
:Properties配置类HystrixCommandExecutionHook
:HystrixCommand回调函数类HystrixDynamicProperties
:配置信息
共六类插件。其实也可以说是五类,因为HystrixDynamicProperties
严格意义上讲不算插件,它是给插件提供外部化配置的一个配置类,只是初始化它的时候也恰好支持到了外部化,所以索性也叫做插件吧。
这些插件被HystrixPlugins
管理着,由它统一负责加载和实例化。本文就介绍这几大插件的作用,以及讲述Hystrix是如何管理、加载它们的。
正文
此部分分为两大块进行讲解:
- SPI接口介绍
- HystrixPlugins详解
SPI接口介绍
SPI:Service Provider Interface
,是一种服务发现机制,JDK自带有ServiceLoader
来实现这种机制,当然更为出名的是Spring的SpringFactoriesLoader
(Dubbo也有自己的SPI实现哦),具体实现方式上都大同小异。
另外,这里所指的Interface
并不强要求必须是接口,比如本文里使用均为抽象类,也是一样的可以正常使用。
HystrixConcurrencyStrategy
并发相关的策略类。抽象类,用于使用默认实现为系统的并发相关方面定义不同的行为或实现。
public abstract class HystrixConcurrencyStrategy {
// ThreadFactory由HystrixThreadPoolKey来完成分组
// 线程均以守护线程形式,线程名为:`hystrix-threadPoolKey.name()-1/2/3/4...`
public ThreadPoolExecutor getThreadPool(final HystrixThreadPoolKey threadPoolKey, HystrixProperty<Integer> corePoolSize, HystrixProperty<Integer> maximumPoolSize, HystrixProperty<Integer> keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
final ThreadFactory threadFactory = getThreadFactory(threadPoolKey);
final int dynamicCoreSize = corePoolSize.get();
final int dynamicMaximumSize = maximumPoolSize.get();
if (dynamicCoreSize > dynamicMaximumSize) {
return new ThreadPoolExecutor(dynamicCoreSize, dynamicCoreSize, keepAliveTime.get(), unit, workQueue, threadFactory);
} else {
return new ThreadPoolExecutor(dynamicCoreSize, dynamicMaximumSize, keepAliveTime.get(), unit, workQueue, threadFactory);
}
}
// 配置来自于HystrixThreadPoolProperties,默认值是:
// core核心是10,最大值max也是10,keepAliveTimeMinutes=1分钟
// BlockingQueue因为不能配置,所以参见下面的这个getBlockingQueue方法
public ThreadPoolExecutor getThreadPool(final HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolProperties threadPoolProperties) { ... }
public BlockingQueue<Runnable> getBlockingQueue(int maxQueueSize) {
// 如果maxQueueSize木值,那就使用同步队列 -> 木有缓冲区
if (maxQueueSize <= 0) {
return new SynchronousQueue<Runnable>();
} else { // 否则使用Linked,队列大小是maxQueueSize哦(并不是无界的哦)
return new LinkedBlockingQueue<Runnable>(maxQueueSize);
}
}
// 给调用者一个机会,然你可以对callback进行包装一把
// 该方法子啊HystrixContextRunnable、HystrixContextCallable、HystrixContexSchedulerAction
// 里均会被调用
public <T> Callable<T> wrapCallable(Callable<T> callable) {
return callable;
}
}
它主要是用于获取/创建线程池,根据实际配置*(每个线程池的参数是可以不一样的)创建ThreadPoolExecutor
。另外提供一个可以对Callback进行包装、装饰的方法~
HystrixConcurrencyStrategyDefault
这是它的唯一实现,也是默认实现。以单例形式提供服务:
public class HystrixConcurrencyStrategyDefault extends HystrixConcurrencyStrategy {
private static HystrixConcurrencyStrategyDefault INSTANCE = new HystrixConcurrencyStrategyDefault();
public static HystrixConcurrencyStrategy getInstance() {
return INSTANCE;
}
...
}
简单的说,它是个空实现,用于兜底。
HystrixEventNotifier
hystrix命令执行过程中,接收相应的事件通知。
需要注意的是:这个notifier是同步调用的,因此里头方法的实现不能太耗时,不然则会阻塞,如果方法太耗时则需要考虑异步到其他线程。
public abstract class HystrixEventNotifier {
// 空实现:当任意事件被触发时,均执行此方法
public void markEvent(HystrixEventType eventType, HystrixCommandKey key) {
// do nothing
}
// 空实现:当使用线程隔离方式,触发事件时执行此方法
// 注意:如果被拒绝rejected(比如断路器全开了)、或者短路short-circuited了,那么此方法是不会被调用的
public void markCommandExecution(HystrixCommandKey key, ExecutionIsolationStrategy isolationStrategy, int duration, List<HystrixEventType> eventsDuringExecution) {
// do nothing
}
}
在HystrixCommand
以及HystrixObservableCommand
调用的时候,都会调用HystrixEventNotifier
来发布事件,提供给开发者自定义实现,来做指标收集及监控报警。
同样的,它的默认实现HystrixEventNotifierDefault
属于空实现,作为兜底使用。
HystrixEventType
Hystrix
事件类型枚举,并且还提供了分类:
EXCEPTION_PRODUCING_EVENT_TYPES
:异常事件类型。包括:BAD_REQUEST、FALLBACK_FAILURE、FALLBACK_MISSING、FALLBACK_REJECTION
- 注意:
FALLBACK_MISSING
表示木有提供fallabck函数,所以抛出异常哦。但是如果你正常提供了fallabck函数,那就是FALLBACK_SUCCESS
就不属于异常的
- 注意:
TERMINAL_EVENT_TYPES
:终端事件。SUCCESS、BAD_REQUEST、FALLBACK_SUCCESS、FALLBACK_FAILURE、FALLBACK_REJECTION、FALLBACK_MISSING、RESPONSE_FROM_CACHE、CANCELLED
,也就是遇上这些事件表示处理结束喽。
这些事件类型和事件分类在Hystrix
处理结果ExecutionResult
里将会有体现~
HystrixMetricsPublisher
抽象类,默认实现工厂方法,用于创建“Metrics Publisher”实例,以获取指标和其他相关数据。
public abstract class HystrixMetricsPublisher {
public HystrixMetricsPublisherCommand getMetricsPublisherForCommand(HystrixCommandKey commandKey, HystrixCommandGroupKey commandGroupKey, HystrixCommandMetrics metrics, HystrixCircuitBreaker circuitBreaker, HystrixCommandProperties properties) {
return new HystrixMetricsPublisherCommandDefault(commandKey, commandGroupKey, metrics, circuitBreaker, properties);
}
public HystrixMetricsPublisherThreadPool getMetricsPublisherForThreadPool(HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolMetrics metrics, HystrixThreadPoolProperties properties) {
return new HystrixMetricsPublisherThreadPoolDefault(threadPoolKey, metrics, properties);
}
public HystrixMetricsPublisherCollapser getMetricsPublisherForCollapser(HystrixCollapserKey collapserKey, HystrixCollapserMetrics metrics, HystrixCollapserProperties properties) {
return new HystrixMetricsPublisherCollapserDefault(collapserKey, metrics, properties);
}
}
三个方法参数都几乎一模一样,分别用于创建如上三组接口的实例。需要说明的是:他们的默认实现XXXDefault
均为空实现。
该接口和Hystrix
的监控密切相关,你可以自己将metrics指标信息落地存储,然后集成到其它指标监控系统。值得注意的是:Micrometer
对它便有集成,详见MicrometerMetricsPublisher
就是一种指标采集的实现。
说明:指标、监控是个较大的话题,也是将后指标监控专题的重中之重。
HystrixPropertiesStrategy
创建HystrixCommandProperties、HystrixThreadPoolProperties、HystrixCollapserProperties、HystrixTimerThreadPoolProperties
实例,方便在Hystrix
的各方各面上使用。
public abstract class HystrixPropertiesStrategy {
public HystrixCommandProperties getCommandProperties(HystrixCommandKey commandKey, HystrixCommandProperties.Setter builder) {
return new HystrixPropertiesCommandDefault(commandKey, builder);
}
public HystrixThreadPoolProperties getThreadPoolProperties(HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolProperties.Setter builder) {
return new HystrixPropertiesThreadPoolDefault(threadPoolKey, builder);
}
public HystrixCollapserProperties getCollapserProperties(HystrixCollapserKey collapserKey, HystrixCollapserProperties.Setter builder) {
return new HystrixPropertiesCollapserDefault(collapserKey, builder);
}
public HystrixTimerThreadPoolProperties getTimerThreadPoolProperties() {
return new HystrixPropertiesTimerThreadPoolDefault();
}
}
这个策略接口比较简单,并且相关xxxProperties
类在上文有详细解释,本处就不再鳌诉。
HystrixCommandExecutionHook
在HystrixCommand
的不同声明周期的回调接口,默认实现是无操作。
public abstract class HystrixCommandExecutionHook {
// 命令开始执行前调用
public <T> void onStart(HystrixInvokable<T> commandInstance) {
//do nothing by default
}
// 发送数据时调用
public <T> T onEmit(HystrixInvokable<T> commandInstance, T value) {
return value; //by default, just pass through
}
public <T> Exception onError(HystrixInvokable<T> commandInstance, FailureType failureType, Exception e) {
return e; //by default, just pass through
}
public <T> void onSuccess(HystrixInvokable<T> commandInstance) {
//do nothing by default
}
.... // 省略其它方法,实在太多了
}
hystrix在执行命令的各个节点会调用HystrixCommandExecutionHook
,这些都是留给调用者的钩子方法。
HystrixCommandExecutionHook
提供了对HystrixCommand
及HystrixObservableCommand
生命周期的钩子方法,开发者可以自定义实现,做一些额外的处理,比如日志打印、覆盖response、更改线程状态等等。
HystrixPlugins详解
本类是Hystrix
内置插件机制的实现。上面介绍了几个SPI接口/抽象类,但是你会发现Default默认实现均是空实现,因为这些均是Hystrix留给使用者的钩子,交给你去定制,而这种定制化的实现就是通过HystrixPlugins
来做的。
初始化HystrixDynamicProperties
实际上HystrixDynamicProperties
也是HystrixPlugins
所管理的六大SPI接口之一,但是它稍有点特殊,它亦是给其它5个SPI接口提供配置的基础,所以它伴随着HystrixPlugins
的初始化而初始化,并且它只能被初始化一次(其它的均可多次)。
// 它自己以单例形式展示
public class HystrixPlugins {
// 获取HystrixPlugins 单例
public static HystrixPlugins getInstance() {
return LazyHolder.INSTANCE;
}
// =======================它的成员属性们=======================
private final ClassLoader classLoader;
// 下面这几个属性均是pachage的访问级别~~~~~~~~~~~~~~~
// 使用AtomicReference来保证原子性
final AtomicReference<HystrixEventNotifier> notifier = new AtomicReference<HystrixEventNotifier>();
final AtomicReference<HystrixConcurrencyStrategy> concurrencyStrategy = new AtomicReference<HystrixConcurrencyStrategy>();
final AtomicReference<HystrixMetricsPublisher> metricsPublisher = new AtomicReference<HystrixMetricsPublisher>();
final AtomicReference<HystrixPropertiesStrategy> propertiesFactory = new AtomicReference<HystrixPropertiesStrategy>();
final AtomicReference<HystrixCommandExecutionHook> commandExecutionHook = new AtomicReference<HystrixCommandExecutionHook>();
private final HystrixDynamicProperties dynamicProperties;
// 初始化的时候(只会进行一次),给成员属性赋值
private HystrixPlugins(ClassLoader classLoader, LoggerSupplier logSupplier) {
//This will load Archaius if its in the classpath.
this.classLoader = classLoader;
dynamicProperties = resolveDynamicProperties(classLoader, logSupplier);
}
}
在HystrixPlugins
初始化的时候,会给dynamicProperties
赋值,获取它的实例优先级顺序是这样的:
- 从System系统属性里找
"hystrix.plugin." + classSimpleName + ".implementation"
,也就是hystrix.plugin.HystrixDynamicProperties.implementation
这个key。若存在就Class.forName() - 通过
ServiceLoader
的SPI方式加载HystrixDynamicProperties
的实现类 - 通过
HystrixArchaiusHelper.createArchaiusDynamicProperties()
创建一个实现类:- 让
hystrix-plugins.properties
和hystrix-plugins-环境名.properties
里的属性都加入到全局配置中 - 从类路径下
Class.forName("com.netflix.hystrix.strategy.properties.archaius.HystrixDynamicPropertiesArchaius")
- 这样Hystrix的属性就和全局属性关联上了喽,并且使用
Archaius
动态管理
- 让
- 最后,若都还没找到,那就兜底使用
HystrixDynamicPropertiesSystemProperties
:使用System属性(一般情况下,到第三步肯定就能完成实例化了)
实际生产中,大概率会在第3步完成初始化,也就是HystrixDynamicPropertiesArchaius
实例,这样的话hystrix-plugin.properties
里的属性也都是会生效的哦。
初始化其它SPI接口
其它SPI接口并不会主动初始化,而是按需被调用的时候完成查找、初始化动作。
下面以获取HystrixEventNotifier
实例为例,其它的接口获取逻辑一毛一样:
HystrixPlugins:
public void registerEventNotifier(HystrixEventNotifier impl) {
if (!notifier.compareAndSet(null, impl)) {
throw new IllegalStateException("Another strategy was already registered.");
}
}
public HystrixEventNotifier getEventNotifier() {
// 由此可见,手动注册进来的优先级是最高的
if (notifier.get() == null) {
Object impl = getPluginImplementation(HystrixEventNotifier.class);
// 如果还找到,就使用Defualt默认实例:空实现
if (impl == null) {
notifier.compareAndSet(null, HystrixEventNotifierDefault.getInstance());
} else {
notifier.compareAndSet(null, (HystrixEventNotifier) impl);
}
}
return notifier.get();
}
// 从
private <T> T getPluginImplementation(Class<T> pluginClass) {
T p = getPluginImplementationViaProperties(pluginClass, dynamicProperties);
if (p != null) return p;
return findService(pluginClass, classLoader);
}
查找一个SPI接口的实现类的流程如下(优先级由高到低):
- 手动
register
进来的 - 从上一步初始化好的
dynamicProperties
里找key为:hystrix.plugin." + classSimpleName + ".implementation
属性作为实现类 - 使用
ServiceLoader
的SPI方式查找实现类 - 使用Default实现(空实现)
由于插件的实现类一般不可能动态改变,所以它一般有个最佳实践:采用hystrix.plugin." + classSimpleName + ".implementation
的方式把它各插件的实现类全类名统一配置在hystrix-plugin.properties
文件里,这也方便了管理。相信这也便是Hystrix
设计一个名hystrix-plugin
的文件的唯一目的吧(因为刚好它里面的属性是不具有动态性的,完全符合条件)~
使用示例
为了看到效果,自定义一个SPI实现类:
public class MyHystrixMetricsPublisher extends HystrixMetricsPublisher {
public MyHystrixMetricsPublisher() {
System.out.println("MyHystrixMetricsPublisher被实例化了...");
}
}
在hystrix-plugins.properties
里配置如下:
hystrix.plugin.HystrixMetricsPublisher.implementation=com.yourbatman.hystrix.MyHystrixMetricsPublisher
书写测试程序:
@Test
public void fun1() {
HystrixPlugins instance = HystrixPlugins.getInstance();
HystrixDynamicProperties dynamicProperties = instance.getDynamicProperties();
System.out.println(dynamicProperties.getString("name", null).get());
System.out.println("===========================================");
// 类型
System.out.println(dynamicProperties.getClass());
System.out.println(instance.getMetricsPublisher().getClass());
System.out.println(instance.getEventNotifier().getClass());
System.out.println(instance.getConcurrencyStrategy().getClass());
}
运行程序,控制台输出:
YourBatman
===========================================
class com.netflix.hystrix.strategy.properties.archaius.HystrixDynamicPropertiesArchaius
MyHystrixMetricsPublisher被实例化了...
class com.yourbatman.hystrix.MyHystrixMetricsPublisher
class com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifierDefault
class com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategyDefault
说明:MyHystrixMetricsPublisher
的配置项放在config.properties
也是有效的,但是放在hystrix-plugins.properties
里乃最佳实践(因为它还有一个优点:hystrix-plugins-环境名称.properties
这个配置相同key的优先级更高,所以非常方便你进行多环境测试使用)。
总结
关于Netflix Hystrix插件机制:SPI接口介绍和HystrixPlugins详解就介绍到这了,这里最为重要的我认为是对后续自定制监控模块打好基础,为扩展做好准备。
同时本文也能告诉我们,一个优秀的框架是需要具备良好的扩展性,以及预留足够多的钩子程序的,这样才能有更多人参与进来,流行度才会铺开。
声明
原创不易,码字不易,多谢你的点赞、收藏、关注。把本文分享到你的朋友圈是被允许的,但拒绝抄袭
。你也可【左边扫码/或加wx:fsx641385712】邀请你加入我的 Java高工、架构师 系列群大家庭学习和交流。
- [享学Netflix] 一、Apache Commons Configuration:你身边的配置管理专家
- [享学Netflix] 二、Apache Commons Configuration事件监听机制及使用ReloadingStrategy实现热更新
- [享学Netflix] 三、Apache Commons Configuration2.x全新的事件-监听机制
- [享学Netflix] 四、Apache Commons Configuration2.x文件定位系统FileLocator和FileHandler
- [享学Netflix] 五、Apache Commons Configuration2.x别样的Builder模式:ConfigurationBuilder
- [享学Netflix] 六、Apache Commons Configuration2.x快速构建工具Parameters和Configurations
- [享学Netflix] 七、Apache Commons Configuration2.x如何实现文件热加载/热更新?
- [享学Netflix] 八、Apache Commons Configuration2.x相较于1.x使用上带来哪些差异?
- [享学Netflix] 九、Netflix Archaius配置管理库:初体验及基础API详解
- [享学Netflix] 十、Netflix Archaius对Commons Configuration核心API Configuration的扩展实现
- [享学Netflix] 十一、Netflix Archaius配置管理器ConfigurationManager和动态属性支持DynamicPropertySupport
- [享学Netflix] 十二、Netflix Archaius动态属性DynamicProperty原理详解(重要)
- [享学Netflix] 十三、Netflix Archaius属性抽象Property和PropertyWrapper详解
- [享学Netflix] 十四、Netflix Archaius如何对多环境、多区域、多云部署提供配置支持?
- [享学Netflix] 十五、Netflix Archaius和Spring Cloud的集成:spring-cloud-starter-netflix-archaius
- [享学Netflix] 十六、Netflix Hystrix断路器:初体验及RxJava简介
- [享学Netflix] 十七、Netflix Hystrix属性抽象以及和Archaius整合实现配置外部化、动态化
- [享学Netflix] 十八、Netflix Hystrix配置之:全局配置和实例配置