Author |
Message
|
JustinM |
Posted: Mon Jun 21, 2004 5:30 am Post subject: Multiple JMS clients prevent shared access to queue manager |
|
|
Novice
Joined: 10 Apr 2002 Posts: 13
|
Hi,
I have a situation where more than a single JMS client can create a connection to a 'shared' queue manager at the same time.
A client sets up the queue connection factory using the following:
Code: |
MQQueueConnectionFactory qcf = new MQQueueConnectionFactory();
qcf.setHostName(host);
qcf.setPort(port);
qcf.setQueueManager(qm);
qcf.setChannel(channel);
qcf.setCCSID(ccsid);
qcf.setTransportType(JMSC.MQJMS_TP_CLIENT_MQ_TCPIP);
QueueConnection connection = qcf.createQueueConnection();
QueueSession session = queueConnection.createQueueSession(false, Session.CLIENT_ACKNOWLEDGE);
try {
// do some work
}
finally {
sesson.close();
}
|
A client keeps a single QueueConnection open for the duration of it's life and does it's work within the confines of a QueueSession. This work involves putting a message on a request queue (with a given correlationId) and retrieving the 'correllated' message from a response queue. All works well when a single client is alive.
The problem that I am having is that when I start up more than a single client (in separate java vms) only the first client that connects to the queuemanager is able to retrieve correlated messages from the response queue. Subsequent clients are able to put messages on the request queue, but are never able to receive a message from the response queue. In fact event a simple queue browser is able to connect to the queue manager, but is not able to display the contents of the queue if another client has an open connection to the the same queue manager.
This behaviour is somewhat unexpected, and I'm not sure where to start looking for solutions. I'm not even sure if this is a programming error (i.e. code problem) or a problem with the setup of websphere mq (v 5.3). Has anyone had any previous experience with JMS and multiple clients? |
|
Back to top |
|
 |
fjb_saper |
Posted: Mon Jun 21, 2004 12:51 pm Post subject: |
|
|
 Grand High Poobah
Joined: 18 Nov 2003 Posts: 20756 Location: LI,NY
|
I have no problems with multiple clients in JMS.
My setup is :
access to queues is always shared
Sessions are always transacted and explicitly committed or rolled back(on JMSException for example)
However you need to be aware that messages currently part of a transaction are not available to the other clients.
Other consideration: Lifetime of the Qconnection:
Start qconnection -- put message-- wait for correlid (use message selector) -- get response message [| time out] -- stop qconnection
Total time on the queue 3 to 5 seconds, most of the time much less.
With start and stop QueueConnection you can reuse that connection anytime and it minimizes the time the connection is in use.
My experience with multiple clients:
Transacted sessions is the way to go.
I had over 15 different clients (each in its own JVM) consuming messages from the same queue concurrently. Each message = 1 transaction. No problems.
Remember for the request response model:
put message on queue 1 -- commit -- get response from queue 2 -- commit.
Have fun
F.J.  |
|
Back to top |
|
 |
JustinM |
Posted: Mon Jun 21, 2004 9:55 pm Post subject: |
|
|
Novice
Joined: 10 Apr 2002 Posts: 13
|
Hi,
Thanks for the reply.
Calling stop() on a QueueConnection allows other QueueConnections to receive messages - so this works now. (I have also made the sessions transacted and commit/rollback explicitly).
The setup as it now stands still bothers me for several reasons:
1) The solution is not scalable. Whenever a queueConnection is waiting for a correlated message (our timeouts are +-60 seconds) no other QueueConnection can access the shared queue, even if the other client is looking for a different correlated message.
2) The client is multithreaded (a CORBA client) so I will not be able to share the thread safe QueueConnection object but rather 'pool' them.
Surely there must be some way to get shared access to a queue that doesn't block other clients when the QueueConnection is open. I can understand that messages part of a transaction are not available to other clients, but I am using a highly specific message selector (based on correlationid).
Thanks |
|
Back to top |
|
 |
fjb_saper |
Posted: Tue Jun 22, 2004 8:18 am Post subject: |
|
|
 Grand High Poobah
