Spring 整合 RabbitMQ
1.导入依赖
<properties> ..... <!-- spring --> <spring.version>5.1.1.RELEASE</spring.version> <!-- log4j日志包版本号 --> <slf4j.version>1.7.18</slf4j.version> <log4j.version>1.2.17</log4j.version> </properties> <dependencies> <!-- Spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-oxm</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <!-- AOP-AspectJ spring-aop依赖 --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.8.6</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.6</version> </dependency> <!-- 添加日志相关jar包 --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${slf4j.version}</version> </dependency> <!-- rabbitmq --> <dependency> <groupId>org.springframework.amqp</groupId> <artifactId>spring-rabbit</artifactId> <version>1.7.5.RELEASE</version> </dependency> ..... </dependencies>
2.创建配置文件
a.创建 spring.xml
<?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:aop="http://www.springframework.org/schema/aop" 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 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 自动扫描的包名 --> <context:component-scan base-package="com.wode" /> <!-- 开启AOP代理 --> <aop:aspectj-autoproxy proxy-target-class="true" /> <!--开启注解处理器 --> <context:annotation-config> </context:annotation-config> <context:property-placeholder location="classpath:rabbit.properties"/> <!-- Spring中引入其他配置文件 --> <import resource="classpath*:/spring-rabbit.xml" /> </beans>
b.创建 spring-rabbit.xml
<?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:rabbit="http://www.springframework.org/schema/rabbit" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit-1.2.xsd"> <!-- ============================================公共部分============================================ --> <!-- 创建连接类 连接安装好的 rabbitmq --> <rabbit:connection-factory id="connectionFactory" host="${rabbit.ip}" port="${rabbit.port}" username="${rabbit.username}" password="${rabbit.password}" /> <rabbit:admin connection-factory="connectionFactory"/> <!-- spring amqp默认的是jackson 的一个插件,目的将生产者生产的数据转换为json存入消息队列 --> <bean id="jsonMessageConverter" class="org.springframework.amqp.support.converter.Jackson2JsonMessageConverter" /> <!-- ============================================direct路由模式============================================ --> <!--定义消息队列,durable:是否持久化,如果想在RabbitMQ退出或崩溃的时候,不会失去所有的queue和消息,需要同时标志队列(queue)和交换机(exchange)是持久化的,即rabbit:queue标签和rabbit:direct-exchange中的durable=true,而消息(message)默认是持久化的可以看类org.springframework.amqp.core.MessageProperties中的属性public static final MessageDeliveryMode DEFAULT_DELIVERY_MODE = MessageDeliveryMode.PERSISTENT;exclusive: 仅创建者可以使用的私有队列,断开后自动删除;auto_delete: 当所有消费客户端连接断开后,是否自动删除队列 --> <rabbit:queue name="direct.queue.1" id="direct.queue.1" durable="true" auto-delete="false" exclusive="false" /> <rabbit:queue name="direct.queue.2" id="direct.queue.2" durable="true" auto-delete="false" exclusive="false" /> <!--绑定队列,rabbitmq的exchangeType常用的三种模式:direct,fanout,topic三种,我们用direct模式,即rabbit:direct-exchange标签,Direct交换器很简单,如果是Direct类型,就会将消息中的RoutingKey与该Exchange关联的所有Binding中的BindingKey进行比较,如果相等,则发送到该Binding对应的Queue中。有一个需要注意的地方:如果找不到指定的exchange,就会报错。但routing key找不到的话,不会报错,这条消息会直接丢失,所以此处要小心,auto-delete:自动删除,如果为Yes,则该交换机所有队列queue删除后,自动删除交换机,默认为false --> <rabbit:direct-exchange id="direct.exchange" name="direct.exchange" durable="true" auto-delete="false"> <rabbit:bindings> <rabbit:binding queue="direct.queue.1" key="${routing.1}"></rabbit:binding> <rabbit:binding queue="direct.queue.2" key="${routing.2}"></rabbit:binding> </rabbit:bindings> </rabbit:direct-exchange> <rabbit:template exchange="direct.exchange" id="rabbitTemplate" connection-factory="connectionFactory" message-converter="jsonMessageConverter" /> <!-- 消费者部分 --> <!-- 自定义接口类 --> <bean id="directConsumerAuto" class="com.wode.direct.DirectConsumerAuto"></bean> <bean id="directConsumerManual" class="com.wode.direct.DirectConsumerManual"></bean> <!-- 配置监听acknowledeg="manual"设置手动应答,它能够保证即使在一个worker处理消息的时候用CTRL+C来杀掉这个worker,或者一个consumer挂了(channel关闭了、connection关闭了或者TCP连接断了),也不会丢失消息。因为RabbitMQ知道没发送ack确认消息导致这个消息没有被完全处理,将会对这条消息做re-queue处理。如果此时有另一个consumer连接,消息会被重新发送至另一个consumer会一直重发,直到消息处理成功,监听容器acknowledge="auto" concurrency="30"设置发送次数,最多发送30次 --> <rabbit:listener-container connection-factory="connectionFactory" acknowledge="auto" concurrency="20"> <rabbit:listener queues="direct.queue.1" ref="directConsumerAuto" /> </rabbit:listener-container> <rabbit:listener-container connection-factory="connectionFactory" acknowledge="manual" concurrency="20"> <rabbit:listener queues="direct.queue.2" ref="directConsumerManual" /> </rabbit:listener-container> <!-- ============================================fanout订阅推送模式============================================ --> <!--定义消息队列--> <rabbit:queue name="fanout.queue.1" id="fanout.queue.1" durable="true" auto-delete="false" exclusive="false" /> <rabbit:queue name="fanout.queue.2" id="fanout.queue.2" durable="true" auto-delete="false" exclusive="false" /> <!-- Fanout 扇出,顾名思义,就是像风扇吹面粉一样,吹得到处都是。如果使用fanout类型的exchange,那么routing key就不重要了。因为凡是绑定到这个exchange的queue,都会受到消息。 --> <rabbit:fanout-exchange id="fanout.exchange" name="fanout.exchange" durable="true" auto-delete="false"> <rabbit:bindings> <rabbit:binding queue="fanout.queue.1"></rabbit:binding> <rabbit:binding queue="fanout.queue.2"></rabbit:binding> </rabbit:bindings> </rabbit:fanout-exchange> <rabbit:template exchange="fanout.exchange" id="fanoutRabbitTemplate" connection-factory="connectionFactory" message-converter="jsonMessageConverter" /> <!-- 消费者部分 --> <!-- 自定义接口类 --> <bean id="fanoutConsumerAuto" class="com.wode.fanout.FanoutConsumerAuto"></bean> <bean id="fanoutConsumerManual" class="com.wode.fanout.FanoutConsumerManual"></bean> <!-- 配置监听acknowledeg="manual"设置手动应答,它能够保证即使在一个worker处理消息的时候用CTRL+C来杀掉这个worker,或者一个consumer挂了(channel关闭了、connection关闭了或者TCP连接断了),也不会丢失消息。因为RabbitMQ知道没发送ack确认消息导致这个消息没有被完全处理,将会对这条消息做re-queue处理。如果此时有另一个consumer连接,消息会被重新发送至另一个consumer会一直重发,直到消息处理成功,监听容器acknowledge="auto" concurrency="30"设置发送次数,最多发送30次 --> <rabbit:listener-container connection-factory="connectionFactory" acknowledge="auto" concurrency="20"> <rabbit:listener queues="fanout.queue.1" ref="fanoutConsumerAuto" /> </rabbit:listener-container> <rabbit:listener-container connection-factory="connectionFactory" acknowledge="manual" concurrency="20"> <rabbit:listener queues="fanout.queue.2" ref="fanoutConsumerManual" /> </rabbit:listener-container> <!-- ============================================topic模式============================================ --> <!--定义消息队列--> <rabbit:queue name="topic.queue.1" id="topic.queue.1" durable="true" auto-delete="false" exclusive="false" /> <rabbit:queue name="topic.queue.2" id="topic.queue.2" durable="true" auto-delete="false" exclusive="false" /> <!-- 发送端不是按固定的routing key发送消息,而是按字符串“匹配”发送,接收端同样如此 --> <rabbit:topic-exchange id="topic.exchange" name="topic.exchange" durable="true" auto-delete="false"> <rabbit:bindings> <rabbit:binding queue="topic.queue.1" pattern="order.*" /> <rabbit:binding queue="topic.queue.2" pattern="*.insert" /> </rabbit:bindings> </rabbit:topic-exchange> <rabbit:template exchange="topic.exchange" id="topicRabbitTemplate" connection-factory="connectionFactory" message-converter="jsonMessageConverter" /> <!-- 消费者部分 --> <!-- 自定义接口类 --> <bean id="topicConsumerAuto" class="com.wode.topic.TopicConsumerAuto"></bean> <bean id="topicConsumerManual" class="com.wode.topic.TopicConsumerManual"></bean> <!-- 配置监听acknowledeg="manual"设置手动应答,它能够保证即使在一个worker处理消息的时候用CTRL+C来杀掉这个worker,或者一个consumer挂了(channel关闭了、connection关闭了或者TCP连接断了),也不会丢失消息。因为RabbitMQ知道没发送ack确认消息导致这个消息没有被完全处理,将会对这条消息做re-queue处理。如果此时有另一个consumer连接,消息会被重新发送至另一个consumer会一直重发,直到消息处理成功,监听容器acknowledge="auto" concurrency="30"设置发送次数,最多发送30次 --> <rabbit:listener-container connection-factory="connectionFactory" acknowledge="auto" concurrency="20"> <rabbit:listener queues="topic.queue.1" ref="topicConsumerAuto" /> </rabbit:listener-container> <rabbit:listener-container connection-factory="connectionFactory" acknowledge="manual" concurrency="20"> <rabbit:listener queues="topic.queue.2" ref="topicConsumerManual" /> </rabbit:listener-container> </beans>
c.创建 rabbit.properties
#RabbitMQ服务器地址,默认值"localhost" rabbit.ip=localhost #RabbitMQ服务端口,默认值为5672 rabbit.port=5672 #访问RabbitMQ服务器的账户,默认是guest rabbit.username=guest #访问RabbitMQ服务器的密码,默认是guest rabbit.password=guest #路由标识 routing.1=1 routing.2=2
3.创建生产者
import org.springframework.amqp.core.AmqpTemplate; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.annotation.Resource; @Component public class CommonProducer { //direct模式 @Resource(name = "rabbitTemplate") private AmqpTemplate rabbitTemplate; @Value("${routing.1}") private String routing1; @Value("${routing.2}") private String routing2; //fanout模式 @Resource(name = "fanoutRabbitTemplate") private AmqpTemplate fanoutRabbitTemplate; //topic模式 @Resource(name = "topicRabbitTemplate") private AmqpTemplate topicRabbitTemplate; public void send(){ rabbitTemplate.convertAndSend(routing1, "routing1"); rabbitTemplate.convertAndSend(routing2, "routing2"); fanoutRabbitTemplate.convertAndSend("fanoutMsg"); topicRabbitTemplate.convertAndSend("order.insert", "order.insert"); topicRabbitTemplate.convertAndSend("order.update", "order.update"); } }
4.创建direct模式消费者
a.自动提交消费者,对应配置文件中 acknowledge="auto"
import com.rabbitmq.client.Channel; import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener; public class DirectConsumerAuto implements ChannelAwareMessageListener { @Override public void onMessage(Message message, Channel channel) throws Exception { String msg = new String(message.getBody(),"UTF-8"); System.out.println("[DirectConsumerAuto]消费者接收到:" + msg); } }
b.手动提交消费者,对应配置文件中 acknowledge="manual"
import com.rabbitmq.client.Channel; import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener; public class DirectConsumerManual implements ChannelAwareMessageListener { @Override public void onMessage(Message message, Channel channel) throws Exception { String msg = new String(message.getBody(),"UTF-8"); System.out.println("[DirectConsumerManual]消费者接收到:" + msg); //手动确认 channel.basicAck(message.getMessageProperties().getDeliveryTag(), true); } }
5.创建fanout模式消费者
a.自动提交消费者,对应配置文件中 acknowledge="auto"
import com.rabbitmq.client.Channel; import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener; public class FanoutConsumerAuto implements ChannelAwareMessageListener { @Override public void onMessage(Message message, Channel channel) throws Exception { String msg = new String(message.getBody(),"UTF-8"); System.out.println("[FanoutConsumerAuto]消费者接收到:" + msg); } }
b.手动提交消费者,对应配置文件中 acknowledge="manual"
import com.rabbitmq.client.Channel; import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener; public class FanoutConsumerManual implements ChannelAwareMessageListener { @Override public void onMessage(Message message, Channel channel) throws Exception { String msg = new String(message.getBody(),"UTF-8"); System.out.println("[FanoutConsumerManual]消费者接收到:" + msg); //手动确认 channel.basicAck(message.getMessageProperties().getDeliveryTag(), true); } }
6.创建topic模式消费者
a.自动提交消费者,对应配置文件中 acknowledge="auto"
import com.rabbitmq.client.Channel; import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener; public class TopicConsumerAuto implements ChannelAwareMessageListener { @Override public void onMessage(Message message, Channel channel) throws Exception { String msg = new String(message.getBody(),"UTF-8"); System.out.println("[TopicConsumerAuto]消费者接收到:" + msg); } }
b.手动提交消费者,对应配置文件中 acknowledge="manual"
import com.rabbitmq.client.Channel; import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener; public class TopicConsumerManual implements ChannelAwareMessageListener { @Override public void onMessage(Message message, Channel channel) throws Exception { String msg = new String(message.getBody(),"UTF-8"); System.out.println("[TopicConsumerManual]消费者接收到:" + msg); //手动确认 channel.basicAck(message.getMessageProperties().getDeliveryTag(), true); } }
7.测试
public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml"); CommonProducer commonProducer = (CommonProducer) applicationContext.getBean("commonProducer"); commonProducer.send(); }