报错内容:No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
我这边的场景是:消费到一条mq的新消息,然后执行业务;业务中包含有一个异步操作,这个异步操作使用了线程池,在使用线程池进行执行的时候报了这个错误
下面贴出代码,每次使用线程池执行的时候都会用访问方法decorate,获取请求的上下文,但是本次的请求不是一个web请求,是没有这个上下文的信息,所以这里报错
@Slf4j
public class ThreadPoolConfig {
/**
* 获取当前机器CPU数量
*/
private static final int cpu = Runtime.getRuntime().availableProcessors();
/**
* 核心线程数(默认线程数)
*/
private static final int corePoolSize = cpu;
/**
* 最大线程数
*/
private static final int maxPoolSize = cpu * 2;
/**
* 允许线程空闲时间(单位:默认为秒)
*/
private static final int keepAliveTime = 60;
/**
* 缓冲队列数
*/
private static final int queueCapacity = 200;
/**
* 线程池名前缀
*/
private static final String threadNamePrefix = "xxx-threadPool-";
@Bean("poolTaskExecutor")
@Primary
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//传递异步调用上下文
executor.setTaskDecorator(new ContextDecorator());
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setKeepAliveSeconds(keepAliveTime);
executor.setThreadNamePrefix(threadNamePrefix);
// 线程池对拒绝任务的处理策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//设置在关闭线程池时是否等待任务完成
executor.setWaitForTasksToCompleteOnShutdown(true);
// 初始化
executor.initialize();
return executor;
}
class ContextDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
RequestAttributes context = RequestContextHolder.currentRequestAttributes();
return () -> {
try {
ServletRequestAttributes attributes = (ServletRequestAttributes) context;
HttpServletRequest request = attributes.getRequest();
Map<String, String> headers = new ConcurrentHashMap<>();
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String values = request.getHeader(name);
headers.put(name, values);
}
}
ThreadLocalUtil.set("thread header", headers);
RequestContextHolder.setRequestAttributes(context);
runnable.run();
} finally {
RequestContextHolder.resetRequestAttributes();
}
};
}
}
}
解决的代码见下面代码,内部类ContextDecorator获取上下文的地方做一个修改,
class ContextDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
RequestAttributes context = getRequestAttributesSafely();
return () -> {
try {
if (context instanceof ServletRequestAttributes){
ServletRequestAttributes attributes = (ServletRequestAttributes) context;
HttpServletRequest request = attributes.getRequest();
Map<String, String> headers = new ConcurrentHashMap<>();
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String values = request.getHeader(name);
headers.put(name, values);
}
}
ThreadLocalUtil.set("thread header", headers);
}
RequestContextHolder.setRequestAttributes(context);
runnable.run();
} finally {
RequestContextHolder.resetRequestAttributes();
}
};
}
private RequestAttributes getRequestAttributesSafely(){
RequestAttributes requestAttributes = null;
try{
requestAttributes = RequestContextHolder.currentRequestAttributes();
}catch (IllegalStateException e){
requestAttributes = new NoWebRequestAttributes();
}
return requestAttributes;
}
}
NoWebRequestAttributes类
public class NoWebRequestAttributes implements RequestAttributes {
@Override
public Object getAttribute(String s, int i) {
return null;
}
@Override
public void setAttribute(String s, Object o, int i) {
}
@Override
public void removeAttribute(String s, int i) {
}
@Override
public String[] getAttributeNames(int i) {
return new String[0];
}
@Override
public void registerDestructionCallback(String s, Runnable runnable, int i) {
}
@Override
public Object resolveReference(String s) {
return null;
}
@Override
public String getSessionId() {
return null;
}
@Override
public Object getSessionMutex() {
return null;
}
}