聊聊HystrixPlugins

本文主要研究下HystrixPlugins

HystrixPlugins

hystrix-core-1.5.12-sources.jar!/com/netflix/hystrix/strategy/HystrixPlugins.java

/**
 * Registry for plugin implementations that allows global override and handles the retrieval of correct implementation based on order of precedence:
 * <ol>
 * <li>plugin registered globally via <code>register</code> methods in this class</li>
 * <li>plugin registered and retrieved using the resolved {@link HystrixDynamicProperties} (usually Archaius, see get methods for property names)</li>
 * <li>plugin registered and retrieved using the JDK {@link ServiceLoader}</li>
 * <li>default implementation</li>
 * </ol>
 * 
 * The exception to the above order is the {@link HystrixDynamicProperties} implementation 
 * which is only loaded through <code>System.properties</code> or the ServiceLoader (see the {@link HystrixPlugins#getDynamicProperties() getter} for more details).
 * <p>
 * See the Hystrix GitHub Wiki for more information: <a href="https://github.com/Netflix/Hystrix/wiki/Plugins">https://github.com/Netflix/Hystrix/wiki/Plugins</a>.
 */
public class HystrixPlugins {

    //We should not load unless we are requested to. This avoids accidental initialization. @agentgt
    //See https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom
    private static class LazyHolder { private static final HystrixPlugins INSTANCE = HystrixPlugins.create(); }
    private final ClassLoader classLoader;
    /* package */ final AtomicReference<HystrixEventNotifier> notifier = new AtomicReference<HystrixEventNotifier>();
    /* package */ final AtomicReference<HystrixConcurrencyStrategy> concurrencyStrategy = new AtomicReference<HystrixConcurrencyStrategy>();
    /* package */ final AtomicReference<HystrixMetricsPublisher> metricsPublisher = new AtomicReference<HystrixMetricsPublisher>();
    /* package */ final AtomicReference<HystrixPropertiesStrategy> propertiesFactory = new AtomicReference<HystrixPropertiesStrategy>();
    /* package */ 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;
        //N.B. Do not use a logger before this is loaded as it will most likely load the configuration system.
        //The configuration system may need to do something prior to loading logging. @agentgt
        dynamicProperties = resolveDynamicProperties(classLoader, logSupplier);
    }

    /**
     * For unit test purposes.
     * @ExcludeFromJavadoc
     */
    /* private */ static HystrixPlugins create(ClassLoader classLoader, LoggerSupplier logSupplier) {
        return new HystrixPlugins(classLoader, logSupplier);
    }

    /**
     * For unit test purposes.
     * @ExcludeFromJavadoc
     */
    /* private */ static HystrixPlugins create(ClassLoader classLoader) {
        return new HystrixPlugins(classLoader, new LoggerSupplier() {
            @Override
            public Logger getLogger() {
                return LoggerFactory.getLogger(HystrixPlugins.class);
            }
        });
    }
    /**
     * @ExcludeFromJavadoc
     */
    /* private */ static HystrixPlugins create() {
        return create(HystrixPlugins.class.getClassLoader());
    }

    public static HystrixPlugins getInstance() {
        return LazyHolder.INSTANCE;
    }

    /**
     * Reset all of the HystrixPlugins to null.  You may invoke this directly, or it also gets invoked via <code>Hystrix.reset()</code>
     */
    public static void reset() {
        getInstance().notifier.set(null);
        getInstance().concurrencyStrategy.set(null);
        getInstance().metricsPublisher.set(null);
        getInstance().propertiesFactory.set(null);
        getInstance().commandExecutionHook.set(null);
        HystrixMetricsPublisherFactory.reset();
    }

    /**
     * Register a {@link HystrixEventNotifier} implementation as a global override of any injected or default implementations.
     * 
     * @param impl
     *            {@link HystrixEventNotifier} implementation
     * @throws IllegalStateException
     *             if called more than once or after the default was initialized (if usage occurs before trying to register)
     */
    public void registerEventNotifier(HystrixEventNotifier impl) {
        if (!notifier.compareAndSet(null, impl)) {
            throw new IllegalStateException("Another strategy was already registered.");
        }
    }

    /**
     * Register a {@link HystrixConcurrencyStrategy} implementation as a global override of any injected or default implementations.
     * 
     * @param impl
     *            {@link HystrixConcurrencyStrategy} implementation
     * @throws IllegalStateException
     *             if called more than once or after the default was initialized (if usage occurs before trying to register)
     */
    public void registerConcurrencyStrategy(HystrixConcurrencyStrategy impl) {
        if (!concurrencyStrategy.compareAndSet(null, impl)) {
            throw new IllegalStateException("Another strategy was already registered.");
        }
    }

    /**
     * Register a {@link HystrixMetricsPublisher} implementation as a global override of any injected or default implementations.
     * 
     * @param impl
     *            {@link HystrixMetricsPublisher} implementation
     * @throws IllegalStateException
     *             if called more than once or after the default was initialized (if usage occurs before trying to register)
     */
    public void registerMetricsPublisher(HystrixMetricsPublisher impl) {
        if (!metricsPublisher.compareAndSet(null, impl)) {
            throw new IllegalStateException("Another strategy was already registered.");
        }
    }

    /**
     * Register a {@link HystrixPropertiesStrategy} implementation as a global override of any injected or default implementations.
     * 
     * @param impl
     *            {@link HystrixPropertiesStrategy} implementation
     * @throws IllegalStateException
     *             if called more than once or after the default was initialized (if usage occurs before trying to register)
     */
    public void registerPropertiesStrategy(HystrixPropertiesStrategy impl) {
        if (!propertiesFactory.compareAndSet(null, impl)) {
            throw new IllegalStateException("Another strategy was already registered.");
        }
    }

    /**
     * Register a {@link HystrixCommandExecutionHook} implementation as a global override of any injected or default implementations.
     * 
     * @param impl
     *            {@link HystrixCommandExecutionHook} implementation
     * @throws IllegalStateException
     *             if called more than once or after the default was initialized (if usage occurs before trying to register)
     * 
     * @since 1.2
     */
    public void registerCommandExecutionHook(HystrixCommandExecutionHook impl) {
        if (!commandExecutionHook.compareAndSet(null, impl)) {
            throw new IllegalStateException("Another strategy was already registered.");
        }
    }
    //......
}
  • hystrix提供了一个plugin机制用于修改hystrix的行为,可以作用到所有的HystrixCommand、HystrixObservableCommand、HystrixCollapser的实现
  • 提供了HystrixEventNotifier、HystrixConcurrencyStrategy、HystrixMetricsPublisher、HystrixPropertiesStrategy、HystrixCommandExecutionHook这几个plugin可以自己去实现
  • 这里采用了static holder的模式来实现HystrixPlugins的单例

