依赖如下
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-core</artifactId>
<version>1.5.18</version>
</dependency>
什么是Hystrix
2018.11发布了最后一个版本,目前处理维护阶段,不再升级版本
- 用途:
- 停止级联故障。fallback和优雅的降级,Fail fast和快速恢复
- 实时监控和配置实时变更
- 资源隔离,部分不可用不会导致整体系统不可用
- 场景:商品列表接口中,需要获取红包、价格、标签等数据。这时候可以给这个一个线程池。
如果线程池打满,也不会影响当前服务的非商品列表接口 - 使用的框架:hystrix主要使用Rxjava,上手可参考:https://www.jianshu.com/p/5e93c9101dc5
Hystrix配置外部化、动态化
Hystrix自己实现了一个配置接口HystrixProperty(没用netfix的通用Config:com.netfix.config.Property)
public interface HystrixProperty<T> {
// get获取属性方法
public T get();
/**
* 生产property的工厂
* HystrixProperty由Factory直接生产,不需要经过第三方依赖。
* - 其核心子接口为(下面会进行讲解):HystrixDynamicProperty
*/
public static class Factory {
public static <T> HystrixProperty<T> asProperty(final T value) {
return new HystrixProperty<T>() {
@Override
public T get() { return value; }
};
}
//..... 剩下其他各种获取属性asProperty方法
}
}
HystrixDynamicProperty
Hystrix中主要通过HystrixDynamicProperty接口来实现动态化属性。其对应 HystrixDynamicProperties 的两个实现类:
- Hystrix也是通过多种方式管理属性类,其中有一个Util类用来管理各个属性,不过目前只支持:String、Integer、Long、Boolean
public interface HystrixDynamicProperties {
public HystrixDynamicProperty<String> getString(String name, String fallback);
public HystrixDynamicProperty<Integer> getInteger(String name, Integer fallback);
public HystrixDynamicProperty<Long> getLong(String name, Long fallback);
public HystrixDynamicProperty<Boolean> getBoolean(String name, Boolean fallback);
public static class Util {
public static <T> HystrixDynamicProperty<T> getProperty(
HystrixDynamicProperties properties, String name, T fallback, Class<T> type) {
return (HystrixDynamicProperty<T>) doProperty(properties, name, fallback, type);
}
private static HystrixDynamicProperty<?> doProperty(
HystrixDynamicProperties delegate,
String name, Object fallback, Class<?> type) {// 仅支持四种类型:String、Integer、Long、Boolean
if(type == String.class) return delegate.getString(name, (String) fallback);
else if (type == Integer.class) return delegate.getInteger(name, (Integer) fallback);
else if (type == Long.class) return delegate.getLong(name, (Long) fallback);
else if (type == Boolean.class) return delegate.getBoolean(name, (Boolean) fallback);
throw new IllegalStateException();
}
}
}
上面HystrixDynamicProperties中delegate.getXxxx()的具体实现为如下两种方式
- HystrixDynamicPropertiesSystemProperties:
通过System.getProperty()获取,天然支持动态性,但不支持callback回调及外部化配置
public final class HystrixDynamicPropertiesSystemProperties implements HystrixDynamicProperties {
public HystrixDynamicPropertiesSystemProperties() {}
private static class LazyHolder {
private static final HystrixDynamicPropertiesSystemProperties INSTANCE = new HystrixDynamicPropertiesSystemProperties();
}
public static HystrixDynamicProperties getInstance() { return LazyHolder.INSTANCE; }
@Override
public HystrixDynamicProperty<Integer> getInteger(final String name, final Integer fallback) {
return new HystrixDynamicProperty<Integer>() {
@Override
public String getName() { return name; }
@Override
public Integer get() {//Integer.getInteger其实最终还是调用了System.getProperty() 了
return Integer.getInteger(name, fallback);
}
@Override
public void addCallback(Runnable callback) {}
};
}
// .......... 剩余其他String、Long、Boolean类型就不列出来,用法一样
}
- HystrixDynamicPropertiesArchaius(Archaius是Netfix自家配置管理库):
Netfix自家的配置管理库,支持动态化
public class HystrixDynamicPropertiesArchaius implements HystrixDynamicProperties {
@Override
public HystrixDynamicProperty<String> getString(String name, String fallback) {
return new StringDynamicProperty(name, fallback);
}
@Override
public HystrixDynamicProperty<Integer> getInteger(String name, Integer fallback) {
return new IntegerDynamicProperty(name, fallback);
}
// ..... 省略其他类型String、Long、Boolean
private abstract static class ArchaiusDynamicProperty<T>
extends PropertyWrapper<T> implements HystrixDynamicProperty<T> {
protected ArchaiusDynamicProperty(String propName, T defaultValue) { super(propName, defaultValue); }
@Override
public T get() { return getValue(); }
}
private static class IntegerDynamicProperty extends ArchaiusDynamicProperty<Integer> {
protected IntegerDynamicProperty(String propName, Integer defaultValue) { super(propName, defaultValue); }
/** prop.getInteger最终调用的是ConcurrentHashMap,那他是怎么塞进去CHM得?见下面解析 **/
@Override
public Integer getValue() { return prop.getInteger(defaultValue); }
}
// ..... 省略其他类型String、Long、Boolean
}
Archaius这种模式不同于System(System.getProperty()),他会从配置文件里面读取数据,起流程如下
- 获取IntegerDynamicProperty,是调用到super()方法
protected IntegerDynamicProperty(String propName, Integer defaultValue) {
super(propName, defaultValue);// 在ArchaiusDynamicProperty类中
}
- 追踪调用到PropertyWrapper类,会进行获取对应单例,并赋予默认值等初始化操作
public abstract class PropertyWrapper<V> {
protected PropertyWrapper(String propName, V defaultValue) {
// !!!重点关注这个,这里才是怎么根据属性名拿到对应属性值的入口
this.prop = DynamicProperty.getInstance(propName);
this.defaultValue = defaultValue;
//.... 期初初始化操作,省略
}
}
- DynamicProperty.getInstance获取实例过程中,可能会更新属性值
public class DynamicProperty {
// 根据属性名获取对应的动态化属性实例DynamicProperty
public static DynamicProperty getInstance(String propName) {
if (dynamicPropertySupportImpl == null) {
DynamicPropertyFactory.getInstance();
}
// 1 先从缓存中获取属性值
DynamicProperty prop = ALL_PROPS.get(propName);
if (prop == null) {
// 2 如果找不到,就尝试去更新(new DynamicProperty(propName)里面就有更新操作)
prop = new DynamicProperty(propName);
// 然后把新的属性值设置进去
DynamicProperty oldProp = ALL_PROPS.putIfAbsent(propName, prop);
if (oldProp != null) {
prop = oldProp;
}
}
return prop;
}
}
- 最后来看下是怎么更新拉取一个值,核心在于updateValue()
public class DynamicProperty {
private DynamicProperty(String propName) {
this.propName = propName;
updateValue();
}
// !!!!最终可以见到是通过一个dynamicPropertySupportImpl进行获取。那这个怎么拿到了,
// !!!!关键在于是怎么绑定的,下面继续讲解怎么绑定
private boolean updateValue() {
String newValue;
try {
if (dynamicPropertySupportImpl != null) {
// 调用这个实例方法获取属性值
newValue = dynamicPropertySupportImpl.getString(propName);
} else {
return false;
}
} catch (Exception e) {
logger.error("Unable to update property: " + propName, e);
return false;
}
return updateValue(newValue);
}
}
Hystrix如何选择合适的配置类进行加载
这一步会解释上一步的dynamicPropertySupportImpl实例是怎么选择?其实最终是通过DynamicPropertyFactory生产配置对象的时候返回回来,然后赋值给dynamicPropertySupportImpl
下面看一下他这个配置对象是怎么实例化的
- 初始化HystrixPlugins时,会加载解析动态属性
public class HystrixPlugins {
private HystrixPlugins(ClassLoader classLoader, LoggerSupplier logSupplier) {
this.classLoader = classLoader;
dynamicProperties = resolveDynamicProperties(classLoader, logSupplier);
}
}
- 先看看有没自定义的,有的话就classForName加载。然后用SPI扫描一下,如果扫描到就用第一个配置类。
再看看HystrixDynamicPropertiesArchaius,如果不存在或加载失败则使用HystrixDynamicPropertiesSystemProperties
public class HystrixPlugins {
private static HystrixDynamicProperties resolveDynamicProperties(ClassLoader classLoader, LoggerSupplier logSupplier) {
// 第一步:先看看有没设置属性:"hystrix.plugin.hystrixDynamicProperties.implementation",如果有就用自定义的属性类
HystrixDynamicProperties hp = getPluginImplementationViaProperties(HystrixDynamicProperties.class,
HystrixDynamicPropertiesSystemProperties.getInstance());
if (hp != null) {
logSupplier.getLogger().debug(
"Created HystrixDynamicProperties instance from System property named "
+ "\"hystrix.plugin.HystrixDynamicProperties.implementation\". Using class: {}",
hp.getClass().getCanonicalName());
return hp;
}
// 第二步:用SPI的加载器进行扫描,拿到第一个然后返回
hp = findService(HystrixDynamicProperties.class, classLoader);
if (hp != null) {
logSupplier.getLogger()
.debug("Created HystrixDynamicProperties instance by loading from ServiceLoader. Using class: {}",
hp.getClass().getCanonicalName());
return hp;
}
// 第三步:再看看HystrixDynamicPropertiesArchaius这个解析成功否,成功就用这个属性配置类
hp = HystrixArchaiusHelper.createArchaiusDynamicProperties();
if (hp != null) {
logSupplier.getLogger().debug("Created HystrixDynamicProperties. Using class : {}",
hp.getClass().getCanonicalName());
return hp;
}
// 第四步:实在上面都找不到了,直接用兜底的系统属性类,用System.getProperty()获取
hp = HystrixDynamicPropertiesSystemProperties.getInstance();
logSupplier.getLogger().info("Using System Properties for HystrixDynamicProperties! Using class: {}",
hp.getClass().getCanonicalName());
return hp;
}
}
看到这步骤,是不是有一点还比较懵逼的!哦,原来发现我们还没搞清他的配置信息是什么时候加载进来的,配置文件是哪一个。别慌,下面讲
配置信息如何加载进来
Hystrix有一个ConfigurationManager,初始化配置加载的时候会调用(默认会从config.properties加载,如果需要添加配置信息请加到这里来)。核心方法步骤如下:
- 获取系统配置(就是通过System.getProperties()全部加载进来)
- 从配置文件里面进行加载数据
public class ConfigurationManager {
//.... 其他逻辑省略
public static AbstractConfiguration getConfigInstance() {
if (instance == null && !Boolean.getBoolean(DynamicPropertyFactory.DISABLE_DEFAULT_CONFIG)) {
synchronized (ConfigurationManager.class) {
if (instance == null) {
instance = new ConcurrentCompositeConfiguration();
if (!Boolean.getBoolean(DynamicPropertyFactory.DISABLE_DEFAULT_SYS_CONFIG)) {
// 第一步:获取系统配置(就是通过System.getProperties()全部加载进来)
SystemConfiguration sysConfig = new SystemConfiguration();
((ConcurrentCompositeConfiguration) instance).addConfiguration(sysConfig, DynamicPropertyFactory.SYS_CONFIG_NAME);
try {
// 第二步:从默认的config.properties文件加载数据
// 然后看有没设置archaius.configurationSource.additionalUrls属性,如果有也从这个文件进行加载动态配置
// 最后会开启一个长轮询,默认每隔一分钟去刷新缓存数据
DynamicURLConfiguration defaultURLConfig = new DynamicURLConfiguration();
((ConcurrentCompositeConfiguration) instance).addConfiguration(defaultURLConfig, DynamicPropertyFactory.URL_CONFIG_NAME);
} catch (Throwable e) {
logger.warn("Failed to create default dynamic configuration", e);
}
}
registerConfigBean();
}
}
}
return instance;
}
}
动态配置长轮询刷新大概代码如下
// 位置:DynamicURLConfiguration.java
public DynamicURLConfiguration() {
URLConfigurationSource source = new URLConfigurationSource();
if (source.getConfigUrls() != null && source.getConfigUrls().size() > 0) {
startPolling(source, new FixedDelayPollingScheduler());
}
}
// 位置:DynamicConfiguration.java
public void startPolling(PolledConfigurationSource source, AbstractPollingScheduler scheduler) {
this.scheduler = scheduler;
this.source = source;
init(source, scheduler);
scheduler.startPolling(source, this);
}