ThreadLocal可以隔離線程變量爪喘,在項(xiàng)目中可以應(yīng)用在存儲當(dāng)前線程信息,例如登錄用戶纠拔,租戶等秉剑,但是在需要異步執(zhí)行時,上下文信息需要手動傳遞稠诲,增加與業(yè)務(wù)無關(guān)的代碼侦鹏,jdk雖然提供了InheritableThreadLocal,但是在使用線程池時臀叙,無法用新值覆蓋舊值導(dǎo)數(shù)數(shù)據(jù)不準(zhǔn)確略水。阿里開源了TransmittableThreadLocal用于支持線程池下的異步信息傳遞。
在微服務(wù)的異步調(diào)用時一些公共信息可以使用TransmittableThreadLocal進(jìn)行傳遞劝萤。
首先創(chuàng)建一個context類存儲用戶信息
public class UserContext {
private static final ThreadLocal<String> ttl = new TransmittableThreadLocal<>();
public static void setTtl(String s) {
ttl.set(s);
}
public static String getTtl() {
return ttl.get();
}
public static void removeTtl() {
ttl.remove();
}
}
配置使用ttl線程池
@Bean(name = "ttlExecutor")
public Executor ttlExecutor() {
ThreadPoolTaskExecutor asyncTaskExecutor = new ThreadPoolTaskExecutor();
asyncTaskExecutor.setMaxPoolSize(2);
asyncTaskExecutor.setCorePoolSize(2);
asyncTaskExecutor.setQueueCapacity(10);
asyncTaskExecutor.setThreadNamePrefix("ttl-async-task-thread-pool-");
asyncTaskExecutor.initialize();
asyncTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return TtlExecutors.getTtlExecutor(asyncTaskExecutor);
}
使用handlerinteceptor攔截請求信息
public class RequestHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Enumeration<String> headerNames = request.getHeaderNames();
HashMap<String, String> requestMap = Maps.newHashMapWithExpectedSize(10);
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String value = request.getHeader(name);
requestMap.put(name, value);
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
//AsyncRequestContextHolder.resetRequestMap();
}
}
注冊攔截器
public class WebApiAutoConfiguration implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(requestHandlerInterceptor).addPathPatterns("/**");
}
}
feign調(diào)用時添加header信息到當(dāng)前請求
public class FeignAutoConfiguration {
@Bean
public RequestInterceptor tenantInterceptor() {
return template -> {
// 需要清空的信息
// template.removeHeader("");
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (null == attributes) {
Map<String, String> requestMap = AsyncRequestContextHolder.getRequestMap();
requestMap.forEach((name, values) -> {
// 跳過content-length渊涝,避免Feign報(bào)錯(feign.RetryableException: too many bytes written executing ...)
if (name.equals("content-length")) {
return;
}
template.header(name, values);
}
}
else {
HttpServletRequest request = attributes.getRequest();
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String values = request.getHeader(name);
// 跳過content-length,避免Feign報(bào)錯(feign.RetryableException: too many bytes written executing ...)
if (name.equals("content-length")) {
continue;
}
template.header(name, values);
}
}
}
}