Joined: 18 Nov 2003 Posts: 20756 Location: LI,NY
|
Hi Justin,
I don't know what your ultimate hang up is.
But in the case I described with 14 clients the setup was following:
a) each client was just another instance of the base client started from the command prompt in unix :
b) All the client did was:
-- connect in server mode to the QMGR (qcf) (qmgr on same box)
-- create qconnection
-- create qsession in transacted mode
-- create qreceiver
-- loop around receive nowait to process messages
-- commit for each message
-- when no more messages on queue
-- close receiver, session, connection
And they all ran fine simultaneously...
Depending on how many messages you expect in return...
-- if it is only one you will need to close the receiver after processing the message or timeout as it is not reusable due to the selector.
You specify a waiting time of 60 s. But what is your average waiting time?
We run a request response model from WEB intranet to CICS and back and there is no perceived wait time between the send and the response.
Your symptoms let me think that you are running multiple threads in the same JVM and not multiple clients in different JVM's.
Would that be possible ?
Make sure each thread acquires it's own instance of the QConnection
QSession, etc ...
Do you ever get a correlated message.
Can you send me the code creating the message selector and the receiver ?
Hope that helps some.
F.J. |
|
Back to top |
|
 |
jefflowrey |
Posted: Tue Jun 22, 2004 8:39 am Post subject: |
|
|
Grand Poobah
Joined: 16 Oct 2002 Posts: 19981
|
I'm starting to wonder if the queue hasn't had it's shareability changed to "Not shareable". _________________ I am *not* the model of the modern major general. |
|
Back to top |
|
 |
bower5932 |
Posted: Tue Jun 22, 2004 10:00 am Post subject: |
|
|
 Jedi Knight
Joined: 27 Aug 2001 Posts: 3023 Location: Dallas, TX, USA
|
If this were the case, I'd expect an underlying MQ exception. |
|
Back to top |
|
 |
JustinM |
Posted: Tue Jun 22, 2004 10:57 pm Post subject: |
|
|
Novice
Joined: 10 Apr 2002 Posts: 13
|
Hi,
Thanks for the replies. To answer your questions:
The queue is shareable. I tried changing it to "Not sharable" - under these circumstances the client throws an exception when another client is connected (as bower5932 mentioned).
FJ: Our average wait time is about 7 seconds. Some operations may take longer, but I timeout at 60 seconds.
As far as the physical setup goes: The only difference that I can see between your setup and mine is that the queue manager resides on a remote machine. Will this make a difference?
As for multiple thread in the same VM: Yes that is possible , but I am manually testing the client (single thread) against another client (different vm).
Another thing that I noticed: We have another java client connecting to websphere mq (same queue, channel and queue manager) using the mq base classes (not jms). When the jms version of the client is started up and the base java client is started (in that order), the base java client hangs at the method call: MQQueue.get(Message, GetMessageOptions), unless the QueueConnection is closed on the JMS client.
My understanding was that a QueueConnection (jms) is a threadsafe class - some of the websphere docs even advise caching the class as a static field in a session bean etc etc.
In summary - this is what I am doing (in a single client, of which there can be many):
- create queueconnection (once)
- start queueconnection
[unit of work]
- create transacted queue session
- create queue sender
- send message (with correlationid set)
- close sender
- commit
- create queue receiver (same session)
- wait for message (or timeout)
- close receiver
- commit
[end unit of work]
I am able to get a correlated message back from the queue when only one client is running. I have confirmed that the correlationId is being set and retrieved correctly.
In the interim I have rewritten the client using the base java classes and don't seem to have a problem (I need to test a little more though). Of interest is the way in which I open the queue:
Code: |
MQQueue responseQueue = mqQueueManager.accessQueue(
responseQueueName, MQC.MQOO_INPUT_SHARED | MQC.MQOO_INQUIRE);
|
Perhaps JMS does not support the notion of shared input and is therefore 'locking up' the queue? As mentioned in a previous mail when I call stop() on the queueConnection, other clients are able to use the queue.
Here is the code used to create the message selector/receiver:
Code: |
private Message getCorrelatedMessage(QueueSession session , String messageId, int timeout)
throws JMSException
{
char[] chars = messageId.toCharArray();
StringBuffer correlationId = new StringBuffer(51).append("ID:");
for (int i = 0; i < chars.length; i++) {
correlationId.append((chars[i] & 0xF0) >>> 4).append(chars[i] & 0x0F);
}
// padding (usually not required as our field is 24 chars = 48 + ID:) - but this may change
for (int i = correlationId.length(); i < 51; i++) correlationId.append('0');
// receive the message
QueueReceiver receiver = session.createReceiver(
session.createQueue(responseQueueName),
"JMSCorrelationID = '" + correlationId + "'");
return receiver.receive(timeout);
}
|
|
|
Back to top |
|
 |
