之所以使用可继承的threadlocal,想必也很容易猜到,原来是需要在父子线程中传递上下文。
场景
线上AB测试
技术实施
- 应用注册时设置A/B状态到注册中心的metaData
- 扩展ribbon的负载均衡策略,当前应用上下文中的A/B状态符合注册中心的服务metaData时即可匹配路由
- 在应用的filter中增加AB上下文状态设置
- zuul网关代理应用接口,同时在请求头中透传A/B状态
集成测试
风风火火进行了大半月,没暴露任何故障,想必是没进行压测导致没提前暴露问题。
翻车现场
B环境的数据跑到了A正式环境。通过一顿操作,定位到是在hystrix线程隔离策略中使用了线程池,一旦池化那么InheritThreadLocal只会在线程池初始化线程时拷贝父线程的ThreadLocal,从而导致线程复用时不再会拷贝threadlocal的A/B状态。为了快速修复该bug,果断对线上hystrix的隔离调整为信号量。
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
........
//关键copy父线程threadlocal的代码
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
tid = nextThreadID();
}
怎么避免
1.使用阿里开源的的threadlocal和线程池来重写ribbon做自定义路由的上下文以及hystrix的线程池。
2.使用spring的TaskDecorator来装饰线程(推荐)移步这里