Spring Controller中的多线程“未找到线程绑定请求”

时间:2018-07-25 19:42:56

标签: java spring multithreading

我正在尝试在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公开当前请求。

关于如何解决这个问题的任何建议?

2 个答案:

答案 0 :(得分:1)

我相信您的假设是正确的。范围为requestsession的组件似乎在控制器线程以外的线程中使用。并且由于ThreadLocalRequestContextHolder的基础用法而引发了异常,该用法被用作requestsession范围内的bean的存储。为了使其正常工作,应使用InheritableThreadLocal。您可以通过在threadContextInheritabletrue中将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);

这允许执行请求。