简体   繁体   English

RequestScoped 与 bean 重用

[英]RequestScoped with bean re-use

I have a class/bean that manages an object (in this example the EngineManager contains an Engine object).我有一个管理 object 的类/bean(在此示例中,EngineManager 包含一个 Engine 对象)。 The Engine object cannot be used concurrently and its initialization is a bit time consuming. Engine object 不能同时使用,初始化有点费时。 However it is possible to create multiple instances of the EngineManager and hence multiple Engine instances.但是,可以创建多个 EngineManager 实例,从而创建多个 Engine 实例。

public class EngineManager
{
    private Engine engine;

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

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

I'm trying to figure out which CDI scope to use for the class, that manages this object.我试图找出哪个 CDI scope 用于管理这个 object 的 class。

  • I don't want to make the class a singleton, since I can create multiple instances of it and a singleton would be a bottleneck.我不想让 class 成为 singleton,因为我可以创建它的多个实例,而 singleton 将成为瓶颈。
  • I cannot use @ApplicationScoped due to concurrency issues.由于并发问题,我无法使用 @ApplicationScoped。
  • I don't want to use RequestScoped, because to my understanding this creates a new instance for every single request and the costly initialization of the Engine object would be a lot of overhead.我不想使用 RequestScoped,因为据我了解,这会为每个请求创建一个新实例,并且引擎 object 的昂贵初始化将是很多开销。

So my question is: Is there a (CDI) way to所以我的问题是:是否有(CDI)方法可以

  1. make the access to the EngineManager class thread-safe and使对 EngineManager class 的访问是线程安全的,并且
  2. have multiple instances of the EngineManager class, that are reused?是否有多个 EngineManager class 实例被重复使用?

In short: To my knowledge, there is no way to solve this strictly within CDI without extra effort.简而言之:据我所知,没有额外的努力就无法在 CDI 内严格解决这个问题。 Here are some generic thoughts:以下是一些一般性的想法:

This problem is similar to that of the DB connection pool.这个问题与DB连接池的问题类似。 One way to solve it is with a pool of Engine instances, from which the EngineManager (s) pick.解决它的一种方法是使用EngineManager从中选择的Engine实例池。

Elaborating a bit, and if you use an engine pool, the EngineManager can be @ApplicationScoped , as long as the pool guarantees that each thread gets a different Engine .详细说明一下,如果您使用引擎池,则EngineManager可以是@ApplicationScoped ,只要该池保证每个线程都获得不同的Engine

An interesting aspect of this is how do you deal with unavailability of Engine instances.一个有趣的方面是如何处理Engine实例的不可用。 Throwing an exception is the simplest answer, but might not be appropriate for you use case.抛出异常是最简单的答案,但可能不适合您的用例。 Blocking the current thread (probably with a timeout) until an Engine is available is another sub-optimal solution because it will not scale well under traffic.Engine可用之前阻塞当前线程(可能有超时)是另一个次优解决方案,因为它在流量下无法很好地扩展。

If your environment allows, you may want to consider an asynchronous solution, in combination with the pool.如果您的环境允许,您可能需要考虑结合池的异步解决方案。 An ExecutorService (see ManagedExecutorService in JEE environments) where you submit tasks;您提交任务的ExecutorService (请参阅 JEE 环境中的ManagedExecutorService ); JMS or other queuing mechanism might be more complex to setup (again depending on your environment) but can offer reliability in the form of message persistence (if the server crashes after you submit your work but before retrieving the result, it can resume and complete the work when it comes back online). JMS 或其他排队机制的设置可能更复杂(同样取决于您的环境),但可以以消息持久性的形式提供可靠性(如果服务器在您提交工作后但在检索结果之前崩溃,它可以恢复并完成当它重新联机时工作)。 Going full async requires more effort, but might be more appropriate if your specific use case justifies it.完全异步需要更多努力,但如果您的特定用例证明它是合理的,则可能更合适。

Reactions to the comments:对评论的反应:

