项目场景:
近期在使用Spring Cloud Alibaba时,因为在微服务中上下文使用ThreadLocal时遇到了失效的问题,所以引入了ThreadLocalHystrixConcurrencyStrategy来处理这个问题,没想到引入后每次项目进行debug时都会跳转到异常
问题描述:
例如:在使用debug模式时启动Application:
@EnableDiscoveryClient
@EnableFeignClients
@SpringBootApplication
@EnableBinding(MQSource.class)
public class OrderServiceStarter {
public static void main(String[] args) {
HystrixPlugins.getInstance().registerConcurrencyStrategy(new ThreadLocalHystrixConcurrencyStrategy());
SpringApplication.run(OrderServiceStarter.class, args);
}
}
项目启动时会跳转至该错误
Class ConfigurationManager:
private static Properties loadCascadedProperties(String configName) throws IOException {
String defaultConfigFileName = configName + ".properties";
if (instance == null) {
instance = getConfigInstance();
}
ClassLoader loader = Thread.currentThread().getContextClassLoader();
URL url = loader.getResource(defaultConfigFileName);
if (url == null) {
//异常跳转至此,缺少hystrix-plugins.properties
throw new IOException("Cannot locate " + defaultConfigFileName + " as a classpath resource.");
}
Properties props = getPropertiesFromFile(url);
String environment = getDeploymentContext().getDeploymentEnvironment();
if (environment != null && environment.length() > 0) {
String envConfigFileName = configName + "-" + environment + ".properties";
url = loader.getResource(envConfigFileName);
if (url != null) {
Properties envProps = getPropertiesFromFile(url);
if (envProps != null) {
props.putAll(envProps);
}
}
}
return props;
}
原因分析:
debug观察后发现是因为缺少hystrix-plugins.properties,配置文件,百思不得其解,明明我们已经在main方法中将对应的插件注册进去了,为啥还会去扫描hystrix-plugins.properties,故而翻阅源码!
话不多讲,直接找到 HystrixPlugins
Class HystrixPlugins:
public HystrixEventNotifier getEventNotifier() {
if (notifier.get() == null) {
//首先检查Archaius的实现,如果有手动注册,则先采用手动注册
Object impl = getPluginImplementation(HystrixEventNotifier.class);
if (impl == null) {
// 没有设置通过Archaius,采用默认初始化
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);
}
@SuppressWarnings("unchecked")
private static <T> T getPluginImplementationViaProperties(Class<T> pluginClass, HystrixDynamicProperties dynamicProperties) {
String classSimpleName = pluginClass.getSimpleName();
// 看到了这里! 恍然大悟,就是这个东西!
String propertyName = "hystrix.plugin." + classSimpleName + ".implementation";
String implementingClass = dynamicProperties.getString(propertyName, null).get();
if (implementingClass != null) {
try {
Class<?> cls = Class.forName(implementingClass);
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;
}
}
解决方案:
源码解读完后,答案已经摆在眼前了,我直接新建一个hystrix-plugins.properties
将我们具体的实现类写入其中,并添加上我们的测试方法
hystrix.plugin.HystrixConcurrencyStrategy.implementation=com.something.common.hystrix.ThreadLocalHystrixConcurrencyStrategy
public class ThreadLocalHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
private final static Logger logger = LoggerFactory.getLogger(ThreadLocalHystrixConcurrencyStrategy.class);
public ThreadLocalHystrixConcurrencyStrategy() {
log.info("ThreadLocalHystrixConcurrencyStrategy被实例化了");
System.out.println("ThreadLocalHystrixConcurrencyStrategy被实例化了");
}
重新debug模式启动项目,不再出现上述错误,大功告成。
同时,观察实例化的时间节点,发现是在该微服务被调用时会进行实例化,这就跟具体实现的内容相关了。
总结:
总而言之,阅读源码的确不可缺少