RequestScoped 与 bean 重用

RequestScoped with bean re-use

我有一个管理对象的 class/bean(在这个例子中,EngineManager 包含一个 Engine 对象)。 Engine对象不能并发使用,初始化有点耗时。但是,可以创建 EngineManager 的多个实例,因此可以创建多个 Engine 实例。

public class EngineManager
{
    private Engine engine;

    @PostConstruct
    public void init()
    {
        this.engine = // ... perform costly initialization
    }

    public void doSomethingWithEngine()
    {
        // ...
    }
}

我正在尝试找出用于管理此对象的 class 的 CDI 范围。

所以我的问题是:是否有 (CDI) 方式

  1. 使对 EngineManager 的访问class 线程安全并且
  2. 有多个 EngineManager class 实例被重用?

简而言之:据我所知,如果不付出额外的努力,就无法严格地在 CDI 内解决这个问题。以下是一些通用的想法:

这个问题和数据库连接池的问题类似。解决它的一种方法是使用 Engine 个实例池,EngineManager(s) 从中选择。

详细一点,如果你使用引擎池,EngineManager可以是@ApplicationScoped,只要池保证每个线程得到不同的Engine即可。

其中一个有趣的方面是您如何处理 Engine 个实例的不可用性。抛出异常是最简单的答案,但可能不适合您的用例。阻塞当前线程(可能超时)直到 Engine 可用是另一个次优解决方案,因为它不会在流量下很好地扩展。

如果您的环境允许,您可能要考虑结合池的异步解决方案。提交任务的 ExecutorService(参见 JEE 环境中的 ManagedExecutorService); JMS 或其他队列机制的设置可能更复杂(再次取决于您的环境),但可以以消息持久性的形式提供可靠性(如果服务器在您提交工作后但在检索结果之前崩溃,它可以恢复并完成当它重新上线时工作)。完全异步需要更多的努力,但如果您的特定用例证明它是合理的,则可能更合适。

对评论的反应:

  • EJB 是传统 JEE 应用程序中此类用例的自然方式。您将使用应用程序服务器提供的工具。 (我的直觉是在今天远离 EJB……只是说)
  • 你在使用 Quarkus(好 IMO)。如果你去排队,你将不得不设置一个不同的系统——你可以判断它是否值得。 Quarkus 以多种方式支持异步执行(您甚至可能想尝试反应流解决方案)。
  • 我不知道提到的 omniservices 库。它可能适合您的需要,但需要转换为 Quarkus 扩展,因为 Quarkus does not support CDI portable extensions,很遗憾。

,所以这个只是稍微扩充一下。 这个问题确实没有现成的解决方案。据我了解,这里的主要问题是 Engine 对象及其共享。您希望能够持有 n 个实例并将它们分布在 m EngineManager 个实例之间。

请注意,如果您使用 @Inject 获取 EngineEngineManager,引擎将在管理器的生命周期内绑定到管理器。所以如果你想动态地交换它(例如,一个经理使用不同的引擎进行不同的调用),那么你还必须使用动态解析(Instance<T>)。基于此,您的 EngineManager 可以是依赖的或应用程序范围的。

我可以想到两种方法来处理 Engine 实例共享和拥有多个实例。

  1. 创建一个包含 Engine@Dependent 作用域生产者的 bean。现在,每次注入 Engine 都会调用此生产者,您可以控制它 returns 的内容。该 bean 可以包含一组引擎,有时它会为您提供一个现有的引擎(如果它们是免费的),有时它可以创建一个新的引擎。 线程安全取决于您!

  2. 定义适合您需要的自定义范围。这需要一些 Quarkus specific APIs as in CDI you would normally use extensions but you cannot do that in Quarkus. For instance in Weld SE, you have @ThreadScoped 的专业知识和用法,这可能是您可以在 Quarkus 中作为自定义范围重新实现的东西,并在您希望 Engine 以每个线程为基础的情况下使用。然而,自定义范围确实可以做任何事情,这只是一个例子。