I noticed strange problem in my web app.
Web app is hosted on AWS, is made in SpringBoot framework and its main responsibility is consuming messages from MQ. Messages are consumed one by one - there is no concurrency.
I am using MQQueueConnectionFactory class to prepare my connection,
I properly set parameters like:
host
port
queue manager
channel
int property
client reconnect options
and all ssl required parameters.
All seems to work well all the time until something happen and messages from MQ are not consumed. I can see messages which are waiting for being consumed, but web app does not consume it. After web app is restarted all work fine. I think it can be problem with connection which seems to be broken after some time (several days). I set reconnect option to connection factory class and for now I do not know what to do else to fix it.
Have you ever met similar problem? Do you have any clues?
Related
I have a Spring Boot based messaging app sending/receiving JMS messages to/from IBM MQ queue manager.
Basically, it uses MQConnectionFactory to organize connection to IBM MQ and a JmsPoolConnectionFactory from messaginghub:pooledjms to enable JMS connection pool, which is removed from MQConnectionFactory in IBM MQ 7.x
The app uses two different appoach to work with JMS. A "correct" one runs a JMSListener to receive messages and then sends a response on each message using JmsTemplate.send(). And there is a second "troubling" approach, where the app sends requests using JmsTemplate.send() and waits for response using JmsTemplate.readByCorrelId() until received or timed out.
I say troubling because this makes JMS sessions last longer if the response is delayed and could easily exhaust IBM MQ connection limit. Unfortunately, I cannot rewrite the app at the moment to the first approach to resolve the issue.
Now I want to restrict the number of connections in the pool. Of course, the delayed requests will fail but IBM MQ connection limit is more important at the moment, so this is kind of appropriate. The problem is that even if I disable the JmsPoolConnectionFactory, it seems that MQConnectionFactory still opens multiple connections to the query manager.
While profiling the app I see multiple threads RvcThread: com.ibm.mq.jmmqi.remote.impl.RemoteTCPConnection#12433875[...] created by JMSCCMasterThreadPool and corresponding connections to the query manager in MQ Explorer. I wonder why there are many of them in spite of the connection pooling is removed from MQConnectionFactory? I suppose it should open and reuse a single connection then but it is not true in my test.
Disabling "troubling" JmsTemplate.readByCorrelId() and leaving only "correct" way in the app removes these multiple connections (and the waiting threads of course).
Replacing JmsPoolConnectionFactory with SingleConnectionFactory has not effect on the issue.
Is there any way to limit those connections? Is it possible to control max threads in the JMSCCMasterThreadPool as a workaround?
Because it affects other applications your MQ admins probably want you to not exhaust the overall Queue Manager's connection limit (MaxChannels and MaxActiveChannels parameters in qm.ini). They can help you by defining an MQ channel exclusively used by your application. By this, they can limit the number of connections of your application with the MAXINST / MAXINSTC channel parameter. You will get an exception when this number is exhausted which is appropriate as you say. Other applications won’t be affected anymore.
I would like to add some conditional logic to our Java application code for attempting to create a JMS Topic Connection. I have seen problems in the past stemming from attempting to create a connection when the MQ server had been restarted or was currently down. One improvement I added was to check for the quiescent state, and another was to increase the timer before attempting reconnection to our durable topic queue.
Is there a way to confirm with the MQ server/topic/channel that it is up and running and a connection request can safely be made?
The best way to confirm that a queue manager (and the channel you are using to connect to the queue manager) is up and running is to attempt to connect to it.
If your connection attempt fails, you will get an MQ Reason code telling you exactly why. This is a much better way to confirm than any administrative command, because it also confirms that your application, and it's security context is correct and able to connect to the queue manager. It is completely possible to have an up-and-running queue manager but an application that is not yet correctly configured to use it. So connect from the application and if it works, the queue manager is up-and-running.
Your comment about having an increased timer before attempting to reconnect after a failure is well made. It doesn't help anyone if you hammer the queue manager with lots of repeated and close together connection attempts until it is ready to accept your connection, but still anything that is going to test the availability of the queue manager needs to ultimately connect to it, so very simply, just connect.
I'm using a web-socket protocol in my spring boot application. There are multiple pods used, to handle heavy traffic. Now, having multiple pods is causing an issue. Let me brief it a bit,
Let's assume there are 2 pods (Pod 1, Pod 2). Angular UI is subscribing to spring boot application on the web-socket protocol, let's say via Pod 1. Now, the spring boot application sends a message to the UI, let's say its send via Pod 2, and this message is getting dropped (never reach the UI) since the web-socket connection was established via Pod 1.
Because of this, some messages are getting dropped, which are being sent to UI by other Pods (which were not used for the initial subscription process), and messages send via Pod which was used initially for subscription, only those messages are received at UI.
How to tackle this scenario, so that every message is send to UI in this multiple pods environment?
The solution to multiple pod issues is by using an external message broker (like RabbitMq, ActiveMq), instead of an in-memory message broker (default behavior).
You may face the below issues while implementing this (writing them down in one place so that you don't have to struggle much as I did 🙂),
Creating Auto-Delete Queues
When using external message broker, you might observe that the queues are created for every websocket connection, but they are not deleted when the websocket connection is over. We don't even need these queues. Hence come the need of Auto-Delete Queues. These auto-queues are automatically deleted when websocket connection is closed. How to declare auto-delete queues, its easy peasy
When using user destinations with an external message broker, check the broker documentation on how to manage inactive queues, so that when the user session is over, all unique user queues are removed. For example, RabbitMQ creates auto-delete queues when destinations like /exchange/amq.direct/position-updates are used. So in that case the client could subscribe to /user/exchange/amq.direct/position-updates. Similarly, ActiveMQ has configuration options for purging inactive destinations.
In simple terms, websocket client and websocket server should use /exchange/amq.direct/<anything> this exchange destination.
For more info, read the official docs
ssl/stomp protocol on Cloud instance
Another issue you might face when you are hosting you application to AWS or Azure or Google Cloud, is that they use ssl/stomp protocol, so you code which works fine in your local machine (since it uses stomp protocol) doesn't work fine in Cloud.
Broadcasting message from one pod to other pods
This issue is the same as written in this Stackoverflow question. [refer the question for clearance]
Now, lemme put up the code snippet and will add comments to indicate which part of snippet fixes which issue. Add it inside your configureMessageBroker method,
val tcpClient = new ReactorNettyTcpClient<>
(TcpClient.create()
.port(yourRabbitmqCloudStompPort)
.host(yourRabbitmqCloudHost)
.secure(SslProvider.defaultClientProvider()),
new StompReactorNettyCodec());
messageBrokerRegistry
// enables stompbroker, instead of in-memory broker
.enableStompBroker("/queue", "/topic", "/exchange")
.setClientLogin(yourRabbitmqCloudClientLogin)
.setClientPasscode(yourRabbitmqCloudClientPasscode)
.setSystemLogin(yourRabbitmqCloudSystemLogin)
.setSystemPasscode(yourRabbitmqCloudSystemPasscode)
// broadcast msg to every pod
.setUserDestinationBroadcast("/topic/unresolved-user-destination")
.setUserRegistryBroadcasr("/topic/user-registry")
// enables ssl/stomp protocol
.setTcpClient(tcpClient);
I have two applications deployed on Cloudfoundry: a service application that computes stuff (aka computeService) and a client application that renders html for us mortals to hit buttons on (aka clientService). I would like a controller in the clientService to send commands to the computeService (when mortals hit buttons). The broker and the computeService run on the same machine.
I know I cannot make remote AMQP connections into a service on cloudfoundry.com, but I assume I can make connections between applications. However, every sensible address combination for broker and clientService gives me the same error:
javax.jms.JMSException: Could not connect to broker URL: tcp://127.0.0.1:61616. Reason: java.net.ConnectException: Connection refused
Whatever address I try, I cannot post to the queue. The code works flawlessly on my local machine.
My question: can I use RabbitMQ to pass messages between the two applications on Cloudfoundry? And if so, which addresses should I use?
Thanx!
One way to try this out is to create two replicas of the rabbit message example at Spring Samples
...a message sender and a message receiver. When deployed, they should share the same rabbit service.
I pushed the rabbit message which worked for me to: rabbitmessage-sndrcv
I've created a console application that listens to a queue using WCF in the past and have no problems with that implementation.
My question:
If, instead of listening to the queue on a console application, I listen to a queue through my website, when would the message be picked up? Would it be instant, as is the case with the console app? Would the message only be received when someone requests a page on the site?
Regards.
A website is not a good host container for a MSMQ client. The reason is the app pool unloads during time of low traffic.
So effectively you are correct in that you will not consume message until the app pool is loaded.
However, that does not prevent others from sending you messages, as the queue receives the messages regardless of whether your client is loaded or not. These would then be stored until the client came back to consume them (providing the queues are durable).
A windows service would be a much more appropriate container.