当将CDI与异步执行方法(例如ManagedExecutorService
,传统上不可能访问在原始线程中处于活动状态的所有CDI范围。 MicroProfile Context Propagation可以定义线程执行上下文并将其传递到完成阶段,尽管我们的代码是异步执行的,但它们仍可以访问各种CDI上下文。 另外,Context Propagation允许创建托管的执行器服务,该服务可以注入到我们的bean中并在其中使用,例如实现舱壁。
增强的CDI上下文
让我们创建并使用在处理请求期间使用的请求范围的Bean。 使用普通的CDI,我们将无法在异步执行中访问和查找bean。
看下面的代码:
@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));
} }
@RequestScoped public class ExampleStore {
private String example;
public String getExample() {
return example;
}
public void setExample(String example) {
this .example = example;
} }
public class Notifier {
@Inject
ExampleStore exampleStore;
public void notifyAbout() {
System.out.println( "New example: " + exampleStore.getExample());
} }
如果客户端PUT
向contexts/example
资源提供了某些内容,则该方法将使用ManagedExecutorService
更新请求范围的ExampleStore
bean并异步执行通知。 为了使异步执行能够查找请求范围的存储,我们使用ThreadContext
将可运行对象与从原始线程捕获的上下文进行包装。 这样可以确保执行的可运行对象可以使用相应的上下文。
我们必须根据要传播的上下文类型(例如CDI , transaction , security )配置并产生ThreadContext
:
public class ThreadContextProducer {
@Produces
ThreadContext threadContext() {
return ThreadContext.builder()
.propagated(ThreadContext.ALL_REMAINING)
.build();
} }
本示例将所有上下文类型传播到包装的执行中。 然后,我们的bean注入并使用产生的ThreadContext
。
使用执行程序定义舱壁
MicroProfile Context Propagation允许创建和配置ManagedExecutor
,这是类似于ManagedExecutorService
的容器管理的执行器服务。 我们可以通过编程方式创建ManagedExecutor
,对允许的并发设置约束,并定义上下文传播。
通过使用专用执行器来实现特定功能,我们可以实现隔板模式,类似于使用MicroProfile Fault Tolerance或Porcupine 。
让我们定义以下异步JAX-RS资源:
@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);
});
} }
我们正在注入两个专用的执行器,它们用于运行相应的功能。 执行者是使用生产者创建的:
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,因此,如果应以不同的方式配置所创建的执行程序,则当然可以定义其他限定符。
您可以使用最新版本的Open Liberty试用MicroProfile Context Propagation。 我已经在GitHub上发布了一个示例存储库。
当我们在Open Liberty上运行应用程序时,MicroProfile Context Propagation执行程序将由自动调整的全局线程池提供支持。 你可以看看由自由所提供的默认线程池的指标,如图所示这里 。
更多资源