无风无影

   ::  :: 新随笔  ::  ::  :: 管理

分布式一致性保证模式之【消息中间件】

 消息中间件

a.非事务消息中间件

这里仍然以上面跨行转账为例,我们很难保证在扣款完成之后对MQ投递消息的操作就一定能成功。这样一致性似乎很难保证。以下伪代码说明了消息投递的异常:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
try{
 
    boolean result = dao.update(model);//更新数据库失败抛出异常
 
    if(result){
 
                      mq.send(model);//如果MQ超时或者接收方处理失败,抛出异常
 
    }
 
}catch(Exception ex){
 
                          rollback();//如果异常回滚
 
}

对于以上的运行情况主要有以下几种:

  1. 操作数据库成功,向MQ中投递消息也成功,该属于正常情况,一切都OK。
  2. 操作数据库失败,不会向MQ中投递消息了。
  3. 操作数据库成功,但是向MQ中投递消息时失败,向外抛出了异常,刚刚执行的更新数据库的操作将被回滚。

从上面分析的几种情况来看,基本上能确保,发送消息的可靠性。我们再来分析下消费者端的问题:

  1. 接收者取出消息后,消费者对应的业务操作要执行成功。如果业务执行失败,消息不能失效或者丢失。需要保证消息与业务操作一致。
  2. 尽量确保消息的幂等性。如果出现重复消息投递,能够进行幂等而不对业务产生影响。

b.支持事务的消息中间件

Apache开源的RocketMQ中间件能够支持一种事务消息机制,确保本地操作和发送消息的异步处理达到本地事务的结果一致。

第一阶段,RocketMQ在执行本地事务之前,会先发送一个Prepared消息,并且会持有这个消息的接口回查地址。

第二阶段,执行本地事物操作。

第三阶段,确认消息发送,通过第一阶段拿到的接口地址URL执行回查,并修改状态,如果本地事务成功,则修改状态为已提交,否则修改状态为已回滚。

其中,如果第三阶段的确认消息发送失败后,RocketMQ会有定时任务扫描集群中的事务消息,如果发现还是处于prepare状态的消息,它会向消息发送者确认本地事务是否已执行成功。RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息。这样就保证了消息的发送与本地事务同时成功或同时失败。

再回到上面转账的例子,如果用户A的账户余额已经减少,且消息已经发送成功,作为消费者用户B开始消费这条消息,这个时候就会出现消费失败和消费超时两个问题,解决超时问题的思路就是一直重试,直到消费端消费消息成功,整个过程中有可能会出现消息重复的问题,就需要采用前面说的幂等方案来进行处理。

posted on 2018-11-30 18:06  NWNS-无风无影  阅读(186)  评论(0)    收藏  举报