Spring接入RabbitMQ
在原有的Spring项目中接入RabbitMQ,使用的Spring集成RabbitMQ,这样 RabbitMQ的功能调用通过Spring封装,调用更加简洁。
首先pom 文件中引入依赖
<dependency> <groupId>org.springframework.amqp</groupId> <artifactId>spring-rabbit</artifactId> <version>${spring-rabbit-version}</version> <exclusions> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> </exclusion> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-messaging</artifactId> </exclusion> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> </exclusion> </exclusions> </dependency>
因为已有项目中Spring的版本与引入的spring-rabbit包中依赖的版本不一致,故exclusions中设置了多条 exclusion。
引入 jar 包后,开始 RabbitMQ 的配置,思路等同于数据库,要将程序与 RabbitMQ建立连接,以配置的形式。官网给出的 demo中,RabbitMQ接入 Spring 以xml 文件的形式;接入 SpringBoot以注解的形式。以下以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/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <!-- 连接服务配置 --> <!-- 消息未达到exchange, confirm callback and ack = false ; 消息达到 exchange, confirm callback and ack = true --> <!-- 消息入 queue成功,no return callback ; 消息入 queue 失败,return callback --> <rabbit:connection-factory id="mqConnectionFactory" host="${rabbitMQ.host}" username="${rabbitMQ.username}" password="${rabbitMQ.password}" port="${rabbitMQ.port}" publisher-confirms="true" publisher-returns="true"/> <rabbit:admin connection-factory="mqConnectionFactory"/> <!-- 配置可用 rabbit 注解 --> <!-- <rabbit:annotation-driven/> <bean id="rabbitListenerContainerFactory" class="org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory"> <property name="connectionFactory" ref="mqConnectionFactory"/> <property name="concurrentConsumers" value="3"/> <property name="maxConcurrentConsumers" value="10"/> </bean> --> <rabbit:listener-container connection-factory="mqConnectionFactory" acknowledge="manual"> <rabbit:listener queue-names="q.crm.msg" ref="msgSenderConsumerServiceImpl"/> </rabbit:listener-container> <!-- exchange - rabbitTemplate 配置 多个名称不同,注入使用 @Qualifier mandatory set true, return callback can success --> <rabbit:template id="rabbitTemplate" connection-factory="mqConnectionFactory" confirm-callback="confirmCallBackListener" return-callback="returnCallBackListener" mandatory="true"/> </beans>
将这个配置文件加载到 spring 的配置文件中,文件中的 host等通过 properties 文件设置。
生产者,消费者部分:
生产者:在需要使用的类中注入
@Autowired private RabbitTemplate rabbitTemplate;
生产部分
rabbitTemplate.convertAndSend("e.crm", "k.crm.push.msg", JSON.toJSONString(msg));
第一个参数(e.crm)指定 exchange,第二个参数(k.crm.push.msg)指定binding key,第三个参数为消息内容。
消费部分
@Service("msgSenderConsumerServiceImpl") public class MsgSenderConsumerServiceImpl implements ChannelAwareMessageListener { public void onMessage(Message message, Channel channel) throws IOException { String taskStr = new String(message.getBody(), "UTF-8"); }
实现的是ChannelAwareMessageListener接口,为在方法中使用Channel,不需要使用的话可直接实现MessageListener接口。
配置中生产者确认机制在配置中需要的类有:
@Service("confirmCallBackListener") public class ConfirmCallBackListener implements ConfirmCallback{ @Override public void confirm(CorrelationData correlationData, boolean ack, String cause) { //exchange未到达成功 ack=false //处理逻辑 } }
@Service("returnCallBackListener") public class ReturnCallBackListener implements ReturnCallback{ @Override public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) { //入队列未成功,则调用此方法 //处理逻辑 } }
通过以上配置,RabbitMQ 接入完成。
但是哩,每个 queue的接入都需要修改配置文件,虽然解耦的方式,但是queue 接入后业务比较固定,修改配置文件就不如注解方式灵活。故接入将消费者修改成注解的方式。
放开 RabbitMQ配置中可使用注解的配置,这样在类中可直接使用 RabbitMQ相关的注解。
@Service("msgSenderConsumerServiceImpl") @RabbitListener(queues = "q.crm.msg") public class MsgSenderConsumerServiceImpl { @RabbitHandler public void onMessage(@Payload String message) throws IOException { System.out.println(message); } }
这种形式则 RabbitMQ配置文件中的这部分就不需要了
<rabbit:listener-container connection-factory="mqConnectionFactory"> <rabbit:listener queue-names="q.crm.msg" ref="msgSenderConsumerServiceImpl"/> </rabbit:listener-container>
好,接下来,我们的业务想要在消费者端手动确认,
消费端通过 xml配置时比较好设置,如下:
<rabbit:listener-container connection-factory="mqConnectionFactory" acknowledge="manual"> <rabbit:listener queue-names="q.crm.msg" ref="msgSenderConsumerServiceImpl"/> </rabbit:listener-container>
但是哩,我们想要用注解的形式,从网上搜了各种资料,一直没找到解决方案。
注解使用@RabbitListener时,需要在配置文件中指定rabbitListenerContainerFactory,但是手动确认的设置是在rabbitListenerContainer中,这样就找不到设置的入口。
网上搜索的很多资料,通过设置:spring.rabbitmq.listener.simple.acknowledge-mode=manual
我将设置添加到 spring 的配置文件中,也是不好使,这个配置应该是 Spring-Boot 项目中有效的。
那直接设置的方案使用没有找到可用的,最终的解决方案是,使用自己定义的rabbitListenerContainerFactory,
内容完全参照的spring-rabbit包中的SimpleRabbitListenerContainerFactory类,修改部分是:
@Override protected SimpleMessageListenerContainer createContainerInstance() { SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(); container.setAcknowledgeMode(AcknowledgeMode.MANUAL); return container; }
这样手动确认就配置好了,手动确认消费者端的代码为:
@Service("msgSenderConsumerServiceImpl") @RabbitListener(queues = "q.crm.msg") public class MsgSenderConsumerServiceImpl { @RabbitHandler public void onMessage(@Payload String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) Long deliveryTag) throws IOException { //处理逻辑 channel.basicAck(deliveryTag, false); }
以上~