spring activeMQ使用连接池工厂报错

1:环境和版本

java:jdk7

spring:4.1.3

activemq:5.8.0

2:spring与activeMQ的结合配置

 
  1. <?xml version="1.0" encoding="UTF-8"?>

  2. <beans xmlns="http://www.springframework.org/schema/beans"

  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  4. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd

  5. ">

  6.  
  7.  
  8. <!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供 -->

  9. <bean id="activeMQConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">

  10. <property name="brokerURL" value="tcp://localhost:61636"></property>

  11. </bean>

  12. <!-- ActiveMQ为我们提供了一个PooledConnectionFactory,往里面注入一个ActiveMQConnectionFactory可以用来将Connection,

  13. Session和MessageProducer池化,这样可以大大的减少我们的资源消耗。

  14. 问题:使用poolConnectionFactory时候,用JMSTemplate同步循环接收消息,因为JMSTemplate会自动在接收消息后关闭连接,

  15. 所以循环到第二次的时候会报错,这个问题待解决

  16. 问题:使用poolConnectionFactory时候,用监听来接收消息,会有部分消息残留在队列里面,问题待解决

  17. 结论:还是先别用连接池了-->

  18. <bean id="poolConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory" >

  19. <property name="connectionFactory" ref="activeMQConnectionFactory" />

  20. <property name="maxConnections" value="10"/>

  21. </bean>

  22. <!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory 这里我使用的是singleConnectionFactory-->

  23. <bean id="singleConnectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">

  24. <property name="targetConnectionFactory" ref="activeMQConnectionFactory"/>

  25. </bean>

  26. <bean id="cachingConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">

  27. <property name="targetConnectionFactory" ref="activeMQConnectionFactory"/>

  28. </bean>

  29.  
  30.  
  31. <!-- 配置生产者 -->

  32. <!-- Spring提供的JMS工具类,它可以进行消息发送、接收等 -->

  33. <bean id="senderJmsTemplate" class="org.springframework.jms.core.JmsTemplate">

  34. <!-- 这个connectionFactory对应的是我们定义的Spring提供的那个ConnectionFactory对象 -->

  35. <property name="connectionFactory" ref="singleConnectionFactory"/>

  36.  
  37. <!-- NON_PERSISTENT非持久化 1 ,PERSISTENT持久化 2 -->

  38. <property name="deliveryMode" value="2"/>

  39.  
  40. <property name="sessionTransacted" value="true"/>

  41. <property name="sessionAcknowledgeModeName" value="AUTO_ACKNOWLEDGE"/>

  42. </bean>

  43. <!--这个是队列目的地,点对点的 -->

  44. <bean id="activeMQQueue" class="org.apache.activemq.command.ActiveMQQueue">

  45. <constructor-arg value="FirstQueue"/>

  46. </bean>

  47. <!--这个是主题目的地,一对多的 -->

  48. <bean id="topicDestination" class="org.apache.activemq.command.ActiveMQTopic">

  49. <constructor-arg value="topic"/>

  50. </bean>

  51.  
  52. <!-- 自定义消费者 -->

  53. <!-- Spring提供的JMS工具类,它可以进行消息发送、接收等 -->

  54. <bean id="receiverJmsTemplate" class="com.system.freemwork.amq.SimpleJmsTemplate">

  55. <!-- 这个connectionFactory对应的是我们定义的Spring提供的那个ConnectionFactory对象 -->

  56. <property name="connectionFactory" ref="singleConnectionFactory"/>

  57.  
  58. <!-- 如果是原生的amq创建的session,将session设置为true时候,ack会固定被设置为AUTO_ACKNOWLEDGE

  59. 所以想要手动确认,那么session的事物必须设置为false,并且ack设置为CLIENT_ACKNOWLEDGE -->

  60. <property name="sessionTransacted" value="false"/>

  61. <property name="sessionAcknowledgeModeName" value="CLIENT_ACKNOWLEDGE"/>

  62. <property name="receiveTimeout" value="1000"/>

  63. <property name="autoAcknowledge" value="true"/>

  64. </bean>

  65. </beans>

