My app is a Spring based application. I'm using activemq as a broker. I manage in my app two different queues receiving messages.
For each queue, my app's aim is to listen to messages sent on the broker, then proceed the message (read it, performs database actions from informations coming to this message etc.), and then treat the next message (so the treatment is synchronous, I want to proceed messages in the order of their arrival).
My actual design is this one :
I create a thread
<bean id="pollThread" class="my.app.receiver" init-method="start" destroy-method="interrupt">
</bean>
run() method of the thread call in a while(true) loop a method which does :
This thread create a connection to active mq and block with receive
After receiving, i close the connection and treat the message (database stuff and so)
Treatment done, end of method
and then the treatment restart (listening with receive, treatment etc.)
My question is : is there any manner to design it better ? My only mandatory process is to proceed message in the order of arrival and do the treatment before handling the next message.
I've read a lot of thing about JMSTemplate and so on but i'm lost whith all the information.
Actually my best guess is too create a PooledConnectionFactory (because we will use only ActiveMQ and CachingConnectionFactory seems to have limitations that matters in our architecture) and to limit it to one concurrentConsumer. Then to use the MessageListener interface to proceed my message.
Thanks
You can use the Message Driven POJO infrastructure provided by the Spring framework. It will do the polling on the destination and recover in case of failures of the broker, etc.
You can change your code (say YourMessageListener) to implement MessageListener. Then the following config can get you started
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="messageListener" class="org.foo.YourMessageListener"/>
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://your-server:61616"/>
</bean>
<bean id="destination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="queue/yourQueue"/>
</bean>
<bean id="jmsContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="destination"/>
<property name="concurrency" value="1"/>
<property name="messageListener" ref="messageListener"/>
</bean>
</beans>
The important setting is the concurrency element which limits the number of threads that are can simultaneously handle messages on that destination. By setting it to one, only one thread consumes a message at a time.
See the documentation for more details
Related
I have a JMS queue implementation with JmsTemplate. I want to have more than one listener when a message is put in the queue, i.e. I want to use topic instead of queue.
I have configuration without JMS namespacing. What are the changes that need to be made to have multiple listeners listen on a topic when someone sends a message in a topic.
I guess you are probably using DefaultMessageListenerContainer. Just to be sure, you want that several individual components receive the same message (i.e. you don't want to process messages in parallel).
Assuming I got this right and component A and compoent B should receive the same message, you simply create two DefaultMessageListenerContainer instance on the same topic and you set the pubSubDomain property to true. Make sure you haven't set any concurrency on the listener container, or better yet, set the concurrency to 1 to make that explicit.
This would give something like
<bean id="listener1"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="pubSubDomain" value="true"/>
<property name="concurrency" value="1"/>
<property name="destinationName=" value="...."/> <!-- topic name -->
<property name="messageListener" ref="...."/>
</bean>
Then you should create a similar bean for the second component.
Since a month we have a reoccurring issue with activemq and spring. After a some time (between a day and a week) we have no more consumers and no new ones get started and the queue starts to fill up.
This setup ran for over a year, without any issues and as far as we can see nothing relevant has been changed.
An other queue we use also started to show the same behavior, but less frequent.
from the activemq webconsole ( as you can see lots of pending messages and no consumers)
Name ↑ Number Of Pending Messages Number Of Consumers Messages Enqueued Messages Dequeued Views Operations
queue.readresult 19595 0 40747 76651 Browse Active Consumers
contents of our bundle-context.xml
<!-- JMS -->
<bean id="jmsConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory" destroy-method="stop">
<property name="maxConnections" value="5" />
<property name="maximumActive" value="5" />
<property name="connectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL">
<value>tcp://localhost:61616</value>
</property>
</bean>
</property>
</bean>
<bean id="ResultMessageConverter" class="com.bla.ResultMessageConverter" />
<jms:listener-container connection-factory="jmsConnectionFactory" destination-resolver="jmsDestinationResolver"
concurrency="2" prefetch="10" acknowledge="auto" cache="none" message-converter="ResultMessageConverter">
<jms:listener destination="queue.readresult" ref="ReaderRequestManager" method="handleReaderResult" />
</jms:listener-container>
There are no exception in any of the logs. Does anyone knows of a reason why after a while no new consumers could be started.
Thanks
I've run into issues before where "consumers stop consuming," but haven't seen consumers stop existing. You may be running out of memory and/or connections in the pool. Do you have to restart ActiveMQ to fix the problem or just your application?
Here are a couple suggestions:
Set the queue prefetch to 0
Add "useKeepAlive=false" on the connection string
Increase the memory limits for the queues
I can see no obvious reason in the config provided why it should fail. So you need to resort to classic trouble shooting.
Try to set logging to debug and recreate the issue. Then you should be able to see more and you might be able to detect the root cause of it.
Also, check out the JMS exception listener and try to attach it your implementation of it to get a grasp of the real problem.
http://docs.oracle.com/javaee/6/api/javax/jms/ExceptionListener.html
I have the need to send/receive messages towards/from different topics stored on a single JMS Server.
I would like to use JmsTemplate for sending and MessageListenerContainer for registering asyncronous listeners.
My configuration looks like this:
<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
<property name="environment">
<props>
<prop key="java.naming.factory.initial">xxx</prop>
<prop key="java.naming.provider.url">yyy</prop>
</props>
</property>
</bean>
<bean id="connectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiTemplate" ref ="jndiTemplate"/>
<property name="jndiName" value="TopicConnectionFactory"/>
</bean>
<bean id="singleConnectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">
<constructor-arg ref="connectionFactory"/>
</bean>
<bean id="tosJmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="singleConnectionFactory"/>
<property name="destinationResolver" ref="destinationResolver"/>
<property name="pubSubDomain" value="true"/>
</bean>
As far as I understood, the singleConnectionFactory, returning always the same connection instance, helps reducing the overhead of creating and closing
a connection each time a jmsTemplate needs (for example) to send/receive a message (as it would be when using a normal ConnectionFactory).
My first question is: if I create multiple jmsTemplate(s), can they all share a ref to a singleConnectionFactory? Or do they have to receive a distinct instance each (singleConnectionFactory1, singleConnectionFactory2, etc)?
Reading the API for SingleConnectionFactory, I found this:
Note that Spring's message listener containers support the use of a shared Connection
within each listener container instance. Using SingleConnectionFactory in combination only really makes sense for sharing a single JMS Connection across multiple listener containers.
This sound a bit cryptic to me. As far as I know, it is possible to register only 1 Listener per MessageListenerContainer, so I don't understand to what extent is a connection shared.
Suppose I want to register N Listeners: I will need to repeat N times something like this:
<bean
class="org.springframework.jms.listener.SimpleMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destinationName" value="destX" />
<property name="messageListener" ref="listener1outOfN" />
</bean>
How many Connections are created in such case from connectionFactory? One for each ListenerContainer or just a pool of Connections? And what if I provide the SimpleMessageListenerContainer-s with a ref to singleConnectionFactory?
What is the best approach (from the point of view of the performances, of course) in this case?
if I create multiple jmsTemplate(s), can they all share a ref to a singleConnectionFactory?
Yes, this is fine. The javadoc for SingleConnectionFactory says:
According to the JMS Connection model, this is perfectly thread-safe (in contrast to e.g. JDBC).
JMS Connection objects are thread-safe, and can be used by multiple threads concurrenctly. So there's no need to use multiple SingleConnectionFactory beans.
As far as I know, it is possible to register only 1 Listener per MessageListenerContainer, so I don't understand to what extent is a connection shared.
This is true; however, each MessageListenerContainer can have multiple threads processing messages concurrently, all using the same MessageListener object. The MessageListenerContainer will use a single, shared Connection for all of these threads (unless configured to do otherwise).
Note that Spring's message listener containers support the use of a shared Connection within each listener container instance. Using SingleConnectionFactory in combination only really makes sense for sharing a single JMS Connection across multiple listener containers.
In other words, if all you have is a single MessageListenerContainer, then SingleConnectionFactory is unnecessary, since the single connection is managed internally to MessageListenerContainer. if you have multiple listener containers, and want them all to share a connection, then SingleConnectionFactory is required. Also, if you want to share a connection between listening and sending, as you do, then SingleConnectionFactory is also necessary.
Environment
There is an existing software with propietary way of clustering, which should be moved to use JMS
The customer do not want to pay for setting up and mainaining a
messaging system, so it can be used ONLY if I can embedd the whole
messaging into the existing virtual machines
Broker instances and consumers should be in the same JVM. Consumers
should be able to connect to remote broker in failover situations, since all consumers no matter in which JVM will they run, should have ONE input queue.
it would be nice if the consumers would use direct method calls to
communicate with local broker
Demo Project
I have created a really simple demo (eclipse) project with ActiveMQ + Maven + Spring (the whole project is at http://www.woofiles.com/dl-279452-fOcsWkcm-activemq.zip). If you try it, change the dataDirectory of activemq, since it is a wired absolute path till now.
I try to start up a broker from spring, and also a set of consumers. See Spring config below:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:amq="http://activemq.apache.org/schema/core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://activemq.apache.org/schema/core
http://activemq.apache.org/schema/core/activemq-core.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.0.xsd">
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_FALLBACK" />
</bean>
<bean id="embeddedBroker" class="org.apache.activemq.broker.BrokerService"
destroy-method="stop" init-method="start" >
<property name="brokerName" value="conversion" />
<property name="dataDirectory"
value="c:\eclipseWithMaven\activemq\working\conversion" />
<property name="schedulerSupport" value="false" />
<property name="transportConnectorURIs">
<list>
<value>tcp://127.0.0.1:600${idOfClusterNode}</value>
</list>
</property>
<property name="managementContext">
<bean class="org.apache.activemq.broker.jmx.ManagementContext">
<property name="connectorPort" value="201${idOfClusterNode}" />
</bean>
</property>
</bean>
<!-- depends-on see why at http://activemq.apache.org/vm-transport-reference.html -->
<!--depends-on="embeddedBroker" -->
<bean id="jmsFactory" class="org.apache.activemq.ActiveMQConnectionFactory" depends-on="embeddedBroker">
<property name="brokerURL">
<value>failover:(vm:/conversion,tcp://127.0.0.1:6001,tcp://127.0.0.1:6002)</value>
</property>
</bean>
<bean id="cachedConnectionFactory"
class="org.springframework.jms.connection.CachingConnectionFactory"
p:targetConnectionFactory-ref="jmsFactory" p:sessionCacheSize="10" />
<bean id="container"
class="org.springframework.jms.listener.SimpleMessageListenerContainer">
<property name="concurrentConsumers" value="10" />
<property name="connectionFactory" ref="cachedConnectionFactory" />
<property name="messageListener" ref="conversion" />
<property name="destination" ref="conversionInputQueue" />
</bean>
<bean id="conversionInputQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="conversionInputQueue" />
</bean>
<bean id="conversion" class="activemq.Conversion"
p:clusterId="${idOfClusterNode}" />
</beans>
I simply try to start up one or two instances of the activemq.ConversionDemo class with different parameters used by spring & log4j config. The environment entries of the run config looks like this:
Instance 1 : -DidOfClusterNode=1 -DidOfOtherClusterNode=2 -DlogFile=conversion1.log
Instance 2: -DidOfClusterNode=2 -DidOfOtherClusterNode=1
-DlogFile=conversion2.log
If I start up one instance, it is fine. If two is running the following problems occoured:
The second broker will not start up at all. It says it does not have the lock. Its fine, but I supposed, it just starts up a thread asynchronously, and will give back the control to spring. But it seems, that it wont let spring to continue.
SimpleMessageListenerContainer also seems like holding the control
over spring till all consumers are started.
What I want
I want to meet the requirements above
I think I have to start up both brokers and consumers asynchronously,
which I cannot really do in spring with this config
It would be nice to have real load balancing between brokers. It seems, that ActiveMQ is prepared only for failover.
If my needs cannot be satisfied by ActiveMQ, please recommend
other free solution.
If you need further info, just let me know.
EDIT
I think ActiveMQ supports my needs, I just need to understand "network of brokers". So I guess I have to have two filestore, and a network from my two brokers.
if you point 2 brokers at the same file store, then the first one will get the lock and the 2nd will block until the lock is released...this is a shared file system master/slave setup
if you want an active/active setup, then you'll need to use separate file stores and wire them together as a network of brokers
I'm writing an emailer webservice for my company. One of the main requirements is guaranteed delivery, so we have a thin HTTP layer over the JMS transport, using a persistent QPid queue.
One of the issues I'm running into is the handling of errors during processing. If I simply roll back the transaction when there's an error, the message goes to the head of the queue. If the error is pervasive enough, this can lock up the entire queue until someone intervenes manually, and we would like to avoid this by posting back to the head so that messages can be processed in the meantime.
However, therein lie my problems. First, while AMQP has a mechanism to "reject and requeue" a message atomically instead of acknowledging it, JMS doesn't seem to have any analog for this feature, so the only way to access it is by casting, which ties me to a specific JMS implementation. Further, the JMS transport for CXF doesn't seem to have any means of overriding or injecting behavior at the transport level, which means I'm stuck either writing a bytecode agent or changing the code and recompiling just to get the behavior I want.
To work around the issue, I've toyed with the idea of implementing a fault handler in CXF that simply reconstructs the JMS message from the CXF message, and re-queues it. But then I can't use the transacted session, because the fault causes a rollback which I can't override, and then I'll wind up with a copy of the message on the head (from the rollback) and on the tail (from the re-queue). And I can't use CLIENT_ACKNOWLEDGE, because the JMS transport acknowledges the message before submitting it for processing, which means if the server goes down at the wrong time, I could lose a message.
So basically, as long as I'm stuck accepting the default behavior of the JMS transport, it seems impossible to get the behavior I want (requeueing of failed messages) without compromising data integrity.
A coworker has suggested eschewing the JMS transport entirely, and invoking the queue directly. The service implementation would then be a skeleton class that exists solely to put messages on the queue, and another process would implement a message listener. To me, this solution is sub-optimal because I lose the elegance of an agnostic web service, and I lose some scalability by coupling my implementation to the underlying technology.
I've also considered just writing a CXF transport for AMQP using the RabbitMQ client library. It would take longer but it would be something our company could continue using going forward, and perhaps something that could be contributed back to the CXF project. That said, I'm not wild about this idea either because of the amount of time involved writing, running and testing the code.
Here's my beans.xml for CXF:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xmlns:jms="http://cxf.apache.org/transports/jms"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd
http://cxf.apache.org/transports/jms http://cxf.apache.org/schemas/configuration/jms.xsd">
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<context:component-scan base-package="com.edo" />
<bean id="jmsConnectionFactory" class="org.apache.qpid.client.AMQConnectionFactory">
<constructor-arg name="broker" value="tcp://localhost:5672"/>
<constructor-arg name="username" value="guest"/>
<constructor-arg name="password" value="guest"/>
<constructor-arg name="clientName" value=""/>
<constructor-arg name="virtualHost" value=""/>
</bean>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate" p:explicitQosEnabled="true" p:deliveryMode="1" p:timeToLive="5000" p:connectionFactory-ref="jmsConnectionFactory" p:sessionTransacted="false" p:sessionAcknowledgeModeName="CLIENT_ACKNOWLEDGE" />
<bean id="jmsConfig" class="org.apache.cxf.transport.jms.JMSConfiguration" p:connectionFactory-ref="jmsConnectionFactory" p:wrapInSingleConnectionFactory="false" p:jmsTemplate-ref="jmsTemplate" p:timeToLive="500000" p:sessionTransacted="false" p:concurrentConsumers="1" p:maxSuspendedContinuations="0" p:maxConcurrentConsumers="1" />
<jms:destination id="jms-destination-bean" name="{http://test.jms.jaxrs.edo.com/}HelloWorldImpl.jms-destination">
<jms:address jndiConnectionFactoryName="ConnectionFactory" jmsDestinationName="TestQueue">
<jms:JMSNamingProperty name="java.naming.factory.initial" value="org.apache.activemq.jndi.ActiveMQInitialContextFactory"/>
<jms:JMSNamingProperty name="java.naming.provider.url" value="tcp://localhost:5672"/>
</jms:address>
<jms:jmsConfig-ref>jmsConfig</jms:jmsConfig-ref>
</jms:destination>
<jaxrs:server id="helloWorld" address="/HelloWorld" transportId="http://cxf.apache.org/transports/jms">
<jaxrs:serviceBeans>
<ref bean="helloWorldBean"/>
</jaxrs:serviceBeans>
<jaxrs:inInterceptors>
<bean class="com.edo.jaxrs.jms.test.FlowControlInInterceptor" p:periodMs="1000" p:permitsPerPeriod="18" />
</jaxrs:inInterceptors>
<jaxrs:providers>
<bean class="org.apache.cxf.jaxrs.provider.JSONProvider">
<property name="produceMediaTypes" ref="jsonTypes"/>
<property name="consumeMediaTypes" ref="jsonTypes"/>
</bean>
</jaxrs:providers>
</jaxrs:server>
<bean id="http-jms-config" class="com.edo.jaxrs.jms.test.HttpOverJmsConfig"
p:jmsFactory-ref="jmsConnectionFactory"
p:jmsDestinationName="TestQueue" />
<util:list id="jsonTypes">
<value>application/json</value>
<value>application/jettison</value>
</util:list>
</beans>
Is there something simple I'm missing? Or is there a better way to go about this that would sidestep the problem?
So - I'm taking my coworker's advice and not using the JMS transport for the web service. Instead, we're going to create a thin web service layer over Spring Integration. This should allow us the granularity of control we need without unnecessarily exposing the messaging layer.