写在前面

hello大家好

欢迎大家来到御风大世界

今天是我们dubbo系列教程的第9课

在这次课我将跟大家分享

关于dubbo的分布式事务

dubbo作者是怎么看的 !

引用

以下所有内容都引用自 dubbo作者的博客

他自己其实在 7 年以前就已经公开阐述过自己对于 dubbo的分布式事务理解

我们一起来看他的描述 !


关于Dubbo服务框架的分布式事务,虽然现在不急着做,但可以讨论一下。

我觉得事务的管理不应该属于Dubbo框架,
Dubbo只需实现可被事务管理即可,
像JDBC和JMS都是可被事务管理的分布式资源,
Dubbo只要实现相同的可被事务管理的行为,比如可以回滚,
其它事务的调度,都应该由专门的事务管理器实现。

在Java中,分布式事务主要的规范是JTA/XA,
其中:JTA是Java的事务管理器规范,
XA是工业标准的X/Open CAE规范,可被两阶段提交及回滚的事务资源定义,
比如某数据库实现了XA规范,则不管是JTA,还是MSDTC,都可以基于同样的行为对该数据库进行事务处理。

在JTA/XA中,主要有两个扩展点:

(1) TransactionManager
JTA事务管理器接口,实现该接口,即可完成对所有XA资源的事务调度,比如BEA的Tuxedo,JBossJTA等。

(2) XAResource
XA资源接口,实现该接口,即可被任意TransactionManager调度,比如:JDBC的XAConnection,JMS的XAMQ等。

而Dubbo的远程服务,也应该是一个XAResource,比如:XAInvoker和XAExporter,
Dubbo只需在第一次提交时,将请求发到服务提供方进行缓存和写盘,
在第二次提交时,再基于缓存调用服务的Impl实现,
当然一些健状性分支流程要考虑清楚。

JTA/XA的基本原理如下:

1.用户启动一个事务:

transactionManager.begin();

2.事务管理器在当前线程中初始化一个事务实例:

threadLocal.set(new TransactionImpl());

3.用户调用JDBC或JMS或Dubbo请求,请求内部初始化一个XAResource实例:

XAResource xaResource = new XAResourceImpl(); // 比如:XAConnection

4.JDBC或JMS或Dubbo内部从当前线程获取事务:

Transaction transaction = transactionManager.getTransaction(); // 其内部为:threadLocal.get();

5.将当前XAResource注册到事务中:

transaction.enlistResource(xaResource);

6.用户提交一个事务:

transactionManager.commit(); // 其内部为:getTransaction().commit();

7.事务for循环调用所有注册的XAResource的两阶段提交:

Xid xid = new XidImpl();  
  for (XAResource xaResource: xaResources) {  
  xaResource.prepare(xid);  
  xaResource.commit(xid, true);  
  xaResource.commit(xid, false);  
}

8.当然,还有一些异常流程,比如rollback和forget等。

TransactionManager transactionManager = ...; // 从JNDI进行lookup等方式获取  
  transactionManager.begin(); // 启动事务  
  try {  
      jdbcConn.executeUpdate(sql); // 执行SQL语句,DB写入binlog,但不更新表  
      jmsMQ.send(message); // 发送消息,MQ记录消息,但不进入队列  
     dubboService.invoke(parameters); // 调用远程服务,Provider缓存请求信息,但不执行  
      transactionManager.commit(); // 提交事务,数据库,消息队列,远程服务同时提交  
  } catch(Throwable t) {  
      transactionManager.rollback(); // 回滚事务,数据库,消息队列,远程服务同时回滚  
 }

我的体会

其实不难发现

作者当时认为

dubbo其实不应该作为事务的一个管理者

或者分布式事务的框架

而是作为分布式事务的参与者

因为在当时已经有了对于 分布式事务管理的方案

是 基于 JTA/XA的 方式

dubbo怎样实现分布式事务 dubbo分布式事务问题_dubbo怎样实现分布式事务

我们可以简单的理解这就是一个 2pc 的方案

也就是 交给一个统一的 外部事务管理器

来管理所有的分布式事务

在整个事务在管理过程中

每一个事务的参与者(dubbo , jms , db) 都是 先给一个预备状态

等到大家就绪了

在轮询各自提交

提交完了以后交由事务管理器查看所有的提交状态

如果在过程中有一个参与者 事务提交失败(异常)

那么所有参与者自行事务回滚.

dubbo的serviceprovider 在收到一个RPC请求的时候

先不实际调用

而是写到磁盘或者缓存 调用信息 RpcRequest

等到事务管理器告知可以提交再实际调用

也就是dubbo作为一个事务参与者, 遵循事务的管理规范就好了

这也是一种不错设计,专业的事交给专业的框架来处理

自己没有必要重复造轮子

2PC的优缺点

优点

尽量保证了数据的强一致,实现成本较低,在各大主流数据库都有自己实现,对于 MySQL 是从 5.5 开始支持。

缺点

单点问题: 事务管理器在整个流程中扮演的角色很关键,如果其宕机,比如在第一阶段已经完成,在第二阶段正准备提交的时候事务管理器宕机,资源管理器就会一直阻塞,导致数据库无法使用。

同步阻塞: 在准备就绪之后,资源管理器中的资源一直处于阻塞,直到提交完成,释放资源。

数据不一致: 两阶段提交协议虽然为分布式数据强一致性所设计,但仍然存在数据不一致性的可能。

比如在第二阶段中,假设协调者发出了事务 Commit 的通知,但是因为网络问题该通知仅被一部分参与者所收到并执行了 Commit 操作,其余的参与者则因为没有收到通知一直处于阻塞状态,这时候就产生了数据的不一致性。

总的来说,XA 协议比较简单,成本较低,但是其单点问题,以及不能支持高并发(由于同步阻塞)依然是其最大的弱点。

更好的解决方案?

其实 现在对于分布式事务的解决方案已经有很多了

开源的和商业的解决方案都有

我相信读者朋友中

很多都已经有所接触了.

主要有三个

TCC

消息队列最终一致性

定期(校对)补偿

接下来的几节课

我会和大家一起来探讨

分布式事务的解决方案

虽然我在这方面也不是很专业

但是我希望大家可以和我一起来关注这个问题

如果我讲的有什么不对的地方

大家可以共同成长