It Has Channels, and It’s Not Mars

Spring Integration in Action

MarkFisher
Spring-Integration-in-Action

An overview of Spring Integration’s message transport conduits, their individual characteristics, and how you can get the most of your application by using the right kind of channel for the job.

One newer member of the Spring family, Spring
Integration, provides easy to use implementations of common
integration patterns. This article taken from Spring Integration
in Action
, offers an overview of Spring Integration’s message
transport conduits, their individual characteristics, and how you
can get the most of your application by using the right kind of
channel for the job.

Messages don’t achieve anything sitting all by themselves. In
order to do something useful and pass along the information they’re
packaging, they need to travel from one component to another and,
for this, we need channels, which are well-defined conduits for
transporting messages across the system.

To use a letter analogy, the sender creates a letter and hands
it off to the mailing system by depositing it into a well-known
location, which is the mailbox. From there on, the letter is
completely under the control of the mailing system, which delivers
it to various waypoints, until it reaches the recipient. The most
that the sender can expect is another reply—but, otherwise, who
routes the message and who the physical recipient of the letter is
(if, for example, the letter is addressed to an organization rather
than a person) are things that are not known to it. From a logical
standpoint, the channel is very much like a mailbox—a place where
certain components, called Producers, deposit messages that are
later on processed by other components, called Consumers. This way,
Producers and Consumers are decoupled from each other and are only
concerned about what kinds of messages they can send and receive,
respectively.

One distinctive trait of Spring Integration, which
differentiates it among other enterprise integration frameworks, is
the emphasis that the channels play in defining the enterprise
integration strategy. They’re not just plain information transfer
components, but they play an active role in defining the overall
application behavior. The business processing takes place in the
endpoints, but you just have to alter the channel configuration to
completely change the application’s runtime characteristics.

So, you’ll see channels presented from a logical perspective,
but you’ll also get an overview of the various channel
implementations provided by the framework, their individual
characteristics, and how you can get the most of your application
by using the right kind of channel for the job.

Using channels to move messages

To connect the producers and consumers configured within the
application we use a channel. All channels within Spring
Integration implement the MessageChannel interface shown below,
which defines standard methods for the sending of messages. Note,
however, that it provides no methods to allow the receipt of
messages.

The reason for this is that the message channels provided by
Spring Integration fall into two distinct categories that primarily
differ with regard to the mechanism for handing the message over to
the next endpoint.

Do you have any message for me?

PollableChannels as defined by the interface shown below require
the receiver or the framework acting on behalf of the receiver to
carry out a periodic check to see if messages are available on the
channel. This approach has the advantage that the consumer can
process messages at a time of their choosing. The approach can also
have downsides, creating a tradeoff between longer poll periods,
which create latency in the processing of a message, and
computation overhead from more frequent polls that find no
messages.

I’ll let you know when I’ve got something

The alternative offered by Spring Integration is the
SubscribableChannel interface, which is implemented by channels
that take responsibility for notifying subscribers when a message
is available. In most cases, however, the JOJO consumer code will
remain unaware of the capabilities of the channel from which
messages are received since the framework will take responsibility
for subscribing or polling, as appropriate.

 

While it is important to understand the implications of whether
a channel pushes out messages or is periodically polled, in most
cases the framework will take on the concern of connecting a
consumer to a channel, alleviating the complications of defining
the appropriate consumer types. To put it plainly, your job is to
select the appropriate channel type, and the framework will select
the appropriate consumer type (polling or event driven).

The right channel for the job

Spring Integration offers a number of different channel
implementations and since is an interface you are also free
MessageChannel to provide your own implementations. The type of
channel selected will have significant implications for your
application, including transactional boundaries, latency, and
overall throughput. This section will walk you through the factors
to consider and a practical scenario of selecting appropriate
channels. In the configuration, we will use the namespace but we
will also discuss which concrete channel implementation will be
instantiated by the framework.

In our flight-booking Internet application, a booking
confirmation results in a number of actions. Foremost in the mind
of many businesses is the need to get paid, so making sure that we
can charge the provided credit card is a high priority. We will
also want to update the number of available seats to ensure we
don’t overbook the flight. The system will also be required to send
a confirmation email with details of the booking and additional
information on the check in process. In addition to a website, our
Internet booking application exposes a REST interface to allow
third-party integration for flight comparison sites and resellers.
Since most of the airline’s premium customers come through the
airline’s own website, any design should allow us to prioritise
bookings originating from there over third-party integration
requests in order to ensure that the airline’s direct customers
experience a responsive website even in times of high load.

  

Is choosing a channel all that difficult?

The selection of channels will be based on both functional and
nonfunctional requirements, and there are several decision factors
that can help us making the right choice. This section provides a
brief overview of the technical criteria and the best practices
that you should consider when selecting the correct channels.

Sharing context

▪ Do we need to propagate context information between the
successive steps of a process? Thread local variables are used to
propagate context when needed in several places but where passing
via the stack would needlessly increase coupling, for example,
transaction context.

▪ Relying on the thread context is in itself a subtle form of
coupling and will have an impact when considering the adoption of a
highly asynchronous SEDA model. It may prevent splitting the
processing in concurrently executing steps, prevent partial
failures, or introduce security risks like leaking permissions to
the processing of different messages.

 

