写于:2020-03-20 21:52:37;代码完成,文章为完成

分布式事务的解决方案可以分为两大类:刚性事务和柔性事务 本文通过案例提供柔性事务解决分布式事务的方案

# 案例背景

当前系统中存在多个独立系统,其中 “保险系统” 需要和 “积分商城系统” 打通。 需求:保险订单下单 同时 积分商城 生成积分。【真实业务为冻结积分,这里为了简化直接加入积分】 分析:“保险系统” 和 “积分商城系统” 为两个独立系统,存在数据一致性问题。【系统同间通过身份证id进行业务关联】

# 采用 消息驱动-柔性事务 进行解决

友链 谈谈分布式事务

方案图,如下

案例解决方案图

使用 “异步确保模式”,“定期校对模式”,“查询模式”,“人工处理” 来保证数据的最终一致性。

# 解决方案具体分析

两个系统间的业务是耦合的,保险下单成功,必须要保证对应的积分生成成功,两个操作是一个事务,本次案例,从业务角度,一旦保险下单成功了,即使最后由于各种原因导致的积分生成失败也不应该会滚保险订单,只能尽最大的努力保证数据的最终一致性。

根据业务剖析使用三种方式来确保数据的最终一致性。

# 异步确保模式

使用 RabbitMQ 来完成,通过 RabbitMQ 消息中间件剥离“保险系统”下单操作 和 “积分商城系统” 积分累加操作。

异步确保模式需要完成的功能点:

  • 1、下单完成后,推送给积分商城的消息,至少被发送一次
  • 2、积分商城的消息消费,需要保证处理的幂等性,消息对业务影响有且仅有一次。

# 尽最大努力通知 + 定期校对

数据库中缓存有消息发送记录,并记录有包括但不仅限于 重试次数,消息处理状态信息。

  • 1、通过 RabbitMQ 本身特性,通过 confirm 和 return 来实现在网络抖动等其他原因导致消息没法路由到队列时进行重试
  • 2、通过 定时任务 ,根据消息发送记录中状态为异常的数据,进行消息的重新发送。

# 查询模式

可能存在消息发送记录记录失败,消息发送失败的场景,虽然几率很小

积分商城提供查询接口,根据系统间关联键【身份证】 和 【保险订单】 查询积分生成记录。 如果不存在积分生成记录,则进行消息重发。

# 人工兜底

消息中间件挂了,或者其他原因导致的消息无法发送的问题(几乎不存在)

积分商城提供入口,让运营人员人工进行积分发放。

# 关键代码实现

根据场景可以通过多数据源,或者下沉为一个服务进行不同库的处理。【本案例将其下沉为一个基础设施服务】

# 项目结构

项目结构图

infra:提供 消息缓存的增删改查,消息的轮询重发 insurance:模拟 下单完成后的 消息的可靠投递 integral:模拟 消息的有效消费

# 消息记录表结构

主键id的生成最好是递增的,避免页分裂和页移动。

mq表结构图

# RabbitMQ 确认机制,保证消息的有效投递

通过 confirm 保证 消息至少发送一次

消息有效发送_confiram代码

通过 return 当,消息无法到达 队列时进行错误记录

# RabbitMq 确定消息只消费一次

业务需要保证幂等性,因为,消息至少发送了一次

配置消息消费手动 ack,相关代码

配置代码

消费手动ack配置图

使用代码

消费者消费手动ack使用代码图图

# integral 积分商城提供查询接口

积分商城积分累加时会生成积分记录,同时记录来源(保险系统或者其他),订单号(保险系统就保险订单号),用户标识。

integral 提供接口:入参为:记录来源,订单号,身份标识,进行查询是否存在积分生成记录。

# infra 定时轮询

定时查询消息库里面未发送的消息进行重新发送。

# 方案整理

整个系统操作流程

# 消息发送流程

下单 -> 生成消息记录 -> 发送消息到 mq

# 消息消费流程

接收 mq消息 -> 消费mq消息

精彩内容推送,请关注公众号!
最近更新时间: 3/24/2020, 9:44:42 PM