记一次线程变量在子线程中丢失问题

1.采用技术:
使用的是HystrixRequestContext 与 HystrixRequestVariableDefault
2.HystrixRequestContext源码:
有一个类变量requestVariables 使用ThreadLocal修饰
有一个类变量state 是ConcurrentHashMap类型
通过initializeContext方法初始化当前线程的变量值
通过getContextForCurrentThread 和 setContextOnCurrentThread 对线程变量值进行修改
3.HystrixRequestVariableDefault源码:
有get方法,获取HystrixRequestContext中的state 值
有set方法,向HystrixRequestContext中的state 赋值
4.使用:
void HystrixRequestVariable{
public static final HystrixRequestVariableDefault version = new HystrixRequestVariableDefault<>();
public static void initHystrixRequestContext(String headerVer) {
log.debug(“headerVer:{}”, headerVer);
if (!HystrixRequestContext.isCurrentThreadInitialized()) {
HystrixRequestContext.initializeContext();
}
HystrixRequestVariable.version.set(headerVer);
}
}
在拦截器的preHandle 中调用HystrixRequestVariable的初始化
HystrixRequestVariable.initHystrixRequestContext(“version”);
在拦截器的postHandle中调用HystrixRequestVariable的shutdown
HystrixRequestVariable.shutdownHystrixRequestContext();
5.子线程调用:
在创建子线程时,需要将当前线程的变量带入
public static final HystrixRequestVariableDefault version = new HystrixRequestVariableDefault<>();
void test {
HystrixRequestContext context = HystrixRequestContext.initializeContext();
version.set(“lili”);
new Thread(new Runnable() {
@Override
public void run() {
HystrixRequestContext.setContextOnCurrentThread(context);
System.out.println(version.get());
}
}).start();
}
线程池:
public static final HystrixRequestVariableDefault version = new HystrixRequestVariableDefault<>();
private static final ExecutorService executorService = Executors.newCachedThreadPool();
public static void main(String[] args) {
HystrixRequestContext context = HystrixRequestContext.initializeContext();
version.set(“lili”);
DemoContextCallable2 demoContextCallable2 = new DemoContextCallable2();
try{
for (int i=0; i<3; i++) {
executorService.submit(demoContextCallable2);
}
}finally {
executorService.shutdown();
}
}

public class DemoContextCallable2 implements Runnable {
	    private final HystrixRequestContext hystrixRequestContext;
	    public DemoContextCallable2() {//初始化时将煮线程的线程变量传过来
	        this.hystrixRequestContext = HystrixRequestContext.getContextForCurrentThread();
	    }
	    @Override
	    public void run() {
	        try {
	            HystrixRequestContext.setContextOnCurrentThread(hystrixRequestContext);
	            System.out.println(ContextText2.version.get());
	        } catch(Exception e) {
	            DemoRequestContext.setContextOnCurrentThread(null);
	        }
	    }
	}g

6.异步调用时,线程变量丢失问题
在子线程执行过程中,HystrixRequestContext中的线程变量丢失了,ContextText2.version.get() = null;
尤其当在子线程中通过fegin接口调用其他服务时,获取不到version,导致失败
7.异常排查:
子线程中加日志打印,确定主线程的变量已经传入子线程中
子线程调用fegin接口后加日志打印,确定在调用后或者在调用时丢失
HystrixRequestContext为什么被清空,哪里被清空
8.溯源:
在子线程中没有清空,在fegin中没有被清空
清空的情况只有在HystrixRequestContext使用完了
在拦截器的postHandle中调用HystrixRequestVariable的shutdown清空
9.原因分析:
在子线程执行的时候,主线程返回,调用了拦截器中的postHandle方法,导致线程变量被清空
子线程刚被创建的时候,可以获得线程变量,但是子线程执行时间超过主线程的时候,HystrixRequestContext中被清空
在接口调用时,HystrixRequestContext中以空,ContextText2.version.get() = null;
10.解决方案:
HystrixRequestContext被初始化后,一定要进行关闭即调用shutdown
如果想HystrixRequestContext一直有值,一直存活,需要在子线程执行完后调用shutdown
如果使用的时线程池,需要等线程池都执行完毕后再shutdown

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值