spring jms and activemq
TRANSCRIPT
Spring JMS with ActiveMQENTERPRISE MESSAGING
Point-to-Point messaging
Queue’s Messages are delivered once and only once
◦ Kept in the queue when listener is not available◦ Sent to only one consumer when multiple are available
◦ Client acknowledge: wait for consumer to acknowledge before removing the message◦ Prefetch: multiple messages can be reserved for a consumer◦ Client timeout: if no ack or reject is received, queue will consider the consumer dead◦ Order not guaranteed with multiple queue’s
◦ Redelivery Policy◦ Set on client side◦ Retry after reject or use Dead Letter Queue
Selectors and Message Groups Selectors: Server side filtering
◦ Consumer can register a filter when connecting◦ Filtering will be done on the server◦ ActiveMQ supports selectors for JMS Headers and XPaths for XML Messages.
◦ No JSON
Message Groups: guaranteed ordering◦ If messages are correlated and relative order is important
◦ Add JMSXGroupID Header◦ All messages with the same groupId will be processed by the same consumer◦ Group mappings are kept in-memory on the server.
◦ Need to close groups properly◦ Could fail after a failover
Publish/Subscribe
Topics Write one message, will be received by all subscribers.
◦ Looser coupling between producer and consumer
When no subscribers are available, the message is not saved. Perfect for frontend – backend communication
◦ Backend post a teaserChange on a topic◦ All frontends showing the teaserList are subscribed on that topic
Not suited for server – to – server communication◦ Clustered service will receive each messages on every node
CompositeTopic a.k.a. VirtualTopic
Configure queue’s on the server which listen to a topic◦ Queue’s will buffer messages when subscribers are temporary offline◦ Messages will be received exactly once per Queue
◦ Queue per interested service◦ Decouple producer from consumer
◦ Unless queue’s are full :-)
<virtualDestinations> <compositeTopic name="redsys.publishing.publishfeed"> <forwardTo> <queue physicalName="redsys.moonriser.consumer.publishing.publishfeed"/> <queue physicalName="redsys.sitemanagement.consumer.publishing.publishfeed"/> <queue physicalName="redsys.publishingeventprocessor.consumer.publishing.publisheventfeed"/> </forwardTo> </compositeTopic>
ActiveMQ Storage configuration ActiveMQ will try to keep as much messages in memory as possible
Flushes to disk if one queue goes over 69% mem, or total mem usage goes over 80%
If disk storage gets over 80%, producers are first slowed down, then blocked
Selectors don’t work on flushed messages◦ Need to wait until other messages are consumed
JMS API
Spring JMS
Synchronous Messaging
// Use the default destination jmsTemplate.convertAndSend("Hello World!"); // Use a different destination jmsTemplate.convertAndSend(“TEST.BAR”, “Hello World!”); // Use a default destination String textMessage1 = (String) jmsTemplate.receiveAndConvert(); // Use a different destination String textMessage2 = (String) jmsTemplate.receiveAndConvert(“TEST.BAR”);
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"
p:brokerURL="tcp://localhost:61616" /><bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"
p:connectionFactory-ref="connectionFactory"p:defaultDestination-ref="destination" />
MessageConvertors Maps java objects to message payloads
◦ ObjectMessage: java serialization◦ TextMessage: Jackson (JSON) or Jaxb (XML)
◦ Jackson inserts a JMS Header with the fully qualified classname by default◦ Can reuse Spring MVC ObjectMapper
Asynchronous Messaging
class MyMessageListener{public Result onMessage(Action a){ // input parameters will be unmarshalled if necessary (jackson/jaxb) // can also receive message headers as input parameters // non-void results will be sent to a response queue // auto-acknowledges if no exceptions thrown}
}
<bean id="connectionFactory"class="org.apache.activemq.ActiveMQConnectionFactory"p:brokerURL="tcp://localhost:61616" />
<bean id="messageListener“ class="org.bsnyder.spring.jms.listener.MyMessageListener" /><jms:listener-container concurrency="5-10“container-type=“simple|default”>
<jms:listener destination="FOO.TEST" ref="messageListener“ method=“onMessage”/></jms:listener-container>
MessageListenerContainer options
• DefaultMessageListenerContainer
– Spring launches threads◦ while(…){ consumer.receive(); }
– Allows for dynamic scaling of queue consumers
– Participates in external transactions
• SimpleMessageListenerContainer
– consumer.setMessageListener(myListener);◦ JMS Provider manages ThreadPool
– No external transaction support
– Works better with MockRunner JMS
JMS Resource Pooling Managed ConnectionFactory
◦ JCA Resource Adapter in JBoss◦ ConnectionFactory bound in JNDI, no pooling in application
Unmanaged broker◦ Define pure ActiveMQConnectionFactory in Spring◦ Wrap in org.apache.activemq.pool.PooledConnectionFactory
◦ Also possible: Spring CachingConnectionFactory◦ Caches Sessions, too
ActiveMQ Client Configuration ActiveMQ namespace for spring configuration
Same configuration format as server
<connectionFactory xmlns="http://activemq.apache.org/schema/core" brokerURL="${activeMQ.brokerURL}" userName="${activeMQ.username}" password="${activeMQ.password}"> <redeliveryPolicyMap> <redeliveryPolicyMap> <defaultEntry> <redeliveryPolicy maximumRedeliveries="2"/> </defaultEntry> </redeliveryPolicyMap> </redeliveryPolicyMap> </connectionFactory>
Transactions JmsTransactionManager
◦ Injected in JmsTemplate and (Default)MessageListenerContainers◦ Transactional behaviour across JMS Senders/Receivers◦ All calls use the same JMS Session◦ Acknowledgements are only processed when whole session is committed
JtaTransactionManager◦ JMS Sessions are synchronized with XA Database transactions◦ Here be dragons…
Testing Unit tests
◦ Call message listeners directly◦ Mockito.mock(JmsTemplate.class)
Component Integration Tests◦ Use mockrunner-jms
System Tests◦ Use embedded ActiveMQ broker
<amq:broker persistent="false" useJmx="false" id="embeddedBroker"> <amq:transportConnectors> <amq:transportConnector uri="tcp://localhost:#{freePortSelector}"/> </amq:transportConnectors></amq:broker><amq:connectionFactory id="jmsFactory" brokerURL="vm://localhost"/>
@Beanpublic JMSMockObjectFactory jmsMockObjectFactory() { return new JMSMockObjectFactory();}@Beanpublic ConnectionFactory connectionFactory() { return jmsMockObjectFactory().getMockConnectionFactory();}@Beanpublic JMSTestModule jmsTestModule() { return new JMSTestModule(jmsMockObjectFactory());}
Q&A