Skip to content

Commit 681ebfb

Browse files
authored
Update chapter13.adoc based on review feedbacks
Update chapter13.adoc based on review feedbacks
1 parent c76f393 commit 681ebfb

1 file changed

Lines changed: 39 additions & 19 deletions

File tree

modules/ROOT/pages/chapter13/chapter13.adoc

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Using this model, developers can build scalable and resilient microservices that
2525
* Managing backpressure in reactive messaging
2626
* Integration with other MicroProfile specifications
2727
* Reactive stream operations
28-
* Best practices and production patterns
28+
* Graph Validation and Deployment Checks
2929

3030
== Event-Driven Architecture (EDA) Overview
3131

@@ -101,7 +101,7 @@ Add the following dependency to your `build.gradle`:
101101
[source,groovy]
102102
----
103103
dependencies {
104-
compileOnly "org.eclipse.microprofile.reactive.messaging:microprofile-reactive-messaging-api:3.0.1"
104+
compileOnly "org.eclipse.microprofile.reactive.messaging:microprofile-reactive-messaging-api:{reactive-messaging-spec-version}"
105105
}
106106
----
107107

@@ -115,7 +115,7 @@ The exact connector dependency depends on the runtime you are using. The connect
115115

116116
Depending on your runtime and packaging model, you may also need to make the Kafka client libraries available to the server or application classloader.
117117

118-
Refer to your runtime's documentation for the correct connector artifact coordinates, supported brokers, and configuration details. For a complete channel configuration example, see the <<_named_channels_for_message_flow>> section.
118+
Refer to your runtime's documentation for the correct connector artifact coordinates, supported brokers, and configuration details. For a complete channel configuration example, see the <<named-channels-for-message-flow>> section.
119119

120120
== Defining Messaging Logic with CDI Beans
121121

