分布式事务(1)消息发送一致性解决方案

来源:互联网 发布:windos系统删除mac系统 编辑:程序博客网 时间:2024/06/06 18:10
消息发送一致性
是指产生消息的业务动作与消息发送的一致。(如果业务操作成功,那么由这个业务操作所产生的消息一定要成功投递出去,否则就丢消息)
消息发送一致性如何保障:
场景:
1.业务处理成功,执行发送消息的时候 应用故障,导致没有发送消息(后续服务没有收到消息处理业务,结果数据不一致)
2.业务处理成功,执行发送消息的时候,消息系统(MQ)故障,导致消息发送失败(后续服务没有收到消息处理业务,结果数据不一致)

解决方案:
1.主动方应用先把消息发给消息中间件,消息状态标记为“待确认”
2.消息中间件收到消息后,把消息持久化到消息存储中,但并不向被动方应用投递消息
3.消息中间件返回消息持久化结果(成功/失败),主动方应用根据返回结果进行判断如何进行业务操作处理
a:失败,放弃业务操作处理,结束(必要时向上层返回失败结果)
b:成功,执行业务操作处理
4.业务操作完成后,把业务操作结果(成功/失败)发送给消息中间件
5.消息中间件收到业务操作结果后,根据业务结果进行处理
a:失败:删除消息存储的消息,结束
b:成功:更新消息存储的消息状态为“待发送(可发送)”,紧接着执行消息投递
6.确保了主动方应用业务处理成功就一定会发送消息
7.被动方应用监听并接收“待发送状态的消息”执行业务处理
8.业务处理完成后向消息中间件发送ACK确认,确认消息已经收到(消息中间件将从队列中删除该消息)

消息发送一致性流程中的异常点

异常情况:
主动方
1.预发送消息失败:
消息未进行存储,业务操作未执行(可能的原因:主动方应用、网络、消息中间件、消息存储)【一致】
2.预发送消息后,主动方应用没有收到返回消息存储的结果:
a:消息未进存储,业务操作未执行【一致】
b:消息已进存储(待确认),业务操作未执行【不一致:待确认消息已存储,但是业务却没执行】
3.收到消息存储成功的返回结果,但未执行业务操作就失败
消息已进存储(待确认),业务操作未执行【不一致:待确认消息已存储,但是业务却没执行】

消息中间件
1.消息中间件没有收到主动方应用的业务操作处理结果
a:消息已进存储(待确认),业务操作未执行成功(或业务操作出错回滚了)【不一致:待确认消息已存储,但是业务失败】
b:消息已进存储(待确认),业务操作成功(主动方网络中断)【不一致:待确认消息已存储,但是业务操作成功,但是没有修改消息状态为待发送】
2.消息中间件收到业务操作结果(成功/失败),但处理消息存储中的消息状态失败
a:消息已进存储(待确认),业务操作未执行成功(或业务操作出错回滚了),告知消息中间件失败(MQ操作消息状态失败)【不一致:业务失败,未删除消息】
b:消息已进存储(待确认),业务操作成功,告知消息中间件成功(MQ更新消息状态失败)【不一致:业务成功,消息状态未更新成待发送】

总结:
1.消息未进存储,业务操作未执行【一致】
2.消息已进存储(状态待确认)业务操作未执行或失败【不一致】
异常解决方案:删除消息
3.消息已进存储(状态待确认)业务操作成功【不一致】
异常解决方案:更新消息状态
异常处理还是会有异常
解决方案:消息持久化,创建定时任务查询消息中间件查询状态为“待确认”的消息调用主动方应用的业务操作结果查询接口,返回业务处理结果(成功/失败)去更新消息状态

被动方

消息重复发送的原因
1.被动方应用接收到消息,业务处理完成后应用出问题,消息中间件不知道消息处理结果,会重新投递消息
2.被动方应用接收到消息,业务处理完成后网络出问题,消息中间件不知道消息处理结果,会重新投递消息
3.被动方应用接收到消息,业务处理时间过长,消息中间件因消息超时未确认,会再次投递消息
4.被动方应用接收到消息,业务处理完成,消息中间件问题导致收不到消息处理结果,消息会重新投递
5.被动方应用接收到消息,业务处理完成,消息中间件收到了消息处理结果,但由于消息存储故障导致消息没能确认,消息会再次投递
总结:消息消费过程中产生消息重复发送主要是因为消息接收者成功处理完消息后,消息中间件没能及时更新消息投递状态(也就是消息没能及时ACK确认)导致的