3:编写send测试类

 
  1. package com.test.spring;

  2.  
  3. import org.junit.Test;

  4. import org.springframework.beans.factory.annotation.Autowired;

  5. import org.springframework.jms.core.JmsTemplate;

  6.  
  7. /**

  8. * 类描述:sender测试类

  9. *

  10. * @author fengyong

  11. * @version 1.0

  12. * @since 1.0

  13. * Created by fengyong on 16/8/3 下午7:45.

  14. */

  15. public class ActiveMqSender extends BaseTest {

  16. @Autowired

  17. private JmsTemplate senderJmsTemplate;

  18.  
  19. @Test

  20. public void activeMq(){

  21. for(int i = 1;i<=10;i++){

  22. senderJmsTemplate.convertAndSend("FirstQueue","我是第"+i+"个");

  23. }

  24. System.out.print("全部执行完毕!!!");

  25. }

  26. }

4:编写receiver测试类

 
  1. package com.test.spring;

  2.  
  3. import com.system.freemwork.amq.SimpleJmsTemplate;

  4. import org.apache.activemq.command.ActiveMQQueue;

  5. import org.junit.Test;

  6. import org.springframework.beans.factory.annotation.Autowired;

  7. import org.springframework.jms.core.JmsTemplate;

  8.  
  9. import javax.jms.JMSException;

  10. import javax.jms.TextMessage;

  11.  
  12. /**

  13. * 类描述:receiver测试类

  14. *

  15. * @author fengyong

  16. * @version 1.0

  17. * @since 1.0

  18. * Created by fengyong on 16/8/8 下午5:28.

  19. */

  20. public class ActiveMqReceiver extends BaseTest{

  21. @Autowired

  22. private SimpleJmsTemplate receiverJmsTemplate;

  23. @Autowired

  24. private ActiveMQQueue activeMQQueue;

  25.  
  26. /**

  27. * 坑爹的方法,如果session事物设置为true,receiver直接将sessioin进行commit,

  28. *

  29. * 如果设置为false,receiver方法会直接判断进行消息确认,无法做到手动的消息确认,所以一旦发生异常,这条消息不会回到消息队列中

  30. *

  31. * session的提交可以认为是消息确认收到

  32. * @throws JMSException

  33. */

  34. @Test

  35. public void receiver() throws JMSException {

  36. int i=1;

  37. while (true){

  38. i++;

  39. TextMessage message = (TextMessage)receiverJmsTemplate.receive(activeMQQueue);

  40. if (null != message) {

  41. System.out.println("收到消息==================" + message.getText());

  42.  
  43. } else {

  44. System.out.print("超时10秒");

  45. break;

  46. }

  47. }

  48. }

  49. }

