При использовании 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, как показано здесь .
Другие ресурсы
- GitHub пример проекта
- Распространение контекста MicroProfile
- Поддержка распространения контекста в Open Liberty
- Мониторинг Открытой Свободы с Прометеем и Графаной
Опубликовано на Java Code Geeks с разрешения Себастьяна Дашнера, партнера нашей программы JCG. См. Оригинальную статью здесь: Расширенные контексты и переборки CDI с распространением контекста MicroProfile. Мнения, высказанные участниками Java Code Geeks, являются их собственными. |