Atomic boundaries ▪ Do we have “all or nothing” scenarios? Classic example: bank
transaction where credit and debit should either succeed or
fail.
▪ Typically used to decide transaction boundaries and, because of
that, it is a specific case of context sharing. It influences the
threading model and therefore limits the available options when
choosing a channel type.
Buffering messages ▪ Do we need to consider variable load? What is immediate and
what can wait?
▪ The ability of systems to withstand high loads is an important
performance factor, but load is typically fluctuating, so adopting
a thread-per-message processing scenario will require more hardware
resources for accommodating the peak load situations. Those
resources will stay unused when the load decreases, so this could
be expensive and inefficient. Moreover, some of the steps may be
very slow, so resources may be blocked for a long amount of
time.
▪ Consider what requires an immediate response and what can be
delayed and use a buffer to store incoming requests at peak rate
and allow the system to process them at its own pace. Consider
mixing the types of processing. For example, an online purchase
system that will acknowledge the receipt of the request and do some
mandatory steps immediately (credit card processing, order number
generation) and respond to the client but will do the actual
handling of
Blocking and non- blocking operations ▪ How many messages can we actually buffer? What should we do
when we can’t cope with demand?
▪ If our application is unable to cope with the number of messages
being received and no limits have been in place, we may simply
exhaust our capacity for storing the message backlog or breach
quality of service guarantees in terms of response
turnaround.
▪ Recognizing that the system cannot cope with demands is usually a
better option than continuing to build up a backlog.
▪ A common approach is to apply a degree of self-limiting to the
system. This can be achieved by blocking the acceptance of new
messages when the system is approaching its maximum capacity. This
can commonly be a maximum number of messages awaiting processing or
a measure of requests received per second.
▪ Where the requester has a finite number of threads for issuing
requests, blocking those threads for long periods of time may
result in timeouts or quality of service breaches. It may be
preferable to accept the message and discard it later if system
capacity is exceeded, or set a timeout on the blocking operation to
avoid indefinite blocking of the requester.
Consumption model ▪ How many components are interested in receiving a particular
message?
▪ There are two major messaging paradigms: point-to-point or
publish-subscribe. In the former, a message is consumed by exactly
one recipient connected to the channel (even if there are more of
them); in the latter, the message is received by all of them.
▪ If the processing requirements are that the same and a message
should be handled by multiple consumers, they can do this
concurrently, and a publish/subscribe channel can take care of
that. An example is a mash-up application that aggregates results
from searching flight bookings. Requests are broadcast
simultaneously to all potential providers, which will respond by
indicating whether they can offer a booking or not.
▪ Conversely, if the request should be always handled by a single
component (for example, processing a payment), a point-to-point
strategy is what we are looking for.

Let’s see how these criteria apply to our flight booking
sample.

A channel selection example

Using the default channel throughout, we have three channels:
one accepting requests and the other two connecting our
services.

In Spring Integration, the default channels are
SubscribableChannels, and the message transmission is synchronous.
The effect of this is simple—we have one thread responsible for
invoking the three services sequentially, as shown in figure 1.

Because all operations are executing in a single thread, there
is a single transaction encompassing those invocations. If the
transaction configuration does not require new transactions to be
created for any of the services, the three service invocations will
occur within the scope of transaction.

Figure 2 shows what you get when you configure an application
using the default channels, which are subscribable and
synchronous.

Having all service invocations happening in one thread and
encompassed by a single transaction is a mixed blessing: it could
be a good thing in certain applications where all three operations
must be executed atomically but takes its toll on the scalability
and robustness of the application.

But email is slow and our servers are
unreliable

The basic configuration is all well and good in the sunny day
case when the email server is always up and responsive and the
network is 100 percent reliable. Reality is different. Our
application needs to work in a real world where the email server is
sometimes overloaded and the network sometimes fails. Analysing our
actions in terms of what we need to do now and what we can afford
to do later is a good way of deciding on what service calls we
should block. Billing the credit card and updating the seat
availability are clearly things we need to do now in order to be
able to respond with confidence that the booking has been made.
Sending the confirmation email is not time critical and we don’t
want to refuse bookings simply because the mail server is down.
Therefore, introducing a queue between the mainline business logic
execution and the confirmation email service will allow us to do
just that—charge the card, update availability, and send the email
confirmation when we can.

Introducing a queue on our emailConfirmationRequests channel
will allow the thread passing in the initial message to return as
soon as the credit card has been charged and the seat availability
has been updated. Changing the Spring Integration configuration to
do this is as simple as adding a child <queue/> element to
the <channel/>

Let’s recap how the
threading model changes by introducing the QueueChannel, as shown
in figure 3.

Because now there isn’t a single thread context that encompasses
all invocations, the transaction boundaries change as well.
Essentially, every operation that is executing on a separate thread
executes in a separate transaction, as shown in figure 4.

We have replaced one of the default channels with a buffered
QueueChannel and have set up an asynchronous communication model.
What we got was some confidence that long-running operations will
not block the application because some component is down or just
takes a long time to respond. But, now we have another challenge:
what if we need to connect one producer with not only one but two
(or more) consumers?

  

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<?>>.

Summary

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.

Author
MarkFisher
Mark Fisher is an engineer within the SpringSource division of VMware and lead of the Spring Integration team. He is also a committer on the core Spring Framework, co-lead of the Spring AMQP project, and a contributor of messaging support to the Spring BlazeDS Integration project. In addition to his role as an engineer, Mark spends a significant amount of time working with customers as a consultant and trainer. The focus of such engagements is primarily in the realm of enterprise integration, messaging, and event-driven architecture. Mark is a frequent speaker at conferences and user groups in North America and Europe, and along with other Spring Integration committers, is an author of the forthcoming book,
Comments
comments powered by Disqus