@@ -185,18 +185,25 @@ public class OrderStatusEventHandler {
185185
@Inject
186186
NotificationService notificationService;
187187
188+
@Inject
189+
ManagedExecutor executor;
190+
188191
@Incoming("payment-authorized")
189-
public void onPaymentAuthorized(Long orderId) {
190-
Order updatedOrder = orderService.updateOrderStatus(orderId, OrderStatus.PAID);
191-
notificationService.notifyOrderStatusChanged(updatedOrder);
192+
public CompletionStage<Void> onPaymentAuthorized(Order order) {
193+
return CompletableFuture.runAsync(() -> {
194+
// Update status on the incoming Order entity
195+
Order updatedOrder = orderService.updateOrderStatus(order.getId(), OrderStatus.PAID);
196+
notificationService.notifyOrderStatusChanged(updatedOrder);
197+
}, executor);
192198
}
193199
}
194200
----
195201

196202
In this example:
197203

198204
* The `OrderStatusEventHandler` bean injects an `OrderService` for updating the persisted order and a `NotificationService` for sending payment confirmations.
199-
* The `onPaymentAuthorized` method reacts to the `payment-authorized` channel, showing how the fulfillment workflow continues asynchronously after the initial REST checkout has completed.
205+
* The `onPaymentAuthorized` method consumes the `Order` payload directly from the `payment-authorized` channel, matching the output type of the checkout producer pipeline.
206+
* The method returns a `CompletionStage<Void>` and offloads the blocking database and notification work to a managed thread pool via `ManagedExecutor`. The underlying message broker will only acknowledge the message *after* this asynchronous processing successfully completes.
200207
* This separation keeps the user-facing request/response path simple while moving cross-service processing into dedicated messaging handlers.
201208

202209
=== Scope Management in Messaging Beans
@@ -366,6 +373,10 @@ Use `Message<T>` when you need explicit acknowledgment control, access to metada
366373

367374
*4. Reactive Streams Processing*:
368375

376+
The MicroProfile Reactive Messaging specification is built on the *MicroProfile Reactive Streams Operators* specification and heavily utilizes `CompletionStage` for asynchronous operations. The core reactive types defined by the MicroProfile Reactive Messaging specification are the standard Reactive Streams interfaces: `Publisher<T>`, `Subscriber<T>`, and `Processor<T,R>`, along with their builder counterparts (`PublisherBuilder<T>`, `SubscriberBuilder<T>`, `ProcessorBuilder<T,R>`) from MicroProfile Reactive Streams Operators.
377+
378+
NOTE: Mutiny (`io.smallrye.mutiny`) is *not* part of the MicroProfile Reactive Messaging specification. It is the reactive library chosen by SmallRye Reactive Messaging (the reference implementation used by Quarkus). Because `Multi<T>` and `Uni<T>` implements the Reactive Streams `Publisher<T>` contract, it integrates seamlessly. However, you are free to use other reactive libraries. The examples in this chapter use Mutiny, but the patterns apply to any Reactive Streams-compatible type.
379+
369380
`Multi<T>` is a reactive type from the Mutiny library (`io.smallrye.mutiny.Multi`) that represents an asynchronous stream of zero or more items. It implements the Reactive Streams `Publisher<T>` contract and provides a rich set of operators for transforming, filtering, merging, and grouping streams in a composable, non-blocking way. Common operators include:
370381

371382
* `map(Function)` — transforms each item to a new value.
@@ -858,21 +869,30 @@ Process multiple messages concurrently while maintaining overall backpressure co
858869
----
859870
import io.smallrye.mutiny.Multi;
860871
import io.smallrye.mutiny.Uni;
872+
import jakarta.enterprise.concurrent.ManagedExecutorService;
873+
import jakarta.inject.Inject;
874+
875+
// ...
876+
877+
@Inject
878+
ManagedExecutorService executor;
861879
862880
@Incoming("tasks")
863881
@Outgoing("results")
864882
public Multi<Result> processConcurrent(Multi<Task> tasks) {
865883
return tasks
866-
.onItem().transformToUniAndMerge(task ->
867-
Uni.createFrom().completionStage(() ->
868-
CompletableFuture.supplyAsync(() -> process(task))
869-
),
870-
3 // Process up to 3 messages concurrently
871-
);
884+
.onItem().transformToUni(task ->
885+
Uni.createFrom().item(() -> process(task))
886+
.runSubscriptionOn(executor)
887+
)
888+
// Explicitly merge the Unis back into a Multi with a concurrency limit of 3
889+
.merge(3);
872890
}
873891
----
874892

875-
The concurrency limit (3 in this example) ensures that no more than 3 messages are processed simultaneously. This provides controlled parallelism for I/O-bound operations while preventing resource exhaustion.
893+
WARNING: Do not call `CompletableFuture.supplyAsync(() -> ...)` without an explicit executor. The no-argument overload submits work to the JVM-wide common `ForkJoinPool`, which is shared across the entire application, is sized for CPU-bound work, and carries no Jakarta EE context (security, transactions, `@RequestScoped` beans). In a MicroProfile application, always inject a `ManagedExecutorService` (from Jakarta Concurrency) and pass it as the second argument, or use Mutiny's `runSubscriptionOn(executor)` as shown above. Both approaches ensure proper container-managed thread execution and context propagation.
894+
895+
The concurrency limit (`3` in this example) ensures that no more than 3 messages are processed simultaneously. This provides controlled parallelism for I/O-bound operations while preventing resource exhaustion.
876896

877897
*Batching for Throughput*:
878898

@@ -911,12 +931,12 @@ import java.time.Duration;
911931
@Outgoing("rate-limited")
912932
public Multi<Data> rateLimit(Multi<Data> stream) {
913933
return stream
914-
.group().intoLists().of(10) // Batch 10 items
915-
.onItem().call(batch -> // Pause between batches
934+
.group().intoLists().of(10) // Batch 10 items
935+
.onItem().call(batch -> // Pause between batches
916936
Uni.createFrom().nullItem()
917937
.onItem().delayIt().by(Duration.ofMillis(100))
918938
)
919-
.onItem().transformToMultiAndConcatenate(Multi::createFrom().iterable);
939+
.onItem().transformToMultiAndConcatenate(Multi.createFrom()::iterable);
920940
}
921941
----
922942

@@ -1021,7 +1041,7 @@ MicroProfile Fault Tolerance complements reactive messaging by adding `@Retry`,
10211041

10221042
=== MicroProfile Metrics Integration
10231043

1024-
When MicroProfile Reactive Messaging 3.0.1 is used in an environment where MicroProfile Metrics is available, implementations automatically produce the following metrics in the `base` scope:
1044+
When MicroProfile Reactive Messaging {reactive-messaging-spec-version} is used in an environment where MicroProfile Metrics is available, implementations automatically produce the following metrics in the `base` scope:
10251045

10261046
[cols="2,1,1,2"]
10271047
|===
@@ -1098,7 +1118,7 @@ Reliable message processing is ensured through multiple acknowledgment strategie
10981118

10991119
The specification's seamless integration with other MicroProfile specifications enhances the overall capabilities of reactive messaging applications. MicroProfile Fault Tolerance provides retry, circuit breaker, and timeout patterns for resilience; Config enables externalized configuration for different environments; Metrics supports both automatic and custom observability; Health integrates with Kubernetes for readiness probes; and OpenAPI documents REST endpoints that bridge to messaging systems. The connector ecosystem, supporting Kafka, AMQP, JMS, and other brokers, handles protocol-specific details, serialization, and connection management, freeing applications to focus purely on message processing logic.
11001120

1101-
To continue your journey with MicroProfile Reactive Messaging, explore the complete https://download.eclipse.org/microprofile/microprofile-reactive-messaging-3.0.1/microprofile-reactive-messaging-spec-3.0.1.html[MicroProfile Reactive Messaging 3.0.1 Specification] for detailed API documentation and conformance requirements. Study connector-specific features for your chosen message broker in your runtime's documentation. Leverage the integration points with other MicroProfile specifications such as Fault Tolerance for resilience, Config for externalization, Metrics for observability, and Health for container orchestration.
1121+
To continue your journey with MicroProfile Reactive Messaging, explore the complete https://download.eclipse.org/microprofile/microprofile-reactive-messaging-{reactive-messaging-spec-version}/microprofile-reactive-messaging-spec-{reactive-messaging-spec-version}.html[MicroProfile Reactive Messaging {reactive-messaging-spec-version} Specification] for detailed API documentation and conformance requirements. Study connector-specific features for your chosen message broker in your runtime's documentation. Leverage the integration points with other MicroProfile specifications such as Fault Tolerance for resilience, Config for externalization, Metrics for observability, and Health for container orchestration.
11021122

11031123
By mastering these concepts and patterns, you can build modern, reactive microservices that handle high-volume data streams efficiently and reliably in production environments, taking full advantage of the reactive programming model to create responsive, resilient, and scalable distributed systems.
11041124

0 commit comments

Comments
 (0)