Spring Boot使用事务向RabbitMQ发送消息

前言

使用SpringBoot向RabbitMQ发送消息非常简单,只需要引入spring-boot-starter-amqp包,再做些简单的配置即可。由于项目对消息有比较高的要求,要求不能丢失任何一条消息,若还是使用默认的发送方式,当网络状况不稳定的时候,有可能会出现消息丢失的情况。这时,我们就需要使用RabbitMQ的事务机制,保证消息发送出去后不会丢失。

事务配置步骤如下:

1. 先引入amqp库

在SpringBoot项目的pom.xml中加入以下依赖库(我这里使用的是Spring Boot 2.1.0.RELEASE版本)。

    <dependency>
	    <groupId>org.springframework.boot</groupId>
	    <artifactId>spring-boot-starter-amqp</artifactId>
	</dependency>

2. 配置application.properties

	spring.rabbitmq.host=x.x.x.x
	spring.rabbitmq.port=5672
	spring.rabbitmq.username=admin
	spring.rabbitmq.password=xxxxxx
	spring.rabbitmq.publisher-confirms=false
	spring.rabbitmq.publisher-returns=true
	spring.rabbitmq.virtual-host=/
	spring.rabbitmq.listener.direct.acknowledge-mode=manual
	spring.rabbitmq.listener.simple.acknowledge-mode=manual
	#以下为队列名称
	mq.exchange=mq.exchange
	mq.ingate.queue=mq.ingate.queue

spring.rabbitmq.publisher-confirms一定要配置为false,否则会与事务处理相冲突,启动时会报异常。

3. RabbitConfig.java配置

	import org.springframework.amqp.core.Binding;
	import org.springframework.amqp.core.BindingBuilder;
	import org.springframework.amqp.core.DirectExchange;
	import org.springframework.amqp.core.Queue;
	import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
	import org.springframework.amqp.rabbit.transaction.RabbitTransactionManager;
	import org.springframework.beans.factory.annotation.Value;
	import org.springframework.context.annotation.Bean;
	import org.springframework.context.annotation.Configuration;
	
	@Configuration
	public class RabbitConfig {
	 
		@Value("${mq.exchange}")
		private String exchange;
		
		@Value("${mq.ingate.queue}")
		private String ingateQueue;
		
		@Bean
	    public DirectExchange exchange() {
	        return new DirectExchange(exchange,true,false);
	    }
	
	    @Bean
	    public Queue ingateQueue() {
	        return new Queue(ingateQueue,true);
	    }
	    
	    @Bean
	    public Binding ingateQueueBinding() {
	        return BindingBuilder.bind(ingateQueue()).to(exchange()).withQueueName();
	    }
	    
	    /**
	     * 配置启用rabbitmq事务
	     * @param connectionFactory
	     * @return
	     */
	    @Bean("rabbitTransactionManager")
	    public RabbitTransactionManager rabbitTransactionManager(CachingConnectionFactory connectionFactory) {
	        return new RabbitTransactionManager(connectionFactory);
	    }
	}

本类用于配置RabbitMQ的Exchange和Queue,同时声明了事务管理器(这个很重要)。

4. 配置消息发送者 RabbitSender.java

	import javax.annotation.PostConstruct;
	
	import org.slf4j.Logger;
	import org.slf4j.LoggerFactory;
	import org.springframework.amqp.AmqpException;
	import org.springframework.amqp.core.Message;
	import org.springframework.amqp.rabbit.connection.CorrelationData;
	import org.springframework.amqp.rabbit.core.RabbitTemplate;
	import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
	import org.springframework.beans.factory.annotation.Autowired;
	import org.springframework.beans.factory.annotation.Value;
	import org.springframework.stereotype.Component;
	import org.springframework.transaction.annotation.Transactional;
	
	import com.xxxx.dto.TradeRefundDTO;
	
	/**
	 * RabbitMQ消息发送类
	 */
	@Component
	public class RabbitSender implements RabbitTemplate.ReturnCallback {
	 
		private static final Logger logger = LoggerFactory.getLogger("mq");
		
	    @Autowired
	    private RabbitTemplate rabbitTemplate;
	    
		@Value("${mq.exchange}")
		private String exchange;
		
		@Value("${mq.ingate.queue}")
		private String ingateQueue;
	    
		@PostConstruct
		private void init() {
		    //启用事务模式,不能开确认回调
			//rabbitTemplate.setConfirmCallback(this);
	       rabbitTemplate.setReturnCallback(this);
			rabbitTemplate.setChannelTransacted(true);
			rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
		}
		
		@Transactional(rollbackFor = Exception.class,transactionManager = "rabbitTransactionManager")
	    public void sendIngateQueue(TradePayModelRes msg) {
			logger.info("进闸消息已发送 {}",msg.getOutTradeNo());
			rabbitTemplate.convertAndSend(exchange,ingateQueue,msg);
	    }

	    @Override
	    public void returnedMessage(Message message, int replyCode, String replyText,String exchange, String routingKey) {
	    	logger.info("消息被退回 {}" , message.toString());
	    }
	
	}

本类用于统一向RabbitMQ发送消息,在发送消息的sendIngateQueue()方法上,加了@Transactional注解,表示这个方法将启用事务(此时的事务即是RabbitMQ事务,因为前面定义了RabbitTransactionManager )。
由于启用了事务,所以需要在系统初始化时,调用rabbitTemplate.setChannelTransacted(true),以激活rabbitTemplate对象事务处理功能。

5. 编写消息消费者 IngateConsumer.java

	import org.slf4j.Logger;
	import org.slf4j.LoggerFactory;
	import org.springframework.amqp.core.Message;
	import org.springframework.amqp.rabbit.annotation.RabbitHandler;
	import org.springframework.amqp.rabbit.annotation.RabbitListener;
	import org.springframework.beans.BeanUtils;
	import org.springframework.stereotype.Component;
	
	import com.alibaba.dubbo.config.annotation.Reference;
	import com.rabbitmq.client.Channel;
	import com.xxxx.gateway.model.TradePayModelRes;
	
	@Component
	@RabbitListener(queues = "${mq.ingate.queue}")
	public class IngateConsumer {
	
		private static Logger logger = LoggerFactory.getLogger("mq");
		
		@RabbitHandler
		public void process(TradePayModelRes tradePayModelRes, Channel channel, Message message) {
			logger.info("收到支付消息 {}",tradePayModelRes.toString());
			try {
					//do samothing
					logger.info("确认消费进闸支付消息 {}",tradePayModelRes.getOutTradeNo());
			} catch (Exception e) {
					logger.info("进闸支付入库异常 {} - {}",tradePayModelRes.getOutTradeNo(),e);
			}
		}
	}

至此,配置完成。
发送消息时,调用rabbitSender.sendIngateQueue()方法,即可启用事务发送消息机制,以保证消息不丢失。
这些只是示例代码,留下记录,方便参考。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值