RequestScoped с повторным использованием бина - PullRequest
1 голос
/ 09 апреля 2020

У меня есть класс / компонент, который управляет объектом (в этом примере EngineManager содержит объект Engine). Объект Engine нельзя использовать одновременно, и его инициализация занимает немного времени. Однако можно создать несколько экземпляров EngineManager и, следовательно, несколько экземпляров Engine.

public class EngineManager
{
    private Engine engine;

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

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

Я пытаюсь выяснить, какую область CDI использовать для класса, который управляет этим объектом.

  • Я не хочу делать класс синглтоном, поскольку я могу создать несколько его экземпляров, и синглтон станет узким местом.
  • Я не могу использовать @ApplicationScoped из-за проблемы параллелизма.
  • Я не хочу использовать RequestScoped, потому что, насколько я понимаю, это создает новый экземпляр для каждого отдельного запроса, и дорогостоящая инициализация объекта Engine будет много накладных расходов.

Итак, мой вопрос: существует ли (CDI) способ

  1. сделать доступ к классу EngineManager потокобезопасным, а
  2. иметь несколько экземпляров EngineManager класс, которые повторно используются?

Ответы [ 2 ]

2 голосов
/ 09 апреля 2020

Короче говоря: Насколько мне известно, нет способа решить это строго в рамках CDI без дополнительных усилий. Вот некоторые общие c мысли:

Эта проблема похожа на что из пула соединений с БД. Один из способов решить эту проблему - использовать пул Engine экземпляров, из которого EngineManager (s) выбирают.

Немного проработав, и если вы используете пул двигателей, EngineManager может быть @ApplicationScoped, поскольку пул гарантирует, что каждый поток получает разные Engine.

Интересный аспект этого заключается в том, как вы справляетесь с недоступностью Engine экземпляров. Вызов исключения - самый простой ответ, но он может не подходить для вашего случая использования. Блокировка текущего потока (возможно, с тайм-аутом) до тех пор, пока Engine не станет доступным, является еще одним неоптимальным решением, поскольку он не будет хорошо масштабироваться под traffi c.

Если ваша среда позволяет, вы можете захотеть рассмотрим асинхронное решение в сочетании с пулом. ExecutorService (см. ManagedExecutorService в средах JEE), где вы отправляете задачи; JMS или другой механизм организации очередей может быть более сложным в настройке (опять же, в зависимости от вашей среды), но может обеспечить надежность в виде сохранения сообщений (если сервер падает после отправки работы, но до получения результата, он может возобновить и завершить работать, когда он возвращается онлайн). Полное асинхронное c требует больше усилий, но может быть более уместным, если ваш конкретный c вариант использования оправдывает его.

Реакции на комментарии:

  • EJB - это естественные способ для таких случаев использования в традиционных приложениях JEE. Вы будете использовать возможности, предоставляемые сервером приложений. (Мой инстинкт заключается в том, чтобы в настоящее время держаться подальше от EJB ... просто говорю)
  • Вы находитесь на Quarkus (хорошая IMO). Если вы go для очереди, вам придется настроить другую систему - вы можете судить, стоит ли это того. Quarkus поддерживает асинхронное выполнение во многих отношениях (и вы даже можете попробовать решения с реактивными потоками).
  • Я не знал о упомянутой библиотеке omniservices. Это может удовлетворить ваши потребности, , но требует преобразования в расширение Quarkus , так как Quarkus в настоящее время не поддерживает переносимые расширения CDI , к сожалению.
1 голос
/ 15 апреля 2020

Хороший ответ Никоса , так что это просто, чтобы немного его расширить. На самом деле не существует готового решения этой проблемы. Из того, что я понял, главная проблема здесь - это объект Engine и его совместное использование. Вы хотите иметь возможность хранить n экземпляров и распределять их между m EngineManager экземплярами.

Обратите внимание, что если вы используете @Inject для получения Engine до EngineManager, двигатель привязан менеджеру на протяжении всего жизненного цикла менеджера. Поэтому, если вы хотите поменять его динамически (например, один менеджер, использующий разные механизмы для разных вызовов), вам также придется использовать динамическое разрешение c (Instance<T>). Исходя из этого, ваш EngineManager может быть зависимым или иметь область действия приложения.

Я могу придумать два способа go, когда экземпляр Engine является общим и имеет несколько экземпляров.

  1. Создайте компонент, содержащий @Dependent производителя области действия для Engine. Теперь этот производитель вызывается для каждой инъекции Engine, и вы можете контролировать, что он возвращает. Бин может содержать коллекцию Двигателей, и иногда он дает вам существующий, если они свободны, иногда он может создать новый. Тем не менее, безопасность потоков зависит от вас!

  2. Определите свой собственный объем, который будет соответствовать вашим потребностям. Это требует некоторого опыта и использования спецификаций Quarkus c API , так как в CDI вы обычно используете расширения, но вы не можете сделать это в Quarkus. Например, в Weld SE у вас есть @ThreadScoped, который может быть чем-то, что вы можете повторно реализовать в качестве пользовательской области в Quarkus и использовать в случае, если вы хотите, чтобы Engine был для каждого потока , Тем не менее, пользовательская область может действительно делать что угодно, это всего лишь пример.

...