views:

697

answers:

3

I have a requirement that a single JMS message sent by a client must be delivered reliably (exactly-once) to two systems. These 2 systems are not HA-enabled, so the best suggestion that I came up with is to:

  1. create single queue where client posts to

  2. set up two "intermediate" queues

  3. use a custom "DuplicatorMDB" that will read messages from the client queue and post them to two queues within the same transaction.

client->JMSDQ->DuplicatorMDB->Q1->MDB->System1
                           \->Q2->MDB->System2

Is there any existing functionality like that? What would be the proper way to balance the system to keep it stable if one or both of the backend systems are down?

The application server is WebLogic 10.

I can't use topics for this because in a cluster topics will cause too much message duplication. If we have 2 instances, then with topics it'll go like this:

 
client->Topic-->MDB1@server1->System1
           | \->MDB2@server1->System2 
           \---->MDB1@server2->System1 
            \--->MDB2@server2->System2 

Thus every message will be delivered twice to System1 and twice to System2 and if there'll be 8 servers in a cluster, each message will be delivered 8 times. This is what I'd really like to avoid...

Finally I got some time to test it and here is what I observed: 2 nodes in a cluster. 2 JMS servers: jms1 on node1, jms2 on node2. Distributed topic dt. MDB with durable subscription and jms-client-id=durableSubscriber. Started the system: 0 messages, mdb@node1 is up, mdb@node2 trying to connect periodically, but it can't because "Client id, durableSubscriber, is in use". As expected.

Sent in 100 messages: jms1@dt messages current = 0, messages total = 100, consumers current = 1 I can see that node1 processed 100 messages.
jms2@dt messages current = 100, messages total = 100 , consumers current = 1 i.e. "duplicate" messages are pending in the topic.

Sent in another 100 messages, 100 were processed on the node1, 200 pending on node2.

Rebooted node1, mdb@node2 reconnected to dt and started processing "pending" messages. 200 messages were processed on node2.

After node1 is up, mdb@node1 can't connect to the dt, while mdb@node2 is connected.

jms1@dt messages current=0, messages total = 0, consumers current = 0
jms2@dt messages current=0, messages total = 200, consumers current = 1

Send in 100 more messages, I see that all 100 messages are processed on node2 and discarded on node1.

jms1@dt messages current=0, messages total = 100, consumers current = 0
jms2@dt messages current=0, messages total = 300, consumers current = 1

Now I reboot node2, mdb@node1 reconnects to dt. After reboot mdb@node2 reconnects to dt and mdb@node1 gets disconnected from dt.

jms1@dt messages current=0, messages total = 100, consumers current = 1
jms2@dt messages current=0, messages total = 0, consumers current = 1

I send in 100 messages, all are processed on node2 and stored in the topic on node1:

jms1@dt messages current=100, messages total = 200, consumers current = 1
jms2@dt messages current=0, messages total = 0, consumers current = 1

Then I shut down node2 and I see 100 "pending messages" being processed on node1 after mdb@node1 reconnects to the topic.

So the result is: I sent 400 messages, 700 were processed by MDB out of which 300 were duplicates.

It looks like the MDB reconnection works good as expected, but the messages may be duplicated if a node hosting the "active" MDB goes down.

This might be a bug or a feature of weblogic JMS implementation.

+1  A: 

I haven't used Weblogic, but most JMS solutions have the concept of Queues and Topics. You want a JMS Topic. Subscribers register and the topic ensures that the message is delivered to each subscriber once.

Configuration details.

Update: If you are running into issues in a clustered environment, I would make sure that everything is configured properly (here is a guide for JMS Topic Clustering). It definitely sounds strange that Weblogic would fail so miserably when clustering. If that does not work, you could look into a 3rd party Messaging Queue, such as RabbitMQ, which support JMS and will definitely not have this issue.

John Paulett
There's a problem with topics in a cluster that will create too many message copies - see update to the post...
vinny_g
A: 

This is the kind of behaviour that an ESB implementation should enbale. In terms of processing overhead there would be no great difference, but it can be useful to have separation of concerns between "plumbing" and application code.

As it happens, the WebSphere JMS implementaion has support for installing mediations that address such requirements. I don't know whether WebLogic has something similar, or whether their associated ESB products are an option for you, but I would recommend investigating those capabilities. You currently have a simple requirement, and your code is surely sufficient, however it's quite easy to imagine how a few minor additional requirements (could we just convert this field from dollars to pounds before we transmit to that destination, could we not send messages with this content to that destination ...) and lo! you find yourself writing your own ESB.

djna
No need for an ESB IMO, this is what Topics are for.
Pascal Thivent
I was following on from the original claim that topics could not be made to work in the questioner's case. I think you've indicated that in fact topics can be used, in which case I agree that would be preferable. Again the general proinciple being, if at all possible avoid implenting plumbing yourself.
djna
A: 

[...] Thus every message will be delivered twice to System1 and twice to System2 and if there'll be 8 servers in a cluster, each message will be delivered 8 times. This is what I'd really like to avoid...

This is right for non-durable subscriptions, but not for durable. For durable, all MDBs share the same connection-id and subscription-id (based on the MDB name by default), so only one MDB will be able to attach and receive messages at a time. The first MDB to try will succeed in connecting, the others will detect a conflict and fail, but keep retrying. So using a durable topic subscription should do the trick.

Pascal Thivent
I guess you mean durable subscription, because this is the property of the receiver, not topic.If an MDB has a durable subscription (and it has to specify ClientID to do this), then it can only be deployed on one node in a cluster. In WLS deployment to the 2nd node fails because there is already a client with the same client ID.
vinny_g
From WLS10 docs: "The subscription ID must be unique on its topic, hence an MDB with a durable topic subscription cannot run on multiple server instances in a cluster." http://download.oracle.com/docs/cd/E12839_01/web.1111/e13719/message_beans.htm#i1161176
vinny_g
Yes, this is what I meant, I wasn't clear enough. Regarding the documentation, I'm not sure what you want to show. Why didn't you paste the whole paragraph: "The subscription ID must be unique on its topic, hence an MDB with a durable topic subscription cannot run on multiple server instances in a cluster. After a first instance of the MDB starts on a server instance in the cluster, an additional instance of the EJB can deploy successfully on another clustered server, but when the MDB starts, a conflict is detected and that instance of the MDB fails to fully connect to JMS."
Pascal Thivent
As I said, the first MDB will succeed in connecting and the others will fail but they will keep retrying. So if the server of the first MDB crashes, another MDB will take his place. In other words, you have high availability but without message duplication. Isn't this what you want? This should be easy to confirm with a prototype.
Pascal Thivent
Hmm.. I think I haven't been able to get the 2nd MDB deployed at all, but maybe i have to rerun the tests, will do that.
vinny_g
As per documentation and BEA support, you should. Please recheck.
Pascal Thivent