七、activemq整合springmvc之queue
一、前言
spring代码基于 SSM整合(spring-springmvc-mybatis)之CRUD ;代码地址:(基础版本:https://gitee.com/joy521125/ssm-senior-base.gitmaven版:https://gitee.com/joy521125/ssm-senior.git)
代码地址:https://gitee.com/joy521125/ssm-senior.git(activemq 分支)
测试代码:在/emp/list中,设置了一个推送;
jar包:
版本:
1 <properties> 2 <org.muses.spring.versin>5.3.18</org.muses.spring.versin> 3 <org.muses.activemq.versin>5.16.5</org.muses.activemq.versin> 4 <org.muses.pagehelper.versin>5.3.0</org.muses.pagehelper.versin> 5 </properties>
jar包:
1 <!-- activemq所需要的jar包 --> 2 <dependency> 3 <groupId>org.apache.activemq</groupId> 4 <artifactId>activemq-all</artifactId> 5 <version>${org.muses.activemq.versin}</version> 6 </dependency> 7 <dependency> 8 <groupId>org.apache.xbean</groupId> 9 <artifactId>xbean-spring</artifactId> 10 <version>4.21</version> 11 </dependency> 12 13 <!-- https://mvnrepository.com/artifact/org.springframework/spring-jms --> 14 <!-- activeMQ对JMS的支持,整合Spring和ActiveMQ --> 15 <dependency> 16 <groupId>org.springframework</groupId> 17 <artifactId>spring-jms</artifactId> 18 <version>${org.muses.spring.versin}</version> 19 </dependency> 20 21 <dependency> 22 <groupId>org.apache.activemq</groupId> 23 <artifactId>activemq-pool</artifactId> 24 <version>${org.muses.activemq.versin}</version> 25 </dependency>
二、activem 队列queue配置
默认是持久化;
activemq_queue.xml(生产者)配置信息:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 3 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> 4 <bean id="mQConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"> 5 <!-- tcp://自己的服务器地址:端口 --> 6 <property name="brokerURL" value="tcp://localhost:61616"></property> 7 <property name="userName" value="admin"></property> 8 <property name="password" value="admin"></property> 9 <!-- 强制异步发送大幅提升性能;但意味着无论消息是否已发送,send() 方法都会立即返回,这可能导致消息丢失 --> 10 <property name="useAsyncSend" value="true"></property> 11 <!-- 如果未设置此标志,则不会使用单独的线程为连接中的每个会话分派消息。但是,如果有多个会话,或者会话未处于自动确认或重复 ok 模式,则始终使用单独的线程。 默认情况下,此值设置为 true,并且会话分派异步发生。 --> 12 <property name="alwaysSessionAsync" value="false"></property> 13 </bean> 14 15 <bean id="connectionFactory" class="org.apache.activemq.jms.pool.PooledConnectionFactory"> 16 <property name="connectionFactory" ref="mQConnectionFactory"></property> 17 <property name="maxConnections" value="100"></property> 18 </bean> 19 <!-- 这个是队列目的地,点对点的 --> 20 <bean id="destinationQueue" class="org.apache.activemq.command.ActiveMQQueue"> 21 <constructor-arg index="0" value="spring-active-queue"></constructor-arg> 22 </bean> 23 24 <!-- 发布者配置 实际项目中消费者配置和发布者配置分开 --> 25 <bean id="mQConverter" class="org.springframework.jms.support.converter.SimpleMessageConverter"></bean> 26 <!-- 发布者配置 实际项目中消费者配置和发布者配置分开 --> 27 <bean id="queueTemplate" class="org.springframework.jms.core.JmsTemplate"> 28 <property name="connectionFactory" ref="connectionFactory" /> 29 <property name="defaultDestination" ref="destinationQueue" /> 30 <property name="messageConverter" ref="mQConverter" /> 31 <property name="sessionTransacted" value="true" /> 32 <!-- 消息持久化配置 这里配置的是持久化 --> 33 <!-- 消息持久化 explicitQosEnabled 必须配置 --> 34 <property name="explicitQosEnabled" value="true"></property> 35 <property name="deliveryPersistent" value="true" /><!-- 非持久化配置为 false --> 36 <property name="deliveryMode" value="2"></property><!-- 非持久化配置为 1 --> 37 </bean> 38 <!-- 发布者配置 实际项目中消费者配置和发布者配置分开 --> 39 <bean class="org.muses.ssm.senior.mgt.core.entity.MessagePublisher"> 40 <property name="jmsTemplate" ref="queueTemplate"></property> 41 </bean> 42 43 </beans>
发布消息MessagePublisher.java:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 3 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> 4 <bean id="mQConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"> 5 <!-- tcp://自己的服务器地址:端口 --> 6 <property name="brokerURL" value="tcp://localhost:61616"></property> 7 <property name="userName" value="admin"></property> 8 <property name="password" value="admin"></property> 9 <!-- 强制异步发送大幅提升性能;但意味着无论消息是否已发送,send() 方法都会立即返回,这可能导致消息丢失 --> 10 <property name="useAsyncSend" value="true"></property> 11 <!-- 如果未设置此标志,则不会使用单独的线程为连接中的每个会话分派消息。但是,如果有多个会话,或者会话未处于自动确认或重复 ok 模式,则始终使用单独的线程。 默认情况下,此值设置为 true,并且会话分派异步发生。 --> 12 <property name="alwaysSessionAsync" value="false"></property> 13 </bean> 14 15 <bean id="connectionFactory" class="org.apache.activemq.jms.pool.PooledConnectionFactory"> 16 <property name="connectionFactory" ref="mQConnectionFactory"></property> 17 <property name="maxConnections" value="100"></property> 18 </bean> 19 <!-- 这个是队列目的地,点对点的 --> 20 <bean id="destinationQueue" class="org.apache.activemq.command.ActiveMQQueue"> 21 <constructor-arg index="0" value="spring-active-queue"></constructor-arg> 22 </bean> 23 <!-- 消费者配置 :监听器 --> 24 <bean id="mqMessageListener" class="org.muses.ssm.senior.mgt.core.utiles.MqMessageListener"> 25 </bean> 26 <!-- 消费者配置 实际项目中消费者配置和发布者配置分开 --> 27 <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> 28 <property name="connectionFactory" ref="connectionFactory"></property> 29 <property name="destination" ref="destinationQueue"></property> 30 <property name="messageListener" ref="mqMessageListener"></property> 31 </bean> 32 </beans>
web.xml中添加 activemq_queue.xml配置:
1 <context-param> 2 <param-name>contextConfigLocation</param-name> 3 <param-value>classpath:spring.xml,classpath:activemq_queue.xml</param-value> 4 </context-param>
完整web.xml:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns="http://xmlns.jcp.org/xml/ns/javaee" 4 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" 5 version="4.0"> 6 <filter> 7 <filter-name>CharacterEncodingFilter</filter-name> 8 <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> 9 <init-param> 10 <param-name>encoding</param-name> 11 <param-value>utf-8</param-value> 12 </init-param> 13 </filter> 14 <filter-mapping> 15 <filter-name>CharacterEncodingFilter</filter-name> 16 <url-pattern>/*</url-pattern> 17 </filter-mapping> 18 <context-param> 19 <param-name>contextConfigLocation</param-name> 20 <param-value>classpath:spring.xml,classpath:activemq_queue.xml</param-value> 21 </context-param> 22 <listener> 23 <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 24 </listener> 25 <servlet> 26 <servlet-name>springDispatcherServlet</servlet-name> 27 <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 28 <init-param> 29 <param-name>contextConfigLocation</param-name> 30 <param-value>classpath:springmvc.xml</param-value> 31 </init-param> 32 <load-on-startup>1</load-on-startup> 33 </servlet> 34 35 <servlet-mapping> 36 <servlet-name>springDispatcherServlet</servlet-name> 37 <url-pattern>/</url-pattern> 38 </servlet-mapping> 39 <!-- 配置HiddenHttpMethodFilter :可以把post请求转为delete or put请求 --> 40 41 <filter> 42 <filter-name>HiddenHttpMethodFilter</filter-name> 43 <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> 44 </filter> 45 <filter-mapping> 46 <filter-name>HiddenHttpMethodFilter</filter-name> 47 <url-pattern>/*</url-pattern> 48 </filter-mapping> 49 </web-app>
MqMessageListener:
1 package org.muses.ssm.senior.mgt.core.utiles; 2 3 import javax.jms.Message; 4 import javax.jms.MessageListener; 5 import javax.jms.TextMessage; 6 7 public class MqMessageListener implements MessageListener { 8 9 @Override 10 public void onMessage(Message message) { 11 if (null != message && message instanceof TextMessage) { 12 TextMessage textMessage = (TextMessage) message; 13 System.out.println("textMessage:" + textMessage); 14 } 15 } 16 17 }
推送方法MessagePublisher :
1 package org.muses.ssm.senior.mgt.core.entity; 2 3 import org.springframework.jms.core.JmsTemplate; 4 5 public class MessagePublisher { 6 7 private static JmsTemplate jmsTemplate; 8 9 public static JmsTemplate getJmsTemplate() { 10 return jmsTemplate; 11 } 12 13 public static void setJmsTemplate(JmsTemplate jmsTemplate) { 14 MessagePublisher.jmsTemplate = jmsTemplate; 15 } 16 17 public static void sendMessage(Object message) { 18 jmsTemplate.convertAndSend(message); 19 } 20 21 }
三、事务配置
在Spring+JMS的设计里面,通过sessionTransacted=true可以满足jms的事务,但是这个事务说的是在消费者端,也就是消费不成功会消息会回滚再次被消费。
事务配置
只需要更改activemq_queue.xml 配置信息;增加属性sessionTransacted 属性值设置为true;表示jms的事务由spring托管,但是发送消息中如果又异常,依然会推送。需要在消费者端配置事务,才能达到回滚的效果。如果消费者端有异常,该条消息被重复发送多次(默认重发6次,redeliveryCounter=6),之后将会被移入到死信队列(非持久消息不回放入死性队列),之后broker不会再将该消息发送给消费者。
即:
1 <!-- 发布者配置 实际项目中消费者配置和发布者配置分开 --> 2 <bean id="queueTemplate" class="org.springframework.jms.core.JmsTemplate"> 3 <property name="connectionFactory" ref="connectionFactory" /> 4 <property name="defaultDestination" ref="destinationQueue" /> 5 <property name="messageConverter" ref="mQConverter" /> 6 <!-- 开启事务配置 --> 7 <property name="sessionTransacted" value="true" /> 8 <!-- 消息持久化配置 这里配置的是持久化 --> 9 <!-- 消息持久化 explicitQosEnabled 必须配置 --> 10 <property name="explicitQosEnabled" value="true"></property> 11 <property name="deliveryPersistent" value="true" /><!-- 非持久化配置为 false --> 12 <property name="deliveryMode" value="2"></property><!-- 非持久化配置为 1 --> 13 </bean>
官方文档地址:https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#jms
1 @Override 2 public void onMessage(Message message) { 3 if (null != message && message instanceof TextMessage) { 4 TextMessage textMessage = (TextMessage) message; 5 System.out.println("textMessage:" + textMessage); 6 int a = 1 / 0; 7 } 8 }
1 <!-- 消费者配置 实际项目中消费者配置和发布者配置分开 --> 2 <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> 3 <property name="connectionFactory" ref="connectionFactory"></property> 4 <property name="destination" ref="destinationQueue"></property> 5 <property name="messageListener" ref="mqMessageListener"></property> 6 <property name="sessionTransacted" value="true" /> 7 </bean>
那么效果就是:


四、签收
- AUTO_ACKNOWLEDGE = 1 :自动确认
- CLIENT_ACKNOWLEDGE = 2:客户端手动确认
- DUPS_OK_ACKNOWLEDGE = 3: 自动批量确认
- SESSION_TRANSACTED = 0:事务提交并确认
- INDIVIDUAL_ACKNOWLEDGE = 4:单条消息确认 但是在activemq补充了一个自定义的ACK模式:
设置签收方式,必须关闭事务,否则签收方式的配置无效;
1 <!-- 消费者配置 实际项目中消费者配置和发布者配置分开 --> 2 <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> 3 <property name="connectionFactory" ref="connectionFactory"></property> 4 <property name="destination" ref="destinationQueue"></property> 5 <property name="messageListener" ref="mqMessageListener"></property> 6 <property name="sessionTransacted" value="false"></property><!-- 设置签收方式,必须关闭事务 --> 7 <property name="sessionAcknowledgeMode" value="4"></property><!-- 设置手动签收 --> 8 </bean>
消息监听,设置手动签收:
1 @Override 2 public void onMessage(Message message) { 3 if (null != message && message instanceof TextMessage) { 4 TextMessage textMessage = (TextMessage) message; 5 System.out.println("textMessage:" + textMessage); 6 try { 7 message.acknowledge();// 手动签收 8 } catch (JMSException e) { 9 // TODO Auto-generated catch block 10 e.printStackTrace(); 11 } 12 } 13 }
如果消息监听没有设置了签收:

sessionAcknowledgeMode 设置为2时,不管客户端设置不设置签收都会签收,原因如下:


参考地址:https://blog.csdn.net/weixin_45150104/article/details/123884015

浙公网安备 33010602011771号