【问题标题】:Accessing request scoped beans in a multi-threaded web application在多线程 Web 应用程序中访问请求范围的 bean
【发布时间】:2011-10-03 16:42:28
【问题描述】:

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

问题: 现在这种方法的问题在于,当线程正在执行它们的 db 调用并且 Aspect 插入时,我们会收到 java.lang.IllegalStateException: No thread-bound request found 异常。我理解这是完全有效的,因为线程是在请求上下文之外执行的。

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

【问题讨论】:

    标签: java multithreading spring


    【解决方案1】:

    我认为您不能直接执行此操作。就算可以,也会有点丑。但是,您可以生成一个唯一的请求标识符(甚至 - 使用会话 ID,但要小心使用多个选项卡),并将其传递给每个处理线程。然后方面可以使用该 id 作为缓存的键。缓存本身也是单例的,但会有Map<String, X>,其中String 是ID,X 是你的缓存结果。

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

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

    【讨论】:

    • 我考虑过这种方法,但我发现的一个缺点是它会迫使我将唯一的请求标识符下游传递给我想要的所有方法调用(在线程执行期间)缓存。另外,缓存不会在一段时间后变得很大吗?这将迫使我定期管理它。也许我在这里遗漏了一些东西。
    • 也许有一个“线程执行上下文”,我可以使用它来存储我的唯一请求标识符并在方面检索它,而不必自己传递它?
    • 我设法解决了这个问题。我开始使用SimpleAsyncTaskExecutor 而不是WorkManagerTaskExecutor。好处是SimpleAsyncTaskExecutor 永远不会重用线程。这只是解决方案的一半。解决方案的另一半是使用RequestContextFilter 而不是RequestContextListenerRequestContextFilter 有一个 setThreadContextInheritable() 方法,它基本上允许子线程继承父上下文。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-12-12
    • 2012-11-13
    • 2014-11-26
    • 1970-01-01
    • 1970-01-01
    • 2012-05-26
    • 1970-01-01
    相关资源
    最近更新 更多