It Has Channels, and It’s Not Mars
Spring Integration in Action - Part 3
Telling everyone who needs to know that a booking occurred
Up until this point, we have been looking at scenarios where a number of services are invoked in sequence with the output of one service becoming the input of the next service in the sequence. This works well when the result of a service invocation only needs to be consumed once; however, it is common that more than one consumer may be interested in receiving certain messages. In our current version of the channel configuration, bookings that have been successfully billed and have been recorded by the seat availability service pass directly into a queue for email confirmation. In reality, this information would be of interest to a number of services within our application and systems within the enterprise such as Customer Relationship Management systems, which track customer purchases to better target promotions, as well as finance systems monitoring the financial health of the enterprise as a whole.
In order to allow the delivery of the same message to more than one consumer, we will introduce a publish/subscribe channel after the availability check. The publish/subscribe channel provides one-to-many semantics rather than one-to-one semantics provided by most channel implementations. This can be particularly useful when we want the flexibility to easily add consumers to the configuration. If the name of the publish/subscribe channel is known, that is all that is required for the configuration of additional consumers, with no changes to the core application configuration.
The publish/subscribe channel does not in itself support queuing, although it does support asynchronous operation. This can be done by providing a task executor, which is then used to deliver messages to each of the subscribers in separate threads. This may, however, still block the main thread sending the message on the channel where the task executor is configured to use the caller thread or block the caller thread where the underlying thread pool is exhausted.
In order to ensure that a backlog in sending email confirmations does not block either the sender thread or the entire thread pool for the task executor, we can connect the new pub/sub channel to the existing email confirmation queue by means of a bridge. The bridge is an EIP pattern that supports the connection of two channels. So, this will allow the pub/sub channel to deliver to the queue and then have the thread return immediately.
Now that we have made it possible to connect one producer with multiple consumers by the means of a publish/subscribe channel, let's get to the last challenge and emerge victoriously with our drastically improved application: what if "first come, first served" isn't always right?
Some customers are more equal than others
In order to ensure that the airline's direct customers have the best possible user experience, we said we wanted to prioritise the processing of their requests to allow us to render the response as quickly as possible. Using a comparator that prioritises direct customers over indirect, we can modify the first channel to be a priority queue. This will cause the framework to instantiate an instance of PriorityChannel, which creates a queue that is able to prioritise the order in which the messages are received. In this case, we are providing an instance of class implementing Comparator<Message<?>>.
What you just saw is an example of applying different types of channels for solving the different requirements with which the application has challenged us. We started with the defaults and, as we worked through the example, we replaced several channel definitions with the ones that are most suitable for each particular situation encountered. What's most important is that every type of channel has its own justification, and what may be recommendable in one particular use case may not be something you want to use somewhere else. We illustrated the decision process with the criteria that we find the most relevant in every case.