  • EJBs are the natural way for such use cases in traditional JEE applications. EJB 是传统 JEE 应用程序中此类用例的自然方式。 You will be using the facilities provided by the application server.您将使用应用程序服务器提供的设施。 (My instinct is to stay away of EJBs in the present day... just saying) (我的直觉是在当今远离 EJB ......只是说)
  • You are on Quarkus (good IMO).你在 Quarkus 上(好 IMO)。 If you go for a queue, you will have to setup a different system - you can judge if it is worth it.如果您将 go 用于队列,则必须设置不同的系统-您可以判断是否值得。 Quarkus supports asynchronous execution in many ways (and you may even want to try the reactive streams solutions). Quarkus 以多种方式支持异步执行(您甚至可能想尝试响应式流解决方案)。
  • I was not aware of the omniservices library mentioned.我不知道提到的全方位服务库。 It may suit your needs, but requires conversion to a Quarkus extension , as Quarkus does not support CDI portable extensions at this time, sadly.它可能适合您的需要,但需要转换为 Quarkus 扩展,因为 Quarkus目前不支持 CDI 便携式扩展,很遗憾。

Nikos answer is good , so this one is just to expand it a bit. Nikos 的回答很好,所以这个只是为了扩大一点。 There is indeed no ready-to-use solution for this problem.对于这个问题,确实没有现成的解决方案。 From what I understood, the main issue here is the Engine object and it's sharing.据我了解,这里的主要问题是Engine object 和它的共享。 You want to be able to hold n instances and distribute them between m EngineManager instances.您希望能够持有n实例并在mEngineManager实例之间分配它们。

Note that if you use @Inject to get Engine to EngineManager the engine is bound to the manager for the lifecycle of the manager.请注意,如果您使用@InjectEngine获取到EngineManager ,则引擎在管理器的生命周期内绑定到管理器。 So if you want to swap it dynamically (eg one manager using different engines for different invocations), then you would have to also use dynamic resolution ( Instance<T> ).因此,如果您想动态交换它(例如,一个管理器使用不同的引擎进行不同的调用),那么您还必须使用动态解析( Instance<T> )。 Based on this your EngineManager can be either dependent or application scoped.基于此,您的EngineManager可以是依赖的或应用程序范围的。

I can think of two ways to go about the Engine instance being shares and having multiple instances.我可以想到两种方法来 go 关于Engine实例是共享并具有多个实例。

  1. Create a bean that holds a @Dependent scope producer for Engine .Engine创建一个包含@Dependent scope 生产者的 bean。 Now this producer gets called for every injection of Engine and you can control what it returns.现在,每次注入Engine都会调用这个生产者,您可以控制它返回的内容。 The bean can hold a collection of Engines and sometimes it gives you an existing one if they are free, sometimes it could create new one. bean 可以包含一组引擎,有时如果它们是免费的,它会为您提供现有的引擎,有时它可以创建新的引擎。 Thread safety is up to you though!不过,线程安全取决于您!

  2. Define your own custom scope that will fit your needs.定义您自己的定制 scope 以满足您的需求。 This requires some expertise and usage of Quarkus specific APIs as in CDI you would normally use extensions but you cannot do that in Quarkus.这需要一些专业知识和Quarkus 特定 API的使用,因为在 CDI 中您通常会使用扩展,但在 Quarkus 中不能这样做。 For instance in Weld SE, you have @ThreadScoped which might be something that you could re-implement as custom scope in Quarkus and use in case you want an Engine to be on a per-thread basis.例如,在Weld SE 中,您有@ThreadScoped ,您可以在 Quarkus 中将其重新实现为自定义 scope 并在您希望Engine基于每个线程时使用。 However, custom scoped can really do next to anything, this is just an example.然而,自定义范围真的可以做任何事情,这只是一个例子。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM