28

Enhanced CDI Contexts and Bulkheads With MicroProfile Context Propagation

 4 years ago
source link: https://www.tuicool.com/articles/fymuimY
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Enhanced CDI Contexts and Bulkheads With MicroProfile Context Propagation

DZone 's Guide to

Enhanced CDI Contexts and Bulkheads With MicroProfile Context Propagation

Learn more about how MicroProfile Context Propagation can provide enhanced CDI contexts and bulkheads.

Free Resource

Join the DZone community and get the full member experience.

When using CDI with asynchronous execution methods, such as a ManagedExecutorService , it's traditionally not possible to access all of CDI's scopes that were active in the originating thread. MicroProfile Context Propagation enables us to define and pass thread execution contexts to completion stages where our code can access various CDI contexts despite being executed asynchronously. Additionally, Context Propagation allows us to create managed executor services that can be injected and used inside our beans, for example, to realize bulkheads.

Let's create and use a request-scoped bean that is being used during the handling of a request. With plain CDI, we would not be able to access and lookup the bean within an asynchronous execution.

Have a look at the following code:

@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());
    }
}

If a client PUT s some content to the contexts/example resource, the method will update the request-scoped ExampleStore bean and execute the notification asynchronously, using the ManagedExecutorService . In order to enable the asynchronous execution to lookup the request-scoped store, we are using the ThreadContext to wrap the runnable with a context captured from the originating thread. This makes sure that the executed runnable can use the corresponding context.

We have to configure and produce a ThreadContext depending on which type of contexts (e.g. CDI , transaction , security ) we want to propagate:

public class ThreadContextProducer {

    @Produces
    ThreadContext threadContext() {
        return ThreadContext.builder()
                .propagated(ThreadContext.ALL_REMAINING)
                .build();
    }
}

This example will propagate all context types to the wrapped execution. Our bean then injects and uses the produced ThreadContext .

Defining Bulkheads Using Executors

MicroProfile Context Propagation allows us to create and configure ManagedExecutor s, a container-managed executor service similar to ManagedExecutorService . We can create a ManagedExecutor programmatically, set constraints on the allowed concurrency, and define a context propagation, as well.

Let's define the following asynchronous JAX-RS resources:

@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);
        });
    }

}

We're injecting two dedicated executors that are used to run the corresponding functionalities. The executors are created using a producer:

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();
    }
}

Our executors will have upper bounds of four concurrently executed completion stages, and four tasks in the queue. The contexts of the CDI and application context types will be propagated to the executing threads.

When injecting the executors, be aware of the scope of the injection point; here, we're using an application-scoped resource. Otherwise, we might create more than two executors, which would defeat the purpose of the bulkhead pattern. Since we're using CDI, it's, of course, possible to define additional qualifies if the created executors should be configured differently.

You can try out MicroProfile Context Propagation, for example, using the latest builds of Open Liberty. I've published an example repository on GitHub .

When we're running our applications on Open Liberty, MicroProfile Context Propagation executors are backed by the automatically-tuned global thread pool. You can have a look at the default thread pool metrics provided by Liberty, as shown here .

DOWNLOAD

Topics:

java , microprofile , microprofile context propagation , cdi , application , cdi contexts , bulkheads , code


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK