Re: How do i handle long-running background tasks in J2EE?
This is a multi-part message in MIME format.
----------------12785812291875077434
Content-Type: text/plain; charset=iso-8859-1; format=flowed
Content-Transfer-Encoding: 8bit
On 2010-09-15 13:36:06 -0400, Tom Anderson said:
Hi all,
By 'long-running', i mean 'up to ten seconds'.
The context is a bog-standard e-commerce site. The task in question is
carrying out a credit card authentication and then submitting an order
to a backend system; most of the up-to-ten seconds is spent waiting for
the remote systems to respond. The page flow we want is:
1. User is on the order review page and clicks 'confirm'
2. User goes quickly to a 'were processing ur order lol' page, and waits
for it to sponteneously occur that ...
3. User arrives at the order confirmation page
....
The one thing i've come up with is using JMS. We could set up a queue,
have the request threads post orders onto it, then use a message-driven
bean to pull them off and deal with them. I have no idea how we'd make
that multithreaded, nor how we'd communicate completion back to the
request threads. In any case, i lean away from it, because we aren't
currently using JMS or EJB, and i fear they would be a bit of a pain to
incorporate into our architecture. It might be the way to go, but i'm
more interested in hearing about other ideas.
JMS is the API intended for this sort of use case, but that doesn't
make it easy. In an EE environment, messaging workflow goes roughly
like this:
1. The originator sends a message to a destination ("sup I have
orders") and continues immediately.
2. The container delivers the message to an available instance of an
MDB, as soon as one is available.
3. The MDB processes the message by calling external services, EJBs,
sending other messages, or what have you.
Some free concurrency is hiding in step 2, in that your container can
be configured with a pool of instances for each MDB, allowing multiple
messages to be handled simultaneously.
JMS participates in container-managed transactions, which (a) gets you
nearly-free retry semantics when an attempt to process a message fails
and (b) means you may need to set up XA support for your database.
Obviously, since your chosen workflow is request-response-shaped, you
do need a way to get those responses back to the originator. JMS has
some provisions for creating temporary destinations (queues, etc) as
needed, and it has some standard headers for including metadata in your
messages. Temporary queues and the reply-to header work acceptably well
for handling responses on a per-originator basis. However, the APIs for
managing temporary destinations are not very sophisticated; in
particular, there's no standard mechanism for expiring a temporary
destination if the user abandons the page before your app responds.
We ended up using HttpSessionBindingListener values stored in the
user's servlet session to clean up "abandoned" destinations in tandem
with some app logic to detect and resume incomplete requests if the
user retried after abandoning the request. You can probably also use
the container's timer service to handle some of that.
-o
----------------12785812291875077434
Content-Type: text/html; charset=iso-8859-1
Content-Transfer-Encoding: 8bit
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta http-equiv="Content-Style-Type" content="text/css">
<title></title>
<meta name="Generator" content="Cocoa HTML Writer">
<meta name="CocoaVersion" content="1038.32">
<style type="text/css">
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; line-height: 15.0px; font: 12.0px Monaco}
p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; line-height: 15.0px; font: 12.0px Monaco; min-height: 16.0px}
p.p3 {margin: 0.0px 0.0px 0.0px 12.0px; line-height: 16.0px; font: 12.0px Monaco; color: #001992}
p.p4 {margin: 0.0px 0.0px 0.0px 12.0px; line-height: 16.0px; font: 12.0px Monaco; color: #001992; min-height: 16.0px}
p.p5 {margin: 0.0px 0.0px 0.0px 0.0px; line-height: 16.0px; font: 12.0px Monaco; min-height: 16.0px}
p.p6 {margin: 0.0px 0.0px 0.0px 0.0px; line-height: 16.0px; font: 12.0px Monaco}
p.p7 {margin: 0.0px 0.0px 0.0px 0.0px; line-height: 16.0px; font: 12.0px Monaco; color: #001992; min-height: 16.0px}
</style>
</head>
<body>
<p class="p1">On 2010-09-15 13:36:06 -0400, Tom Anderson said:</p>
<p class="p2"><br></p>
<p class="p3">Hi all,</p>
<p class="p4"><br></p>
<p class="p3">By 'long-running', i mean 'up to ten seconds'.</p>
<p class="p4"><br></p>
<p class="p3">The context is a bog-standard e-commerce site. The task in question is carrying out a credit card authentication and then submitting an order to a backend system; most of the up-to-ten seconds is spent waiting for the remote systems to respond. The page flow we want is:</p>
<p class="p4"><br></p>
<p class="p3">1. User is on the order review page and clicks 'confirm'</p>
<p class="p3">2. User goes quickly to a 'were processing ur order lol' page, and waits</p>
<p class="p3"><span class="Apple-converted-space">? ? </span>for it to sponteneously occur that ...</p>
<p class="p3">3. User arrives at the order confirmation page</p>
<p class="p5"><br></p>
<p class="p6">...</p>
<p class="p7"><br></p>
<p class="p3">The one thing i've come up with is using JMS. We could set up a queue, have the request threads post orders onto it, then use a message-driven bean to pull them off and deal with them. I have no idea how we'd make that multithreaded, nor how we'd communicate completion back to the request threads. In any case, i lean away from it, because we aren't currently using JMS or EJB, and i fear they would be a bit of a pain to incorporate into our architecture. It might be the way to go, but i'm more interested in hearing about other ideas.</p>
<p class="p5"><br></p>
<p class="p6">JMS is the API intended for this sort of use case, but that doesn't make it easy. In an EE environment, messaging workflow goes roughly like this:</p>
<p class="p5"><br></p>
<p class="p6">1. The originator sends a message to a destination ("sup I have orders") and continues immediately.</p>
<p class="p6">2. The container delivers the message to an available instance of an MDB, as soon as one is available.</p>
<p class="p6">3. The MDB processes the message by calling external services, EJBs, sending other messages, or what have you.</p>
<p class="p5"><br></p>
<p class="p6">Some free concurrency is hiding in step 2, in that your container can be configured with a pool of instances for each MDB, allowing multiple messages to be handled simultaneously.</p>
<p class="p5"><br></p>
<p class="p6">JMS participates in container-managed transactions, which (a) gets you nearly-free retry semantics when an attempt to process a message fails and (b) means you may need to set up XA support for your database.</p>
<p class="p5"><br></p>
<p class="p6">Obviously, since your chosen workflow is request-response-shaped, you do need a way to get those responses back to the originator. JMS has some provisions for creating temporary destinations (queues, etc) as needed, and it has some standard headers for including metadata in your messages. Temporary queues and the reply-to header work acceptably well for handling responses on a per-originator basis. However, the APIs for managing temporary destinations are not very sophisticated; in particular, there's no standard mechanism for expiring a temporary destination if the user abandons the page before your app responds.</p>
<p class="p5"><br></p>
<p class="p6">We ended up using HttpSessionBindingListener values stored in the user's servlet session to clean up "abandoned" destinations in tandem with some app logic to detect and resume incomplete requests if the user retried after abandoning the request. You can probably also use the container's timer service to handle some of that.</p>
<p class="p5"><br></p>
<p class="p6">-o</p>
</body>
</html>
----------------12785812291875077434--