分布式一致性保证模式之【消息中间件】
消息中间件
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();//如果异常回滚} |
对于以上的运行情况主要有以下几种:
- 操作数据库成功,向MQ中投递消息也成功,该属于正常情况,一切都OK。
- 操作数据库失败,不会向MQ中投递消息了。
- 操作数据库成功,但是向MQ中投递消息时失败,向外抛出了异常,刚刚执行的更新数据库的操作将被回滚。
从上面分析的几种情况来看,基本上能确保,发送消息的可靠性。我们再来分析下消费者端的问题:
- 接收者取出消息后,消费者对应的业务操作要执行成功。如果业务执行失败,消息不能失效或者丢失。需要保证消息与业务操作一致。
- 尽量确保消息的幂等性。如果出现重复消息投递,能够进行幂等而不对业务产生影响。
b.支持事务的消息中间件
Apache开源的RocketMQ中间件能够支持一种事务消息机制,确保本地操作和发送消息的异步处理达到本地事务的结果一致。
第一阶段,RocketMQ在执行本地事务之前,会先发送一个Prepared消息,并且会持有这个消息的接口回查地址。
第二阶段,执行本地事物操作。
第三阶段,确认消息发送,通过第一阶段拿到的接口地址URL执行回查,并修改状态,如果本地事务成功,则修改状态为已提交,否则修改状态为已回滚。
其中,如果第三阶段的确认消息发送失败后,RocketMQ会有定时任务扫描集群中的事务消息,如果发现还是处于prepare状态的消息,它会向消息发送者确认本地事务是否已执行成功。RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息。这样就保证了消息的发送与本地事务同时成功或同时失败。
再回到上面转账的例子,如果用户A的账户余额已经减少,且消息已经发送成功,作为消费者用户B开始消费这条消息,这个时候就会出现消费失败和消费超时两个问题,解决超时问题的思路就是一直重试,直到消费端消费消息成功,整个过程中有可能会出现消息重复的问题,就需要采用前面说的幂等方案来进行处理。

浙公网安备 33010602011771号