- 1:Seata  和Rocketmq 事务消息实现,实现 强弱结合型事务

- Seata  + Rocketmq 事务消息 结合

- Seata  + Rocketmq 事务消息 结合的使用场景

- 2.  面试题标准答案: 如何解决分布式事务问题的? 

 - (1)强一致性场景

- (2)弱一致性场景

- (3)强弱结合一致性场景

- 各大模式的总体对比:

1:一图解读分布式事务

首先奉上一张全网最为牛逼的图,给大家做个总览:

1:Seata  和Rocketmq 事务消息实现,实现 强弱结合型事务

秒杀/抢购 等场景,都是属于 强弱结合型 的数据一致性场景。

首先看看 RocketMQ 的事务消息, 能够保证本地操作 + 消息发送的原子性。

具体来说, 主要是保证了本地方法执行和消息发送在一个分布式事务中,要不全部成功,要不全部失败。

见下图:

RocketMQ 通过发送 half 消息来实现,下面详细说明一下:

  1. 消息发送方 向 Broker 发送一条 half 消息;

  2. half 消息发送成功后,消息发送方 执行本地事务;

  3. 如果 消息发送方 执行本地事务成功,则向 Broker 发送 commit 请求,否则发送 rollback 请求;

  4. 如果 Broker 收到的是 rollback 请求,则删除保存的 half 消息;

  5. 如果 Broker 收到的是 commit 请求,则把 half 消息投递到 真实 队列, 等待消费服务来拉取,然后删除保存的 half 消息;

  6. 如果 Broker 没有收到 rollback/commit 请求,则会发送请求到 Producer 查询本地事务状态,然后根据 Producer 返回的本地状态做 commit/rollback 相关处理。

Seata + Rocketmq 事务消息 结合

Seata + Rocketmq 事务消息 结合的目标:

  • 一 是保证 分布式事务 + 消息 发送的原子性。

  • 二 是 再通过mq的重试机制,去保证订阅者的最终一致性,

Seata 的改进主要在 prepare 阶段。

Seata 提供了一个 SeataMQProducer 类,把 RocketMQ 中 TransactionListener 的方法加入到全局事务。

见下面代码:



SeataMQProducer(final String namespace, final String producerGroup, RPCHook rpcHook) {
super(namespace, producerGroup, rpcHook);
this.transactionListener = new TransactionListener() {
@Override
public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
return LocalTransactionState.UNKNOW;
}

@Override
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
String xid = msg.getProperty(PROPERTY\_SEATA\_XID);
if (StringUtils.isBlank(xid)) {
LOGGER.error("msg has no xid, msgTransactionId: {}, msg will be rollback", msg.getTransactionId());
return LocalTransactionState.ROLLBACK\_MESSAGE;
}
GlobalStatus globalStatus = DefaultResourceManager.get().getGlobalStatus(SeataMQProducerFactory.ROCKET\_BRANCH\_TYPE, xid);
if (COMMIT\_STATUSES.contains(globalStatus)) {
return LocalTransactionState.COMMIT\_MESSAGE;
} else if (ROLLBACK\_STATUSES.contains(globalStatus) || GlobalStatus.isOnePhaseTimeout(globalStatus)) {
return LocalTransactionState.ROLLBACK\_MESSAGE;
} else if (GlobalStatus.Finished.equals(globalStatus)) {
LOGGER.error("global transaction finished, msg will be rollback, xid: {}", xid);
return LocalTransactionState.ROLLBACK\_MESSAGE;
}
return LocalTransactionState.UNKNOW;
}
};



在 prepare 阶段,SeataMQProducer 向 RocketMQ Broker 发送 half 消息,执行本地事务,如果执行成功,则强行把 LocalTransactionState 改回 UNKNOW,等待 TC 发送指令,决定是 commit 或 rollback。

如果执行失败,返回 ROLLBACK_MESSAGE,TC 下发指令,回滚全局事务。

Seata + Rocketmq 事务消息 结合的使用场景

如果 MQ Broker 没有收到 commit/rollback 消息,则会回查 Producer 本地事务状态,也就是上面代码中的 checkLocalTransaction。

checkLocalTransaction 检查 全局事务状态,使用 XID 去查询 全局事务,去决定 half 消息 是 抛弃还是 投递 。

集成 RocketMQ 之后,Seata 的分布式事务调用流程, 下面以 订单服务、库存服务两个服务为例:

Apache Seata 引入 RocketMQ 后,支持的分布式事务场景更加丰富,使得 Seata 可以用于 强一致性 + 弱一致性 结合的场景。

2. 面试题标准答案: 如何解决分布式事务问题的?

现在Java面试,分布式系统、分布式事务几乎是标配。而分布式系统、分布式事务本身比较复杂,大家学起来也非常头疼。



面试题:分布式事务了解吗?你们是如何解决分布式事务问题的?



Seata AT/TCC 和 MQ异步确保型 事务 是在生产中最常用。

  • 强一致性模型, Seata AT/TCC 强一致方案 模式用于强一致主要用于核心模块,例如交易/订单等。

  • 弱一致性模型。 MQ异步确保型 事务 弱一致方案一般用于边缘模块例如库存,通过MQ 原子消息和发布订阅,保证最终一致性,也可以业务解耦。

面试中如果你真的被问到,可以分场景回答:

(1)强一致性场景

对于那些特别严格的场景,用的是Seata AT模式来保证强一致性;

准备好例子:你找一个严格要求数据绝对不能错的场景(如电商交易交易中的库存和订单、优惠券),可以回答使用成熟的如中间件Seata AT模式。



阿里开源了分布式事务框架seata经历过阿里生产环境大量考验的框架。seata支持Dubbo,Spring Cloud。


是Seata AT/TCC模式,保障强一致性,支持跨多个库修改数据;

  • 订单库:增加订单

  • 商品库:扣减库存

  • 优惠券库:预扣优惠券

(2)弱一致性场景

基于可靠消息的最终一致性,各个子事务可以较长时间内异步,但数据绝对不能丢的场景。可以使用异步确保型事务事。

可以使用基于MQ的异步确保型事务,比如电商平台的通知支付结果:

  • 积分服务:增加积分

  • 会计服务:生成会计记录

(3)强弱结合一致性场景

两阶段 提交,如 Seata AT/TCC模式,保障强一致性,支持跨多个库修改数据;

  • 订单库:增加订单

  • 商品库:扣减库存

  • 优惠券库:预扣优惠券

异步确保型事务,保障弱一致性,支持跨多个服务和系统修改数据,在下面的场景中相关的弱一致性操作为:

  • 积分服务:增加积分

  • 通知服务:发生通知

弱一致性部分:如果不是严格对数据一致性要求、或者由不同系统执行子事务的场景,如电商发送成功支付成功消息,只需要保障弱一致性即可。

各大模式的总体对比:

属性 2PC TCC Saga 异步确保型事务 尽最大努力通知
事务一致性
复杂性
业务侵入性
使用局限性
性能
维护成本