Outbox Quarkus Extension
Overview
This extension is inspired by theOutbox Event Routersingle message transformation (SMT). As discussed in the blog postReliable Microservices Data Exchange with the Outbox Pattern, microservices often need to exchange information with one another and an excellent way to deal with that is using the Outbox pattern combined with Debezium’s Outbox Event Router SMT.
The following image shows the overall architecture of this pattern:
The Outbox extension’s goal is to provide aQuarkusapplication with a reusable, highly configurable component that facilitates the use of the Outbox pattern paired with Debezium’s CDC connector pipeline to reliably and asynchronously share data with any consumer of said data.
Getting Started
In order to start using the Debezium Outbox Quarkus extension, the extension needs to be added as a part of the Quarkus application as follows:
io.debezium debezium-quarkus-outbox 2.1.2.Final
The extension provides the application with theio.debezium.outbox.quarkus.ExportedEvent
interface. It’s expected that an application class will implement this interface and that the event will be emitted using thejavax.enterprise.event.Event
class.
The |
Example
The following example illustrates an implementation of theExportedEvent
interface representing an order that has been created:
public class OrderCreatedEvent implements ExportedEvent { private static final String TYPE = "Order"; private static final String EVENT_TYPE = "OrderCreated"; private final long orderId; private final JsonNode jsonNode; private final Instant timestamp; public OrderCreatedEvent(Instant createdAt, Order order) { this.orderId = order.getId(); this.timestamp = createdAt; this.jsonNode = convertToJson(order); } @Override public String getAggregateId() { return String.valueOf(orderId); } @Override public String getAggregateType() { return TYPE; } @Override public JsonNode getPayload() { return jsonNode; } @Override public String getType() { return EVENT_TYPE; } @Override public Instant getTimestamp() { return timestamp; } @Override public Map getAdditionalFieldValues() { // no additional fields return Collections.emptyMap(); } }
The following example illustrates anOrderService
that emits theOrderCreatedEvent
:
@ApplicationScoped public class OrderService { @Inject OrderRepository orderRepository; @Inject Event> event; @Transactional public Order addOrder(Order order) { order = orderRepository.save(order); event.fire(new OrderCreatedEvent(Instant.now(), order)); return order; } }
When the application code fires the event by callingEvent#fire()
, the Outbox extension will be notified that the event occurred and persists the contents of the event into an outbox event table within the scope of the current transaction. The Debezium CDC connector in conjunction with the Outbox Event Router will be monitoring this table and will be responsible for relaying that data using CDC events.
To see a full end-to-end demo, theOutboxexample illustrates two Quarkus microservice applications using the outbox pattern to share data between them when orders are placed or cancelled.
Additional Field mappings
The Debezium Outbox SMT can be configured to read additional fields and emit those field values either as event headers, or as part of the event value.
In order to pass additional field mappings to be saved by the Quarkus Outbox extension, the configuration propertyquarkus.debezium-outbox.additional-fields
must be specified in theapplication.properties
. This configuration property is a comma-separated list of additional field definitions that will be added to the Outbox entity mapping and passed by the application’s implementation of theExportedEvent
interface.
Each entry in this comma-separated list must follow this format:
:[:[:]]
The pattern indicates that the field’s name and java-type are required while the column definition and JPA attribute converter are optional. However, please note that if you wish to specify a JPA attribute converter then the column definition must be specified.
The following example shows how to define an additional field calledcustomer_name
that is represented in Java as aString
and which should be stored in the outbox table as aVARCHAR(100)
column. This example also shows a JPA Attribute converter defined that forces the storage of the string to upper-case.
quarkus.开云体育官方注册网址debezium-outbox.additional-fields =客户_name:string:varchar(100):example.UpperCase
Once the field(s) are configured in the application’s.properties
file, the application’s code needs to provide the corresponding values through its exported events. In order to do this, the application class that extends theExportedEvent
needs to override the method calledgetAdditionalFieldValues()
and return aMap
of the additional field names and values.
In the following example, we show how to specify thecustomer_name
field with a value ofAcme Goods
. Using ourOrderCreatedEvent
from the example section above, we’ve extended the event:
public class OrderCreatedEvent implements ExportedEvent { ... @Override public Map getAdditionalFieldValues() { return Collections.singletonMap("customer_name", "Acme Goods"); } }
Additional field mappings do allow specifying a JPA attribute converter per field. In this example, we defined |
With the configuration in the application’s.properties
file and updating ofOrderCreateedEvent
to provide these additional fields and values, the Debezium Outbox SMT now can access these additional field values and place them in the emitted event.
Configuration
The Outbox extension can be configured by setting options in the Quarkusapplication.properties
file. The extension works out-of-the-box with a default configuration, but this configuration may not be ideal for every situation.
Build time configuration options
Configuration property |
Type |
Default |
|
string |
OutboxEvent |
|
string |
|
|
string |
|
|
string |
|
|
string |
|
|
string |
|
|
string |
|
|
string |
|
|
string |
|
|
string |
|
|
string |
|
|
string |
|
|
string |
|
|
string |
|
|
string |
|
|
string |
|
|
string |
|
|
string |
|
|
string |
|
|
string |
|
|
string |
|
|
string |
The build time configuration defaults will work with the Outbox Event Router SMT out of the box. When not using the default values, be sure that the SMT configuration matches. |
Runtime configuration options
Configuration property |
Type |
Default |
|
boolean |
true |
Distributed tracing
This feature is currently in incubating state, i.e. exact semantics, configuration options etc. may change in future revisions, based on the feedback we receive. Specifically, Distributed Tracing support will be replaced with support for the Open Telemetry specification in a future release. |
The extension has support for the distributed tracing. Seetracing documentationfor more details.