解决方案:
消息重复发送无法解决,通过被动方应用实现幂等性设计(任意多次执行所产生的影响均与一次执行的影响相同)
1.通过业务操作本身实现幂等性
2.系统缓存所有请求与处理结果 检测到重复请求后,自动返回之前的处理结果

极端情况:
消息重发也得有次数限制,要不然就变成了死循环,对于超过重发限制的消息,进入DLQ(死亡队列),等待人工干预或延后定期处理


实现方式:
1.本地消息服务
1.在主动方应用业务操作中,用一个本地事务将业务处理和消息确认数据存放在本地库中,确保业务完成 本地一定有一条“待确认的消息数据”
2.主动方应用业务处理成功后,发送消息至MQ中
3.被动方应用采用自动ACK确认方式,处理业务(需加幂等性设计)
4.业务处理成功后调用主动方消息确认接口(或再加一个消息确认的MQ,只是业务会相对更复杂),修改消息数据状态,完成整体流程
5.消息恢复系统定时轮询一段时间未确认的消息数据,从新放置再MQ中继续消费

优点:
1.消息数据的可靠性不依赖于MQ,弱化了多MQ的依赖
2.方案轻量级,容易实现
缺点:
1.业务和消息耦合性高,不可共用
2.消息数据与业务数据同库,占用资源
3.业务系统在使用关系型数据库的情况下,消息服务性能会受到关系型数据库并发性能的局限

2.独立消息服务

1.主动方应用系统调用消息服务系统预发送消息
2.获得返回结果后处理业务操作,发送结果告知消息服务系统
3.消息服务系统根据业务处理结果(成功/失败)修改消息状态为“待发送”
异常情况:
业务处理成功,但是与消息系统确认消息状态失败
通过消息状态确认子系统定时轮询未确认的消息去主动请求主动方应用获得业务处理结果更新消息状态
4.被动方系统接收消息,ACK确认删除MQ中的消息,处理业务逻辑完成后
5.调用消息系统确认消息已被成功消费(更新消息状态或删除消息记录)
异常情况:
业务处理成功后没有修改成功消息系统此条消息被成功消费状态
通过消息恢复子系统定时轮询成功消费的消息从新放到MQ队列中去重做直到成功
被动方采用幂等性设计保证多次消费的结果一致性

优点:
1.消息服务独立部署,独立维护、独立伸缩
2.消息存储可以按需选择不同的数据库来集成实现(关系型数据库或者nosql)
3.可复用,可以被相同的使用场景共用
4.消息数据的可靠性不依赖于MQ,弱化了多MQ的依赖
5.降低了业务系统和消息系统间的耦合,有利于系统的扩展维护
缺点:
1.一次消息发送需要两次请求,时效性偏低一点
2.主动方应用系统需要实现业务操作状态校验查询接口


消息状态确认子系统设计
1.系统集成了消息系统查询“未确认”数据接口服务,主动方查询业务状态接口服务
2.消息系统查询“未确认“数据接口,查询条件:
状态“未确认“
查询升序(ASC,先处理最早的数据)
指定固定数量(防止数据量过多一次查询出来)
并且消息存放时间大于一定时间(防止拿到新数据)
3.遍历拿到的“未确认“消息数据,调用主动方查询业务状态接口查询业务状态或者未生成业务数据
a)如果业务状态为成功,则修改消息状态为“待发送”
b)如果业务状态为失败或者就没执行业务,则删除消息

消息恢复子系统设计
1.系统集成了消息系统查询“未消费“数据接口服务
2.消息系统查询“未消费“数据接口,查询条件:
状态“未消费“
查询升序(ASC,先处理最早的数据)
指定固定数量(防止数据量过多一次查询出来)
并且消息存放时间大于一定时间(防止拿到新数据)
未死亡的消息(重试多次后还失败的消息会放入死亡队列)
3.遍历拿到的“未消费“消息数据
a)判断消息重试次数是否达到最多重试次数,如果达到则标记此消息死亡(防止消息一直重试)
b)根据消息重试次数判断是否达到了消息间隔
如第1次失败1分钟后就可以尝试重试,第5次失败则可以30分钟后再尝试,第6次就标记为死亡
c)重发消息


消息服务子系统设计
1.存储预发送消息
2.确定并发送消息
3.查询确认超时的消息(一直处于“未发送”状态的消息)
4.确认已被成功消费消息
5.查询消费确认超时的消息(一直处于“未消费”状态的消息)
6.删除消息

实时消息服务子系统设计(操作MQ)
1.修改mq重试次数为1或者0,将重试的机制交给消息恢复子系统去处理,减少频繁短时间内多次重试带来的效率问题

阅读全文
0 0