使用监听器实现在事务提交后发送消息

用rocketmq构造事件中心

之前说了用消息队列实现了事件中心(不知道算不算),现在虽然换成rabbitmq了但是原理是一样的,这时候就出现了一个问题,就是消息消费的时候,之前的事务还没提交就会有错误。比如编辑机器名称,发送机器编辑事件,在消费消息时把用到机器名称的地方修改机器名称(我们称为冗余字段,这样就减少了关联查询)。但是机器编辑的事务还没有提交。

@Resource
MachineDao machineDao;
@Resource
IMessageSenderService messageSenderService;

@Transactional
public void update(MachineModel machine){
    machineDao.save(machine);
    //因为消息发送包含在事务下面,所以发送消息的时候事务还没提交
    messageSenderService.sendMsg(MessageType.MACHINEEDIT, machine.getId());
}

这时候有两种解决办法,一是把业务方法和消息发送的方法分开,让业务方法的事务不要包含消息发送,但是这样很不合适,消息发送应该和业务代码在同一个方法中,而且如果有一个复合业务包含了这个方法,需要多个方法的事务合并,那么就很麻烦了。

二是使用编程事务,不使用注解的方式,但是在复合业务中同样有问题。

刚开始为此烦恼了很多,并且报了很多错误,使用了很多编程式事务,把产生事件的方法放在复合业务的最后执行,并且事务分开等等等等,有时候不得不破坏事务的一致性。我就想有没有存在一个事务监听的机制,只有监听到事务提交才执行该方法,结果还真被我找到了

Spring 事务提交成功事件监听

Spring 事务事件监控及实现原理

创建一个事务监听器

@Component  
public class TransactionalMessageListener {  
    Logger logger = LoggerFactory.getLogger(this.getClass());
	@Resource
	IMessageSenderService messageSenderService;
	@TransactionalEventListener(fallbackExecution = true,phase=TransactionPhase.AFTER_COMMIT)//AFTER_COMMIT是事务提交后执行,默认就是这个
//fallbackExecution=true则会在没有事务时也执行该方法,不然只有加上事务才会监听,切记。默认是false
    public void handleMessageSend(MessageDTO message) throws Exception{  
		logger.info("***********************messageListener************************");
		logger.info("messageType:"+message.getMessageType());
		logger.info("messageContent"+message.getMessageContent());
		String uuid = UUID.randomUUID().toString();
		messageSenderService.sendMessage(message.getMessageType(), uuid, message.getMessageContent()); 
    }   
} 
@Service
public class MessageSenderServiceImpl implements IMessageSenderService{
	Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired  
    ApplicationEventPublisher publisher;  
    /**
	 * 发送消息到mq
	 * @param type 消息类型
	 * @param message 消息内容
	 * @throws Exception 
	 */
	@Override
	public void sendMessage(String type, String message) throws Exception {
        //触发事件
		publisher.publishEvent(new MessageDTO(type, message));
//		String uuid = UUID.randomUUID().toString();
//		sendMessage(type, uuid, message);
	}
    /**
	 * 发送消息到mq
	 * @param type 消息类型
	 * @param key 消息主键
	 * @param message 消息内容
	 * @throws Exception
	 */
	@Override
    public void sendMessage(String type, String key, String message) throws Exception {
        //发送消息到mq
    }
}

把sendMessage(String type, String message)方法改成触发事件,然后监听器会在这个事件所在的事务提交后执行。这样就不用改任何代码,而且即使事务被包含在了其它事务中也没关系。完美。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值