我正在尝试在Spring应用程序中使用并行流,但是却收到“找不到线程绑定的请求”异常。
我的代码类似于以下内容:
@Controller
@RequiredArgsConstructor(onConstructor = @__(@Inject)) // yes, I'm using lombok
public class controllerClass {
private final someOtherComponent;
@RequestMapping(value = "/test", method = RequestMethod.Get)
public Map<String, String> doParallelStream() {
List<String> testStrings = Arrays.asList("one", "two", "three");
return testStrings.parallelStream()
.map(testStrings -> someOtherComponent.someCall(testStrings))
.collect(Collectors.toConcurrentMap(
returnedString, returnedString, (p1, p2) -> p1
));
}
}
我的猜测是,因为我在并行流内的映射中使用了someOtherComponent,所以线程弹出的线程不再具有访问它的上下文。
我得到的全部错误是:
执行控制器时出错{java.lang.IllegalStateException:java.lang.IllegalStateException:未找到线程绑定的请求:您是在实际的Web请求之外引用请求属性,还是在原始接收线程之外处理请求?如果您实际上是在Web请求中操作并且仍然收到此消息,则您的代码可能在DispatcherServlet / DispatcherPortlet之外运行:在这种情况下,请使用RequestContextListener或RequestContextFilter公开当前请求。
关于如何解决这个问题的任何建议?
答案 0 :(得分:1)
我相信您的假设是正确的。范围为request
或session
的组件似乎在控制器线程以外的线程中使用。并且由于ThreadLocal
中RequestContextHolder
的基础用法而引发了异常,该用法被用作request
或session
范围内的bean的存储。为了使其正常工作,应使用InheritableThreadLocal
。您可以通过在threadContextInheritable
或true
中将DispatcherServlet
属性设置为RequestContextFilter
来启用它。
答案 1 :(得分:0)
我遇到了相同的错误,但情况有所不同。我有:
public ClientSettingsDTO getClientSettings(String clientId) {
CompletableFuture<Boolean> blacklistStatus = CompletableFuture.supplyAsync( () -> {
return getBlacklistStatus(clientId);
});
}
private Boolean getBlacklistStatus(String clientId) {
return mmmBlacklistRestClient.getBlacklistClientById(clientId); // IllegalStateException
}
通过配置我自己的Executor
bean并指定任务装饰器来解决此问题:
@Bean(name = "executorAsyncThread")
public TaskExecutor getAccountAsyncExecutor() {
ThreadPoolTaskExecutor poolExecutor = new ThreadPoolTaskExecutor();
poolExecutor.setTaskDecorator(new ContextCopyingDecorator());
poolExecutor.setCorePoolSize(10);
poolExecutor.setMaxPoolSize(20);
poolExecutor.setQueueCapacity(80000);
poolExecutor.setThreadNamePrefix("Async-Executor-");
poolExecutor.initialize();
return poolExecutor;
}
public class ContextCopyingDecorator implements TaskDecorator {
@Nonnull
@Override
public Runnable decorate(@Nonnull Runnable runnable) {
RequestAttributes context = RequestContextHolder.currentRequestAttributes();
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return () -> {
try {
RequestContextHolder.setRequestAttributes(context);
MDC.setContextMap(contextMap);
runnable.run();
} finally {
MDC.clear();
RequestContextHolder.resetRequestAttributes();
}
};
}
}
并将其作为supplyAsync
中的第二个参数传递:
CompletableFuture<Boolean> blacklistStatus = CompletableFuture.supplyAsync( () -> {
return getBlacklistStatus(clientId);
}, executor);
这允许执行请求。