注:如果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

 
  1. package com.system.freemwork.amq;

  2.  
  3. import org.springframework.jms.JmsException;

  4. import org.springframework.jms.connection.ConnectionFactoryUtils;

  5. import org.springframework.jms.connection.JmsResourceHolder;

  6. import org.springframework.jms.core.JmsTemplate;

  7. import org.springframework.jms.core.SessionCallback;

  8. import org.springframework.jms.support.JmsUtils;

  9. import org.springframework.transaction.support.TransactionSynchronizationManager;

  10. import org.springframework.util.Assert;

  11. import sun.misc.resources.Messages_ja;

  12.  
  13. import javax.jms.Connection;

  14. import javax.jms.JMSException;

  15. import javax.jms.Message;

  16. import javax.jms.MessageConsumer;

  17. import javax.jms.Session;

  18.  
  19. /**

  20. * 类描述:自定义JmsTemplate,实现客户手动确认

  21. *

  22. * @author fengyong

  23. * @version 1.0

  24. * @since 1.0

  25. * Created by fengyong on 16/8/10 上午10:03.

  26. */

  27. public class SimpleJmsTemplate extends JmsTemplate {

  28.  
  29.  
  30. private final JmsTemplateResourceFactory transactionalResourceFactory = new JmsTemplateResourceFactory();

  31. /**

  32. * 是否开启手动确认标记

  33. */

  34. private Boolean autoAcknowledge;

  35.  
  36. MessageConsumer consumer = null;

  37. Session sessionToClose = null;

  38. Connection conToClose = null;

  39. boolean startConnection = false;

  40.  
  41. /**

  42. * 接收消息

  43. * @param session

  44. * @param consumer

  45. * @return

  46. * @throws JMSException

  47. */

  48. protected Message doReceive(Session session, MessageConsumer consumer) throws JMSException {

  49. try {

  50. this.consumer = consumer;

  51. // Use transaction timeout (if available).

  52. long timeout = getReceiveTimeout();

  53. JmsResourceHolder resourceHolder =

  54. (JmsResourceHolder) TransactionSynchronizationManager.getResource(getConnectionFactory());

  55. if (resourceHolder != null && resourceHolder.hasTimeout()) {

  56. timeout = Math.min(timeout, resourceHolder.getTimeToLiveInMillis());

  57. }

  58. Message message = doReceive(consumer, timeout);

  59. if (session.getTransacted()) {

  60. // Commit necessary - but avoid commit call within a JTA transaction.

  61. // 如果开启了jta事物,那么不会进行提交,jta事物会直接覆盖掉session事物

  62. if (isSessionLocallyTransacted(session)) {

  63. // Transacted session created by this template -> commit.

  64. JmsUtils.commitIfNecessary(session);

  65. }

  66. }

  67. //autoAcknowledge如果为真,不进行自动确认

  68. else if (isClientAcknowledge(session) && !autoAcknowledge) {

  69. // Manually acknowledge message, if any.

  70. if (message != null) {

  71. message.acknowledge();

  72. }

  73. }

  74. return message;

  75. }

  76. finally {

  77. consumer = null;

  78. }

  79. }

  80.  
  81. /**

  82. * 自定义的消息确认,关闭consumer和sesseionToClose是父类本身就要执行的,这里直接拷贝下来,能不改的地方尽量不改

  83. * 该子类只是为了自定义确认消息

  84. * @param message

  85. * @throws JMSException

  86. */

  87. public void msgAckAndcloseSession(Message message) throws JMSException {

  88. message.acknowledge();

  89. JmsUtils.closeMessageConsumer(consumer);

  90. JmsUtils.closeSession(sessionToClose);

  91. ConnectionFactoryUtils.releaseConnection(conToClose, getConnectionFactory(), startConnection);

  92. }

  93.  
  94. /**

  95. * 由于上面的doReceive(Session session, MessageConsumer consumer)需要调用这个方法,

  96. * 而在父类里面这个方法是私有的,所以直接拷贝下来了

  97. * @param consumer

  98. * @param timeout

  99. * @return

  100. * @throws JMSException

  101. */

  102. private Message doReceive(MessageConsumer consumer, long timeout) throws JMSException {

  103. if (timeout == RECEIVE_TIMEOUT_NO_WAIT) {

  104. return consumer.receiveNoWait();

  105. }

  106. else if (timeout > 0) {

  107. return consumer.receive(timeout);

  108. }

  109. else {

  110. return consumer.receive();

  111. }

  112. }

  113.  
  114.  
  115. /**

  116. * 该方法是为了防止确认消息前session被关闭,不然确认消息前session关闭会导致异常发生

  117. * transactionalResourceFactory在父类中是私有且不可修改,因为只有这一个方法用到了transactionalResourceFactory

  118. * 所以直接将JmsTemplateResourceFactory拷贝下来使用

  119. * @param action

  120. * @param startConnection

  121. * @param <T>

  122. * @return

  123. * @throws JmsException

  124. */

  125. public <T> T execute(SessionCallback<T> action, boolean startConnection) throws JmsException {

  126. Assert.notNull(action, "Callback object must not be null");

  127. this.startConnection = startConnection;

  128.  
  129. try {

  130. Session sessionToUse = ConnectionFactoryUtils.doGetTransactionalSession(

  131. getConnectionFactory(), transactionalResourceFactory, startConnection);

  132. if (sessionToUse == null) {

  133. conToClose = createConnection();

  134. sessionToClose = createSession(conToClose);

  135. if (startConnection) {

  136. conToClose.start();

  137. }

  138. sessionToUse = sessionToClose;

  139. }

  140. if (logger.isDebugEnabled()) {

  141. logger.debug("Executing callback on JMS Session: " + sessionToUse);

  142. }

  143. return action.doInJms(sessionToUse);

  144. }

  145. catch (JMSException ex) {

  146. throw convertJmsAccessException(ex);

  147. }

  148. finally {

  149. sessionToClose = null;

  150. conToClose = null;

  151. startConnection = false;

  152. }

  153. }

  154.  
  155.  
  156. /**

  157. * Sets new 是否开启手动确认标记.

  158. *

  159. * @param autoAcknowledge New value of 是否开启手动确认标记.

  160. */

  161. public void setAutoAcknowledge(Boolean autoAcknowledge) {

  162. this.autoAcknowledge = autoAcknowledge;

  163. }

  164.  
  165. /**

  166. * Gets 是否开启手动确认标记.

  167. *

  168. * @return Value of 是否开启手动确认标记.

  169. */

  170. public Boolean getAutoAcknowledge() {

  171. return autoAcknowledge;

  172. }

  173.  
  174.  
  175. /**

  176. * 直接拷贝下来的

  177. */

  178. private class JmsTemplateResourceFactory implements ConnectionFactoryUtils.ResourceFactory {

  179.  
  180. @Override

  181. public Connection getConnection(JmsResourceHolder holder) {

  182. return SimpleJmsTemplate.this.getConnection(holder);

  183. }

  184.  
  185. @Override

  186. public Session getSession(JmsResourceHolder holder) {

  187. return SimpleJmsTemplate.this.getSession(holder);

  188. }

  189.  
  190. @Override

  191. public Connection createConnection() throws JMSException {

  192. return SimpleJmsTemplate.this.createConnection();

  193. }

  194.  
  195. @Override

  196. public Session createSession(Connection con) throws JMSException {

  197. return SimpleJmsTemplate.this.createSession(con);

  198. }

  199.  
  200. @Override

  201. public boolean isSynchedLocalTransactionAllowed() {

  202. return SimpleJmsTemplate.this.isSessionTransacted();

  203. }

  204. }

  205.  
  206. }

