背景:项目中采用了Feigin + Hystrix 实现RPC 熔断处理,当使用到了 Hystrix 线程隔离机制时,由于业务中对于 Feigin 调用请求拦截器中统一添加 Token 到请求 Header中,由于多线程的缘故导致拦截器无法正常获取到ThreadLocal中设置的参数。
Tips: 解决ThreadLocal传值和线程池可见性方案有很多种,今天重点介绍下 Hystrix 提供的解决方案。
一、自定义实现HystrixConcurrencyStrategy 和 自定义CallableWrapper 包装器
package com.github.ext;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.Callable;
/**
* 熔断器并发处理策略
*/
public class HystrixConcurrencyStrategyExtension extends HystrixConcurrencyStrategy {
protected final static ThreadLocal<Map<String, Object>> threadLocal = new ThreadLocal<Map<String, Object>>() {
@Override
protected Map<String, Object> initialValue() {
return new HashMap<>();
}
};
/**
* 设置属性信息到上下文
*
* @param name 属性名称(不能为空)
* @param value 属性值(不能为空)
*/
public static void setAttribute(String name, Object value) {
Objects.requireNonNull(name, "`name` cannot be null");
Objects.requireNonNull(value, "`value` cannot be null");
threadLocal.get().putIfAbsent(name, value);
}
/**
* 获取属性值
*
* @param name 属性名称
* @return
*/
public static <T> Optional<T> getAttribute(String name) {
Object value = threadLocal.get().get(name);
return (Optional<T>) Optional.ofNullable(value);
}
/**
* 清除上下文信息
*/
public static void remove() {
threadLocal.remove();
}
/**
* 包装Callable处理器
*
* @return
*/
@Override
public <T> Callable<T> wrapCallable(Callable<T> callable) {
return new CallableWrapper(callable);
}
/**
* 实现自定义Callable包装器
* @param <T>
*/
public static class CallableWrapper<T> implements Callable<T> {
private final Callable<T> delegate;
private final Map<String, Object> attributes;
public CallableWrapper(Callable<T> callable) {
this.delegate = callable;
this.attributes = HystrixConcurrencyStrategyExtension.threadLocal.get();
}
@Override
public T call() throws Exception {
try {
HystrixConcurrencyStrategyExtension.threadLocal.set(attributes);
return delegate.call();
} finally {
HystrixConcurrencyStrategyExtension.remove();
}
}
}
}
二、将HystrixConcurrencyStrategyExtension注册到 Hystrix 即可。
方式一:通过 Java SPI 扩展机制实现自动注册
在工程 resources/目录下创建目录:META-INF/services/
创建文件:com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy 并将自定义的扩展类名称写入到文件即可,
com.github.ext.HystrixConcurrencyStrategyExtension
方式二:通过Hystrix 提供的编程式 API 来实现注册
HystrixPlugins.reset();
HystrixPlugins.getInstance().registerConcurrencyStrategy(new HystrixConcurrencyStrategyExtension());
测试用例:
//获取当前 Hystrix 并发访问策略
HystrixPlugins.getInstance().getConcurrencyStrategy();
//设置传递参数
HystrixConcurrencyStrategyExtension.setAttribute("token", "HelloWorld!!!");
assert HystrixConcurrencyStrategyExtension.getAttribute("token").equals("HelloWorld!!!");
版权声明:本文为kevin_Luan原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。