Статьи

Улучшенные контексты и переборки CDI с распространением контекста MicroProfile

При использовании CDI с асинхронными методами выполнения, такими как ManagedExecutorService , традиционно невозможно получить доступ ко всем областям CDI, которые были активны в исходном потоке. Распространение контекста MicroProfile позволяет определять и передавать контексты выполнения потоков на стадии завершения, где наш код может обращаться к различным контекстам CDI, несмотря на то, что выполняется асинхронно. Кроме того, распространение контекста позволяет создавать управляемые сервисы-исполнители, которые можно внедрять и использовать внутри наших компонентов, например, для реализации переборок.

Расширенные контексты CDI

Давайте создадим и используем bean-объект в области запроса, который используется во время обработки запроса. С простым CDI мы не смогли бы получить доступ и искать компонент в асинхронном выполнении.

Посмотрите на следующий код:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
@ApplicationScoped
@Path("contexts/example")
public class ThreadContextExampleResource {
 
    @Inject
    ExampleStore exampleStore;
 
    @Inject
    ThreadContext threadContext;
 
    @Resource
    ManagedExecutorService mes;
 
    @Inject
    Notifier notifier;
 
    @PUT
    public void setExample(String example) {
        exampleStore.setExample(example);
        mes.execute(threadContext.contextualRunnable(notifier::notifyAbout));
    }
}
01
02
03
04
05
06
07
08
09
10
11
12
13
@RequestScoped
public class ExampleStore {
 
    private String example;
 
    public String getExample() {
        return example;
    }
 
    public void setExample(String example) {
        this.example = example;
    }
}
1
2
3
4
5
6
7
8
9
public class Notifier {
 
    @Inject
    ExampleStore exampleStore;
 
    public void notifyAbout() {
        System.out.println("New example: " + exampleStore.getExample());
    }
}

Если клиент PUT некоторый контент в ресурс ExampleStore contexts/example , метод ExampleStore bean-компонент ExampleStore в области запроса и выполнит асинхронное уведомление, используя ManagedExecutorService . Чтобы включить асинхронное выполнение для поиска хранилища в области запросов, мы используем ThreadContext чтобы обернуть исполняемый объект контекстом, захваченным из исходного потока. Это гарантирует, что исполняемый runnable может использовать соответствующий контекст.

Мы должны сконфигурировать и создать ThreadContext зависимости от того, какой тип контекста (например, CDI , транзакция , безопасность ) мы хотим распространить:

1
2
3
4
5
6
7
8
9
public class ThreadContextProducer {
 
    @Produces
    ThreadContext threadContext() {
        return ThreadContext.builder()
                .propagated(ThreadContext.ALL_REMAINING)
                .build();
    }
}

В этом примере будут распространены все типы контекста для выполнения в оболочке. Наш компонент затем вводит и использует полученный ThreadContext .

Определение переборок с использованием исполнителей

Распространение контекста MicroProfile позволяет создавать и настраивать ManagedExecutor s, управляемую контейнером службу исполнителя, аналогичную ManagedExecutorService . Мы можем создать ManagedExecutor программно, установить ограничения на разрешенный параллелизм, а также определить распространение контекста.

Используя выделенных исполнителей для определенной функциональности, мы можем реализовать шаблон переборки, аналогичный использованию MicroProfile Fault Tolerance или Porcupine .

Давайте определим следующие асинхронные ресурсы JAX-RS:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@ApplicationScoped
@Path("bulkheads")
public class BulkheadExampleResource {
 
    @Inject
    ExampleStore exampleStore;
 
    @Inject
    Notifier notifier;
 
    @Inject
    ManagedExecutor writeExecutor;
 
    @Inject
    ManagedExecutor readExecutor;
 
    @GET
    public CompletionStage<String> example() {
        return readExecutor.supplyAsync(exampleStore::getExample);
    }
 
    @PUT
    public CompletionStage<Void> setExample(String example) {
        return writeExecutor.runAsync(() -> {
            exampleStore.setExample(example);
            writeExecutor.execute(notifier::notifyAbout);
        });
    }
 
}

Мы вводим двух выделенных исполнителей, которые используются для запуска соответствующих функций. Исполнители создаются с использованием производителя:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
public class ManagedExecutorProducer {
 
    @Produces
    ManagedExecutor managedExecutor() {
        return ManagedExecutor.builder()
                .propagated(ThreadContext.CDI, ThreadContext.APPLICATION)
                .maxAsync(4)
                .maxQueued(4)
                .build();
    }
 
    public void disposeManagedExecutor(@Disposes ManagedExecutor managedExecutor) {
        managedExecutor.shutdownNow();
    }
}

Наши исполнители будут иметь верхние границы четырех одновременно выполняемых этапов завершения и четырех задач в очереди. Контексты CDI и типы контекста приложения будут распространяться на исполняющие потоки.

При введении исполнителей, имейте в виду объем точки ввода; здесь мы используем ресурс области приложения, в противном случае мы могли бы создать более двух исполнителей, что противоречило бы цели шаблона переборки. Поскольку мы используем CDI, конечно, можно определить дополнительные квалификации, если созданные исполнители должны быть настроены по-другому.

Вы можете попробовать распространение контекста MicroProfile, например, используя последние сборки Open Liberty. Я опубликовал пример репозитория на GitHub .

Когда мы запускаем наши приложения в Open Liberty, исполнители распространения контекста MicroProfile поддерживаются автоматически настраиваемым глобальным пулом потоков. Вы можете посмотреть на показатели пула потоков по умолчанию, предоставляемые Liberty, как показано здесь .

Другие ресурсы

Опубликовано на Java Code Geeks с разрешения Себастьяна Дашнера, партнера нашей программы JCG. См. Оригинальную статью здесь: Расширенные контексты и переборки CDI с распространением контекста MicroProfile.

Мнения, высказанные участниками Java Code Geeks, являются их собственными.