Bulkheads

Ships contain bulkheads that divide the vessel into several areas. If the ship hull gets a leak in some locations, only a single area is filled with water and the whole ship is likely still able to float.

The bulkhead pattern takes this idea to enterprise applications. If some component of the application fails or is working to capacity due to workload, then the rest of the application should still be able to fulfill its purpose. This, of course, highly depends on the business use case.

One example is to separate the thread execution of business processes from HTTP endpoints. Application servers manage a single pool of request threads. If, for example, a single business component fails and blocks all incoming requests, all available request threads will eventually be occupied. The result is in no other business uses cases is being able to be invoked, due to unavailable request threads. This could be the case if used clients don't implement proper timeouts, connect against a system that is down, and block the execution.

Using asynchronous JAX-RS resources together with dedicated managed executor services can relieve this issue. As seen earlier in this book, JAX-RS resources can invoke the business functionality in separate, container-managed threads to prevent the overall execution utilizing a request thread. Multiple components can use independent thread pools, which prevent failures from spreading.

Since the application server is responsible for managing threads, this approach should be implemented following Java EE standards. The idea is to define dedicated executor services that are injectable at the required positions.

The open source library Porcupine by Adam Bien uses this approach to create dedicated executor services that use ManagedThreadFactory to define thread pools with container-managed threads. The dedicated executor services can be configured and instrumented appropriately.

The following snippet shows one example of the bulkheads pattern, combining asynchronous JAX-RS resources with dedicated executor services:

import com.airhacks.porcupine.execution.boundary.Dedicated;
import java.util.concurrent.ExecutorService;

@Path("users") @Produces(MediaType.APPLICATION_JSON) public class UsersResource { @Inject @Dedicated("custom-name") ExecutorService executor; @GET public CompletionStage<Response> get() { return CompletableFuture .supplyAsync(this::getUsers, executor) .thenApply(s -> Response.ok(s).build()); } ... }

The business use case is executed in a managed thread provided by the executor service, in order to allow the request thread to return and to handle other requests. This enables other functionality of the application to still function, even if this part is overloaded, and utilizes all threads of the custom-name executer.

The following examines how the custom executor service is configured.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.39.133