spring activeMQ 开启事物接收消息时自定义确认消息
来源:互联网 发布:ubuntu selinux设置 编辑:程序博客网 时间:2024/05/21 11:02
1:环境和版本
java:jdk7
spring:4.1.3
activemq:5.8.0
2:spring与activeMQ的结合配置
<?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"> <!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供 --> <bean id="activeMQConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"> <property name="brokerURL" value="tcp://localhost:61636"></property> </bean> <!-- ActiveMQ为我们提供了一个PooledConnectionFactory,往里面注入一个ActiveMQConnectionFactory可以用来将Connection, Session和MessageProducer池化,这样可以大大的减少我们的资源消耗。 问题:使用poolConnectionFactory时候,用JMSTemplate同步循环接收消息,因为JMSTemplate会自动在接收消息后关闭连接, 所以循环到第二次的时候会报错,这个问题待解决 问题:使用poolConnectionFactory时候,用监听来接收消息,会有部分消息残留在队列里面,问题待解决 结论:还是先别用连接池了--> <bean id="poolConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory" > <property name="connectionFactory" ref="activeMQConnectionFactory" /> <property name="maxConnections" value="10"/> </bean> <!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory 这里我使用的是singleConnectionFactory--> <bean id="singleConnectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory"> <property name="targetConnectionFactory" ref="activeMQConnectionFactory"/> </bean> <bean id="cachingConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory"> <property name="targetConnectionFactory" ref="activeMQConnectionFactory"/> </bean> <!-- 配置生产者 --> <!-- Spring提供的JMS工具类,它可以进行消息发送、接收等 --> <bean id="senderJmsTemplate" class="org.springframework.jms.core.JmsTemplate"> <!-- 这个connectionFactory对应的是我们定义的Spring提供的那个ConnectionFactory对象 --> <property name="connectionFactory" ref="singleConnectionFactory"/> <!-- NON_PERSISTENT非持久化 1 ,PERSISTENT持久化 2 --> <property name="deliveryMode" value="2"/> <property name="sessionTransacted" value="true"/> <property name="sessionAcknowledgeModeName" value="AUTO_ACKNOWLEDGE"/> </bean> <!--这个是队列目的地,点对点的 --> <bean id="activeMQQueue" class="org.apache.activemq.command.ActiveMQQueue"> <constructor-arg value="FirstQueue"/> </bean> <!--这个是主题目的地,一对多的 --> <bean id="topicDestination" class="org.apache.activemq.command.ActiveMQTopic"> <constructor-arg value="topic"/> </bean> <!-- 自定义消费者 --> <!-- Spring提供的JMS工具类,它可以进行消息发送、接收等 --> <bean id="receiverJmsTemplate" class="com.system.freemwork.amq.SimpleJmsTemplate"> <!-- 这个connectionFactory对应的是我们定义的Spring提供的那个ConnectionFactory对象 --> <property name="connectionFactory" ref="singleConnectionFactory"/> <!-- 如果是原生的amq创建的session,将session设置为true时候,ack会固定被设置为AUTO_ACKNOWLEDGE 所以想要手动确认,那么session的事物必须设置为false,并且ack设置为CLIENT_ACKNOWLEDGE --> <property name="sessionTransacted" value="false"/> <property name="sessionAcknowledgeModeName" value="CLIENT_ACKNOWLEDGE"/> <property name="receiveTimeout" value="1000"/> <property name="autoAcknowledge" value="true"/> </bean></beans>
3:编写send测试类
package com.test.spring;import org.junit.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.jms.core.JmsTemplate;/** * 类描述:sender测试类 * * @author fengyong * @version 1.0 * @since 1.0 * Created by fengyong on 16/8/3 下午7:45. */public class ActiveMqSender extends BaseTest { @Autowired private JmsTemplate senderJmsTemplate; @Test public void activeMq(){ for(int i = 1;i<=10;i++){ senderJmsTemplate.convertAndSend("FirstQueue","我是第"+i+"个"); } System.out.print("全部执行完毕!!!"); }}
4:编写receiver测试类
package com.test.spring;import com.system.freemwork.amq.SimpleJmsTemplate;import org.apache.activemq.command.ActiveMQQueue;import org.junit.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.jms.core.JmsTemplate;import javax.jms.JMSException;import javax.jms.TextMessage;/** * 类描述:receiver测试类 * * @author fengyong * @version 1.0 * @since 1.0 * Created by fengyong on 16/8/8 下午5:28. */public class ActiveMqReceiver extends BaseTest{ @Autowired private SimpleJmsTemplate receiverJmsTemplate; @Autowired private ActiveMQQueue activeMQQueue; /** * 坑爹的方法,如果session事物设置为true,receiver直接将sessioin进行commit, * * 如果设置为false,receiver方法会直接判断进行消息确认,无法做到手动的消息确认,所以一旦发生异常,这条消息不会回到消息队列中 * * session的提交可以认为是消息确认收到 * @throws JMSException */ @Test public void receiver() throws JMSException { int i=1; while (true){ i++; TextMessage message = (TextMessage)receiverJmsTemplate.receive(activeMQQueue); if (null != message) { System.out.println("收到消息==================" + message.getText()); } else { System.out.print("超时10秒"); break; } } }}
注:如果session事物设置为true,receiver直接将sessioin进行commit.源码如下
if (session.getTransacted()) { // Commit necessary - but avoid commit call within a JTA transaction. if (isSessionLocallyTransacted(session)) { // Transacted session created by this template -> commit. JmsUtils.commitIfNecessary(session); }}
如果session事物设置为false,receiver方法会直接判断进行消息确认,无法做到手动的消息确认,所以一旦发生异常,这条消息不会回到消息队列中.源码如下
else if (isClientAcknowledge(session)) { // Manually acknowledge message, if any. if (message != null) { message.acknowledge(); }}
所以需要修改源码不让其在receiver的时候自动确认收到消息
5:新建SimpleJmsTemplate继承JmsTemplate
package com.system.freemwork.amq;import org.springframework.jms.JmsException;import org.springframework.jms.connection.ConnectionFactoryUtils;import org.springframework.jms.connection.JmsResourceHolder;import org.springframework.jms.core.JmsTemplate;import org.springframework.jms.core.SessionCallback;import org.springframework.jms.support.JmsUtils;import org.springframework.transaction.support.TransactionSynchronizationManager;import org.springframework.util.Assert;import sun.misc.resources.Messages_ja;import javax.jms.Connection;import javax.jms.JMSException;import javax.jms.Message;import javax.jms.MessageConsumer;import javax.jms.Session;/** * 类描述:自定义JmsTemplate,实现客户手动确认 * * @author fengyong * @version 1.0 * @since 1.0 * Created by fengyong on 16/8/10 上午10:03. */public class SimpleJmsTemplate extends JmsTemplate { private final JmsTemplateResourceFactory transactionalResourceFactory = new JmsTemplateResourceFactory(); /** * 是否开启手动确认标记 */ private Boolean autoAcknowledge; MessageConsumer consumer = null; Session sessionToClose = null; Connection conToClose = null; boolean startConnection = false; /** * 接收消息 * @param session * @param consumer * @return * @throws JMSException */ protected Message doReceive(Session session, MessageConsumer consumer) throws JMSException { try { this.consumer = consumer; // Use transaction timeout (if available). long timeout = getReceiveTimeout(); JmsResourceHolder resourceHolder = (JmsResourceHolder) TransactionSynchronizationManager.getResource(getConnectionFactory()); if (resourceHolder != null && resourceHolder.hasTimeout()) { timeout = Math.min(timeout, resourceHolder.getTimeToLiveInMillis()); } Message message = doReceive(consumer, timeout); if (session.getTransacted()) { // Commit necessary - but avoid commit call within a JTA transaction. // 如果开启了jta事物,那么不会进行提交,jta事物会直接覆盖掉session事物 if (isSessionLocallyTransacted(session)) { // Transacted session created by this template -> commit. JmsUtils.commitIfNecessary(session); } } //autoAcknowledge如果为真,不进行自动确认 else if (isClientAcknowledge(session) && !autoAcknowledge) { // Manually acknowledge message, if any. if (message != null) { message.acknowledge(); } } return message; } finally { consumer = null; } } /** * 自定义的消息确认,关闭consumer和sesseionToClose是父类本身就要执行的,这里直接拷贝下来,能不改的地方尽量不改 * 该子类只是为了自定义确认消息 * @param message * @throws JMSException */ public void msgAckAndcloseSession(Message message) throws JMSException { message.acknowledge(); JmsUtils.closeMessageConsumer(consumer); JmsUtils.closeSession(sessionToClose); ConnectionFactoryUtils.releaseConnection(conToClose, getConnectionFactory(), startConnection); } /** * 由于上面的doReceive(Session session, MessageConsumer consumer)需要调用这个方法, * 而在父类里面这个方法是私有的,所以直接拷贝下来了 * @param consumer * @param timeout * @return * @throws JMSException */ private Message doReceive(MessageConsumer consumer, long timeout) throws JMSException { if (timeout == RECEIVE_TIMEOUT_NO_WAIT) { return consumer.receiveNoWait(); } else if (timeout > 0) { return consumer.receive(timeout); } else { return consumer.receive(); } } /** * 该方法是为了防止确认消息前session被关闭,不然确认消息前session关闭会导致异常发生 * transactionalResourceFactory在父类中是私有且不可修改,因为只有这一个方法用到了transactionalResourceFactory * 所以直接将JmsTemplateResourceFactory拷贝下来使用 * @param action * @param startConnection * @param <T> * @return * @throws JmsException */ public <T> T execute(SessionCallback<T> action, boolean startConnection) throws JmsException { Assert.notNull(action, "Callback object must not be null"); this.startConnection = startConnection; try { Session sessionToUse = ConnectionFactoryUtils.doGetTransactionalSession( getConnectionFactory(), transactionalResourceFactory, startConnection); if (sessionToUse == null) { conToClose = createConnection(); sessionToClose = createSession(conToClose); if (startConnection) { conToClose.start(); } sessionToUse = sessionToClose; } if (logger.isDebugEnabled()) { logger.debug("Executing callback on JMS Session: " + sessionToUse); } return action.doInJms(sessionToUse); } catch (JMSException ex) { throw convertJmsAccessException(ex); } finally { sessionToClose = null; conToClose = null; startConnection = false; } } /** * Sets new 是否开启手动确认标记. * * @param autoAcknowledge New value of 是否开启手动确认标记. */ public void setAutoAcknowledge(Boolean autoAcknowledge) { this.autoAcknowledge = autoAcknowledge; } /** * Gets 是否开启手动确认标记. * * @return Value of 是否开启手动确认标记. */ public Boolean getAutoAcknowledge() { return autoAcknowledge; } /** * 直接拷贝下来的 */ private class JmsTemplateResourceFactory implements ConnectionFactoryUtils.ResourceFactory { @Override public Connection getConnection(JmsResourceHolder holder) { return SimpleJmsTemplate.this.getConnection(holder); } @Override public Session getSession(JmsResourceHolder holder) { return SimpleJmsTemplate.this.getSession(holder); } @Override public Connection createConnection() throws JMSException { return SimpleJmsTemplate.this.createConnection(); } @Override public Session createSession(Connection con) throws JMSException { return SimpleJmsTemplate.this.createSession(con); } @Override public boolean isSynchedLocalTransactionAllowed() { return SimpleJmsTemplate.this.isSessionTransacted(); } }}
6:第二个receiver测试类
package com.test.spring;import com.system.freemwork.amq.SimpleJmsTemplate;import org.apache.activemq.command.ActiveMQQueue;import org.junit.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.jms.core.JmsTemplate;import javax.jms.JMSException;import javax.jms.TextMessage;/** * 类描述:receiver测试类 * * @author fengyong * @version 1.0 * @since 1.0 * Created by fengyong on 16/8/8 下午5:28. */public class ActiveMqReceiver extends BaseTest{ @Autowired private SimpleJmsTemplate receiverJmsTemplate; @Autowired private ActiveMQQueue activeMQQueue; @Test public void recerver() throws JMSException { int i=1; while (true){ i++; TextMessage message = (TextMessage)receiverJmsTemplate.receive(activeMQQueue); if (null != message) { System.out.println("收到消息==================" + message.getText()); if(i==4){ throw new RuntimeException("Exception"); } receiverJmsTemplate.msgAckAndcloseSession(message); } else { System.out.print("超时10秒"); break; } } }}
7:测试结果
收到消息==================我是第1个收到消息==================我是第2个收到消息==================我是第3个java.lang.RuntimeException: Exceptionat com.test.spring.ActiveMqReceiver.show(ActiveMqReceiver.java:43)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:606)at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:73)
注:接收第三个时失败,虽然这里打印出来了,但是第三个消息并没有被确认接收,同步自定义接收消息修改成功
0 0
- spring activeMQ 开启事物接收消息时自定义确认消息
- ActiveMQ的消息接收确认
- Activemq消息确认机制
- Activemq消息确认机制
- activemq消息确认机制
- Activemq消息确认机制
- ActiveMQ 整合 Spring 发送和接收消息
- RabbitMQ消息确认(发送确认,接收确认)
- activemq消息接收流程
- 测试接收 ActiveMq 消息
- ActiveMQ 消息接收方式
- ActiveMQ消息确认的问题
- jms+spring+activemq配置(发送和接收消息)
- jms+spring+activemq配置(发送和接收消息)
- ActiveMQ - tcp 协议接收消息
- ActiveMQ - stomp 协议接收消息
- 使用ActiveMQ 发送/接收消息
- ActiveMQ消息发送和接收
- 机房收费之组合查询
- 【转】什么时候你才会从重复中惊醒?
- Set,List,Map的区别
- ShareSDK微信分享BUG
- ci、tp、yii优缺点
- spring activeMQ 开启事物接收消息时自定义确认消息
- Android中EditTex焦点设置和弹不弹出输入法的问题
- ionic入门教程第十九课-ionic路由详解(state、route、resolve)
- 获取指定时间时间戳
- linux 下火狐浏览器不能播放flash 的解决办法
- linux 下火狐浏览器不能播放flash 的解决办法
- POJ-2566 Bound Found(尺取法变形)
- java中通用的数据库连接与关闭方法类的简单写法
- 发送消息-群发消息,边看边打字。。