getPluginImplementation

    private <T> T getPluginImplementation(Class<T> pluginClass) {
        T p = getPluginImplementationViaProperties(pluginClass, dynamicProperties);
        if (p != null) return p;        
        return findService(pluginClass, classLoader);
    }

    @SuppressWarnings("unchecked")
    private static <T> T getPluginImplementationViaProperties(Class<T> pluginClass, HystrixDynamicProperties dynamicProperties) {
        String classSimpleName = pluginClass.getSimpleName();
        // Check Archaius for plugin class.
        String propertyName = "hystrix.plugin." + classSimpleName + ".implementation";
        String implementingClass = dynamicProperties.getString(propertyName, null).get();
        if (implementingClass != null) {
            try {
                Class<?> cls = Class.forName(implementingClass);
                // narrow the scope (cast) to the type we're expecting
                cls = cls.asSubclass(pluginClass);
                return (T) cls.newInstance();
            } catch (ClassCastException e) {
                throw new RuntimeException(classSimpleName + " implementation is not an instance of " + classSimpleName + ": " + implementingClass);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(classSimpleName + " implementation class not found: " + implementingClass, e);
            } catch (InstantiationException e) {
                throw new RuntimeException(classSimpleName + " implementation not able to be instantiated: " + implementingClass, e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(classSimpleName + " implementation not able to be accessed: " + implementingClass, e);
            }
        } else {
            return null;
        }
    }

    private static <T> T findService(
            Class<T> spi, 
            ClassLoader classLoader) throws ServiceConfigurationError {

        ServiceLoader<T> sl = ServiceLoader.load(spi,
                classLoader);
        for (T s : sl) {
            if (s != null)
                return s;
        }
        return null;
    }
  • 先从动态配置找实现类,如果找不到则使用ServiceLoader进行加载
  • 配置实现类的格式为:”hystrix.plugin.” + classSimpleName + “.implementation”,比如hystrix.plugin.HystrixEventNotifier.implementation=com.example.hystrix.hook.CustomHystrixEventNotifierHook 配置可以放到配置文件hystrix-plugins.properties中
  • 从配置文件找到实现类的,采用的是Class.forName的形式来获取Class,然后使用newInstance来实例化

小结

hystrix内置了一个plugin机制,内置了HystrixEventNotifier、HystrixConcurrencyStrategy、HystrixMetricsPublisher、HystrixPropertiesStrategy、HystrixCommandExecutionHook这几个plugin,提供了默认的实现,并允许开发者在指定配置文件自定义该插件的实现类,通过类寻找及实例化的方式来加载,然后作用在HystrixCommand、HystrixObservableCommand及HystrixCollapser上。

doc

  • Hystrix/wiki/Plugins

原文发布于微信公众号 - 码匠的流水账(geek_luandun)

原文发表时间:2018-06-28

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏码匠的流水账

聊聊storm的LoggingMetricsConsumer

storm-2.0.0/storm-client/src/jvm/org/apache/storm/metric/LoggingMetricsConsumer....

1103
来自专栏码匠的流水账

聊聊storm的AssignmentDistributionService

本文主要研究一下storm的AssignmentDistributionService

1541
来自专栏小白鼠

【DUBBO】 Schema解析Spring扩展机制集成Spring

dubbo是如何做到与spring集成的?这都依赖于Spring提供的XML Schema可扩展机制,用户可以自定义XML Schema文件,并自定义XML B...

1873
来自专栏曾大稳的博客

Glide v3.7源码分析(2)-----RequestManager.load

可以看到,Glide初始化的时候做了很多的事,初始化了缓存相关的类,任务执行以及缓存管理的引擎,注册了DataLoadProviderRegistry Gene...

2261
来自专栏技术小黑屋

浅析WeakHashMap

在Java或者是Android编程中,我们一般都会使用到Map,比如HashMap这样的具体实现。更高级一点,我们可能会使用WeakHashMap。

3942
来自专栏Golang语言社区

【Golang语言社区】源码篇--sync包map

早晨看到知乎上一篇介绍Go1.9X版本部分功能,特产关注了一下;把源码想给大家呈现下,实际测试请看下一篇文章:Go语言sync.map 实际测...

35910
来自专栏java、Spring、技术分享

java应用CAS

  CAS(Compare and Swap),即比较并替换。jdk里的大量源码通过CAS来提供线程安全操作,比如AtomicInteger类。下面我们来分析一...

1453
来自专栏码匠的流水账

聊聊storm的WindowedBoltExecutor

storm-2.0.0/storm-client/src/jvm/org/apache/storm/topology/WindowedBoltExecutor....

1112
来自专栏码匠的流水账

kafka0.8生产者异常处理

本文简单解析一下kafka0.8.2.2版本中的java producer的异常处理。

1101
来自专栏码匠的流水账

聊聊storm的LoggingMetricsConsumer

storm-2.0.0/storm-client/src/jvm/org/apache/storm/metric/LoggingMetricsConsumer....

1443

扫码关注云+社区

领取腾讯云代金券