fjb_saper |
Posted: Wed Jun 23, 2004 7:08 am Post subject: |
|
|
 Grand High Poobah
Joined: 18 Nov 2003 Posts: 20756 Location: LI,NY
|
Nothing looks wrong with your setup.
The average of 7 seconds wait time for the reply looks HUGE to me but may be justifiable.
As for the difference between client binding and server binding that would need testing. It should not however influence the way the connection happens to the queue. Check as well the connection attribute for the queue. (default bind on open)
The way you set up your msg selector looks complicated to me.
This looks easier to me:
Code: |
getmyconnection().start();
mysender.send(mymessage);
getmysession().commit();
String msgid = mymessage.getJMSMessageID();
String selector = "JMSMessageID='" + msgid + "'";
QueueReceiver receiver =
getmysession().createReceiver(getmyqueue(), selector);
setmyreceiver(receiver);
mymessage = (TextMessage)(getmyreceiver().receive(waittime));
if (mymessage!= null) process(mymessage);
getmysession().commit();
// put this into the finally part of the try
getmyreceiver().close();
getmyconnection().stop();
getmyconnection().close();
|
Now it is possible that in JMS there is no possibility of 2 processes doing the receiver.receive(waittime) at the same moment.
With the length of your average waittime, this could well be the reason.
Depending on the acceptable overhead you could create a temporary queue as a response queue. This way it would be non shareable but each request would have its own response queue. Base the temporary response queue on a model queue.
Let us know how you ended up solving it.
Thanks
F.J.  |
|
Back to top |
|
 |
vennela |
Posted: Wed Jun 23, 2004 7:29 am Post subject: |
|
|
 Jedi Knight
