Java 控制器中并行流中没有线程绑定请求错误
我的控制器上有一个端点,它接受ID列表。对于每个ID,我从一个REST端点获取详细信息,我希望异步执行该操作。这是我的实现:Java 控制器中并行流中没有线程绑定请求错误,java,spring,spring-boot,Java,Spring,Spring Boot,我的控制器上有一个端点,它接受ID列表。对于每个ID,我从一个REST端点获取详细信息,我希望异步执行该操作。这是我的实现: @PostMapping("/leaderboard") @ApiOperation("Get clients leaderboard statistics") public List<Dto> clientLeaderboard(@RequestBody List<String> accountIds) { IntStream.rang
@PostMapping("/leaderboard")
@ApiOperation("Get clients leaderboard statistics")
public List<Dto> clientLeaderboard(@RequestBody List<String> accountIds) {
IntStream.range(0, accountIds.size()).parallel().forEach(i -> {
AccountDTO accountDTO = accountMaintenanceRestClient.getAccount(accountIds.get(i));
// More logic
}
}
我想澄清一下这个问题 FeignssTokenRequestInterceptor是您的自定义RequestInterceptor,您正试图在apply方法中访问ServletRequest,这从堆栈跟踪中可以明显看出 RequestObjectFactory.getObject(WebApplicationContextUtils.java:320) 此RequestObjectFactory是一个静态内部类,它允许您访问当前请求
private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable {
private RequestObjectFactory() {
}
public ServletRequest getObject() {
return WebApplicationContextUtils.currentRequestAttributes().getRequest();
}
public String toString() {
return "Current HttpServletRequest";
}
}
如果您没有任何代码访问当前请求,并且它是从OpenFeign类文件调用的,那么唯一的方法是使用顺序流而不是并行流。这很简单
accountIds.stream().forEach(accountId -> {
AccountDTO accountDTO = accountMaintenanceRestClient.getAccount(accountId);
// More logic
}
我想澄清一下这个问题 FeignssTokenRequestInterceptor是您的自定义RequestInterceptor,您正试图在apply方法中访问ServletRequest,这从堆栈跟踪中可以明显看出 RequestObjectFactory.getObject(WebApplicationContextUtils.java:320) 此RequestObjectFactory是一个静态内部类,它允许您访问当前请求
private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable {
private RequestObjectFactory() {
}
public ServletRequest getObject() {
return WebApplicationContextUtils.currentRequestAttributes().getRequest();
}
public String toString() {
return "Current HttpServletRequest";
}
}
如果您没有任何代码访问当前请求,并且它是从OpenFeign类文件调用的,那么唯一的方法是使用顺序流而不是并行流。这很简单
accountIds.stream().forEach(accountId -> {
AccountDTO accountDTO = accountMaintenanceRestClient.getAccount(accountId);
// More logic
}
您的
feignssotkenrequestinterceptor
依赖于HttpServletRequest
代理bean
请求是通过threadlocal公开的,因此threadpool中的线程不知道/不知道它,因此当它尝试从threadlocal映射读取请求时会发生异常
因此,问题基本上缩小到如何将请求设置为线程池的线程上下文
设置一个线程池,如引用的帖子,然后提交任务
@Autowired
ThreadPoolTaskExecutor poolExecutor;
CountdownLatch counter = new CountdownLatch(accountIds.size());
IntStream.range(0, accountIds.size()) // Note that .parallel is omitted
.forEach(() -> poolExecutor.submit(() -> {
try {
your task
} finally {
counter.countDown();
}
}))
counter.await(); // Wait for all tasks complete
这是首选的方式,因为默认的并行流用于公共共享池,所以您不应该将一个任务放置在一个具有类似IO的API访问权限的池中)您的
FeignssTokenRequestInterceptor
依赖于HttpServletRequest
代理bean
请求是通过threadlocal公开的,因此threadpool中的线程不知道/不知道它,因此当它尝试从threadlocal映射读取请求时会发生异常
因此,问题基本上缩小到如何将请求设置为线程池的线程上下文
设置一个线程池,如引用的帖子,然后提交任务
@Autowired
ThreadPoolTaskExecutor poolExecutor;
CountdownLatch counter = new CountdownLatch(accountIds.size());
IntStream.range(0, accountIds.size()) // Note that .parallel is omitted
.forEach(() -> poolExecutor.submit(() -> {
try {
your task
} finally {
counter.countDown();
}
}))
counter.await(); // Wait for all tasks complete
这是首选方式,因为默认并行流用于公共共享池,你不应该把一个任务放在一个像API一样有大量IO访问权限的地方)我认为这个bean
accountMaintenanceRestClient
必须是request
或session
Scoped共享accountMaintenanceRestClient的代码以及你是如何使用的。@Deadpool是的,看起来它是在线程绑定请求上被处理/调用/自动连接的一个顶级组件
,不应该处理。添加了剩余的客户端代码。@Nanor你能发布整个堆栈跟踪
错误吗?我想这个beanaccountMaintenanceRestClient
必须是请求
或会话
范围共享accountMaintenanceRestClient的代码以及您是如何使用的。@Deadpool是的,看起来它正在处理/调用/自动连接一个顶级组件,该组件在线程绑定请求中不应处理。添加了rest客户端代码。@Nanor您能发布整个堆栈跟踪错误吗?
accountIds.stream().forEach(accountId -> {
AccountDTO accountDTO = accountMaintenanceRestClient.getAccount(accountId);
// More logic
}
@Autowired
ThreadPoolTaskExecutor poolExecutor;
CountdownLatch counter = new CountdownLatch(accountIds.size());
IntStream.range(0, accountIds.size()) // Note that .parallel is omitted
.forEach(() -> poolExecutor.submit(() -> {
try {
your task
} finally {
counter.countDown();
}
}))
counter.await(); // Wait for all tasks complete