【分布式事务系列】可靠性消息投递实现最终一致性

338 阅读3分钟

这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战

对于支付服务的本地事务与发送消息操作的原子性问题,存在两种情况如下:

  • 先发送消息,再执行数据库事务,在这种情况下可能会出现消息发送成功但是本地事务更新失败的情况,也会出导致数据不一致的问题。
begin transaction;
sendMessage();
updateStatus();
commit transaction;
  • 先执行数据库事务操作,再发送消息,在这种情况下可能会狐仙MQ响应超时导致异常,从而将本地事务回滚,但消息可能已经发送成功了,也会存在数据不一致的问题。

    begin transaction;
    updateStatus();
    sendMessage();
    commit transaction;
    

    对于以上两种情况有对应解决方案,以使用RocketMQ消息中间件,它提供了事务消息模型,执行流程如下:

    1. 生产者发送一个事务消息到消息队列上,消息队列只记录这条消息的数据,消费者五发消费这条消息。
    2. 生产者执行具体的业务逻辑,完成本地事务的操作。
    3. 生产者根据本地事务的执行结果发送一条确认消息给消息队列服务器,如果本地事务执行成功,则发送一个commit消息,标识在第一步发送的消息可被消费,否则,消费队列服务器会把第一步存储的消息删除。
    4. 如果生产者在执行本地事务的过程中因为某些情况一直未给消息队列服务器发送确认,那消息队列服务器会定时主动回查生产者获取本地事务的执行结果,根据回查结果来决定这条消息是否需要投递给消息者。
    5. 消息队列服务器上存储的消息被生产者确认之后,消费者可消费这条消息,消息消费完成后发送一个确认标识给消息队列服务器,表示消息投递成功。

    在RocketMQ事务消息模型中,事务是由生产者来完成,消费者不需要考虑,因为消息队列可靠性投递机制的存在,如果消费者没有签收该消息,那消息队列服务器会重复投递,从而实现生产者的本地数据和消费者的本地数据在消息队列机制下达到最终一致。

    在RocketMQ的事务消息模型中最核心的机制是事务回查,实际上查询模式在很多场景下都有使用到,在分布式系统中,由于网络通信的存在,服务之间的远程通信除了成功和失败外,还存在一种未知状态,比如网络超时。服务提供者可以提供一个查询接口向外部输出操作的执行状态,服务调用方可通过调用接口得知之前操作的结果并进行相应的处理。