Joined: 11 Aug 2002 Posts: 4055 Location: Hyderabad, India
|
Quote: |
Now it is possible that in JMS there is no possibility of 2 processes doing the receiver.receive(waittime) at the same moment. |
This is not true because I have configured two MDBs to listen to the same queue and the messages were alternatively (atleast that's what I saw) invoking each MDB. |
|
Back to top |
|
 |
fjb_saper |
Posted: Wed Jun 23, 2004 7:38 am Post subject: |
|
|
 Grand High Poobah
Joined: 18 Nov 2003 Posts: 20756 Location: LI,NY
|
Ah but Vennela,
Quote: |
This is not true because I have configured two MDBs to listen to the same queue and the messages were alternatively (atleast that's what I saw) invoking each MDB.
|
MDB's do not use receiver.receive(waittime)....
MDB's use a MessageListener.
That's a little bit of a different method there.
Alas it could be used as well in the way he sets up his receive function
But the sequence needs to be ..
-- setup the Listener
-- put it on the queue
-- start the connection and timer
-- at time out stop the connection
-- hopefully the message will have been received in between
It will be a much messier setup... |
|
Back to top |
|
 |
vennela |
Posted: Wed Jun 23, 2004 8:22 am Post subject: |
|
|
 Jedi Knight
Joined: 11 Aug 2002 Posts: 4055 Location: Hyderabad, India
|
F.J
I slightly modified the PTPSample01 so that it only does a GET on a wait of 30 seconds. With this jmsget sample program with 30 seconds wait I have run 2 copies of it and I still could get messages to both of these (and I used client, (not server bindings) for QCF).
To prove further I have checked the QSTATUS and I could see two apps connected to GET messages. |
|
Back to top |
|
 |
fjb_saper |
Posted: Wed Jun 23, 2004 3:07 pm Post subject: |
|
|
 Grand High Poobah
Joined: 18 Nov 2003 Posts: 20756 Location: LI,NY
|
Vennela this is great!
The difficulty is in duplicating his setup with the long wait times.
Typically if the message is there the wait time is null.
So on a queue with a lot of messages you do your receiver.receive(waittime) and you receive your message immediately.
Now while you process the next thread/client is running receiver.receive(waittime), getting the message immidiately and processing and so on.
Imagine the scenario with 7 seconds average response time and a 60 seconds wait time.
Is there a way to check that the second client does not get
?
So if I had 2 clients connecting at the same time, and messages where returned in order client2 client1...
-- client 1 sends msg commits
-- client 1 does receiver.receive(waittime)
-- client 2 sends msg commits
-- client 2 does receiver.receive(waittime)
-- message return for client 2
-- message return for client 1
Normally I would expect client 2 to process his return message before client 1.
Justin tells us he has to wait until client 1 has his message to get the message for client 2.... In any case once client 1 has his message the retrieval for client 2's message should be immediate as the message is already there...
Does not make much sense to me... I expect something to not be quite as described in his setup... but I can't find what it would be...
F.J.  |
|
Back to top |
|
 |
maxis |
Posted: Thu Jun 24, 2004 3:22 am Post subject: |
|
|
Centurion
Joined: 25 Jun 2002 Posts: 144
|
JustinM ...
couple of questions .... ( which might lead to your answer )
1. When you say ..
Quote: |
Subsequent clients are able to put messages on the request queue, but are never able to receive a message from the response queue. |
What does the thread do in this interval ? just wait ? or any exception .. if there is/are any exception .. pl post that
2. Have you verified that 2 ( and subsequent ) clients have opened connection to the queue ? .. ( tip check the output count on the queue )
And last ... personally I would try to release the MQ related resources ... rather than to GC .. |
|
Back to top |
|
 |
JustinM |
Posted: Thu Jun 24, 2004 9:03 pm Post subject: |
|
|
Novice
Joined: 10 Apr 2002 Posts: 13
|
Quote: |
What does the thread do in this interval ? just wait ? or any exception |
The thread just waits using receive(long timeout). No exception is thrown. Another thread in a different VM (client) may be doing receive(long timeout) at the same time - and this is where the problem arises.
Quote: |
Have you verified that 2 (and subsequent) clients have opened connection to the queue? |
No. How do I do that?
Part of the problem that I am having in solving this issue is that I have no control over the management/administration of the queue. I can't even explore the queue remotely using the explorer (remote access has been disabled) and it is unlikely that the other party is going to let me fiddle. I have however raised this issue with them and strangely enough they mentioned that they have had a similar issue in the past using the JMS classes and therefore resorted to using MQ base classes for java. On top of all this they are in a middle of a large rollout and the system is in a continual state of flux. Once is is stable again I am going to run the tests against it and will get back to you.
My gut feeling is that my code is not the culprit and that there may be something that the other party has done during the setup/admin of MQSeries that has resulted in this problem. What they have done I don't know but it seems really strange that no-one had had / can duplicate this problem.
Justin |
|
Back to top |
|
 |
fjb_saper |
Posted: Sat Jun 26, 2004 5:19 am Post subject: |
|
|
 Grand High Poobah
Joined: 18 Nov 2003 Posts: 20756 Location: LI,NY
|
Hi Justin,
Courtesy of one of jeff's and Peter's posts I stumbled on something that might shed a little bit more light on your problem:
Quote: |
Q:When my Java applet makes a WebSphere MQ call, why are all other WebSphere MQ calls blocked?
A: The implementation of the Java client ensures that, for a given connection (queue manager object instance), all access to the target queue manager is synchronized. This means that a thread wishing to issue a call to a queue manager is blocked until all other calls in progress for that connection have completed. If you require simultaneous access to the same queue manager from within your program, create a new queue manager object for each thread requiring concurrent access. (This is equivalent to issuing a separate MQCONN call for each thread).
|
Here is the url they mentioned and where I took this snippet from:
[url]
http://www.developer.ibm.com/tech/faq/results/0,1322,1%253A401%253A407%253A1%253Amqjava,00.html#q1
[/url]
I guess translating that into JMS, it means that each thread needs to use its own QueueConnection instance.
 |
|
Back to top |
|
 |
|