在多线程Web应用程序中访问请求范围的bean

r4j*_*j1v 8 java spring multithreading

场景:我们有一个在Websphere中运行的Spring托管Web应用程序.(Spring 3.0.x,WAS 7)webapp通过Spring WorkManagerTaskExecutor(配置线程池大小为10)利用Websphere的工作管理器来执行计算密集型数据库读取操作.所以基本上,一个请求来生成,比方说,生成10个不同的文档.要生成文档,只需要db读取来收集/处理数据.因此,我们基本上产生10个线程来处理10个文档,最后收集10个工作者返回的10个文档并合并它们并向客户端写回一个大响应.我们确定的是,当10个线程正在收集/处理数据时,会产生大量类似的数据库调用.所以我们想出的是围绕最常执行的db方法创建一个Aspect来缓存响应.方面配置为单例,方面使用的缓存自动连接到方面,并将范围设置为请求范围,以便每个请求都有自己的缓存.

问题:现在这个方法的问题是,当线程正在进行数据库调用而Aspect是interjects时,我们会遇到java.lang.IllegalStateException: No thread-bound request found异常.我理解的是完全有效的,因为线程正在请求上下文之外执行.

有没有办法绕过这个问题?是否可以将带有请求范围缓存的方面应用于这些线程调用的方法?

Boz*_*zho 5

我认为你不能直接这样做.即使你可以,也会有点难看.但是,您可以生成唯一的请求标识符(甚至 - 使用会话ID,但要小心多个选项卡),并将其传递给每个处理线程.然后,方面可以使用该id作为缓存的密钥.缓存本身也将是单例,但是会有Map<String, X>,StringID和X 在哪里是缓存结果.

为了使事情更容易处理,您可以使用@Async方法(而不是手动生成线程),并且每个@Async方法都可以将缓存ID作为其第一个参数传递.

(当然,您的异步方法应该返回,Future<Result>以便您可以在请求线程中收集它们的结果)

  • 我成功解决了这个问题.我开始使用`SimpleAsyncTaskExecutor`而不是`WorkManagerTaskExecutor`.好处是`SimpleAsyncTaskExecutor`永远不会重用线程.这只是解决方案的一半.解决方案的另一半是使用`RequestContextFilter`而不是`RequestContextListener`.`RequestContextFilter`有一个`setThreadContextInheritable()`方法,它基本上允许子线程继承父上下文. (6认同)