6:第二个receiver测试类

 
  1. package com.test.spring;

  2.  
  3. import com.system.freemwork.amq.SimpleJmsTemplate;

  4. import org.apache.activemq.command.ActiveMQQueue;

  5. import org.junit.Test;

  6. import org.springframework.beans.factory.annotation.Autowired;

  7. import org.springframework.jms.core.JmsTemplate;

  8.  
  9. import javax.jms.JMSException;

  10. import javax.jms.TextMessage;

  11.  
  12. /**

  13. * 类描述:receiver测试类

  14. *

  15. * @author fengyong

  16. * @version 1.0

  17. * @since 1.0

  18. * Created by fengyong on 16/8/8 下午5:28.

  19. */

  20. public class ActiveMqReceiver extends BaseTest{

  21. @Autowired

  22. private SimpleJmsTemplate receiverJmsTemplate;

  23. @Autowired

  24. private ActiveMQQueue activeMQQueue;

  25.  
  26.  
  27. @Test

  28. public void recerver() throws JMSException {

  29. int i=1;

  30. while (true){

  31. i++;

  32. TextMessage message = (TextMessage)receiverJmsTemplate.receive(activeMQQueue);

  33. if (null != message) {

  34. System.out.println("收到消息==================" + message.getText());

  35. if(i==4){

  36. throw new RuntimeException("Exception");

  37. }

  38. receiverJmsTemplate.msgAckAndcloseSession(message);

  39. } else {

  40. System.out.print("超时10秒");

  41. break;

  42. }

  43. }

  44. }

  45. }

7:测试结果

 
  1. 收到消息==================我是第1个

  2. 收到消息==================我是第2个

  3. 收到消息==================我是第3个

  4.  
  5. java.lang.RuntimeException

  6. : Exception

  7. at com.test.spring.ActiveMqReceiver.show(ActiveMqReceiver.java:43)

  8. at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

  9. at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)

  10. at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

  11. at java.lang.reflect.Method.invoke(Method.java:606)

  12. at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)

  13. at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)

  14. at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)

  15. at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)

  16. at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:73)

注:接收第三个时失败,虽然这里打印出来了,但是第三个消息并没有被确认接收,同步自定义接收消息修改成功

猜你喜欢

转载自blog.csdn.net/dhdhxgx/article/details/81188671
今日推荐