数据库里的事务大家都不陌生,而在微服务架构中由于一个任务执行可能涉及多个微服务,要想在分布式系统实现事务 就要用到分布式事务了。
事务(Transaction),一般指一个事情,一个任务,或者一个执行单位。事务可以看做是一个完整的任务,它由不同的小活动组成,这些活动要么全部成功,要么全部失败。
事务的ACID属性 事务应该具有四个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性。
分布式事务
分布式事务: 分布式系统会把一个应用系统拆分为可独立部署的多个服务,服务与服务之间通过远程协作完成事务操作,这种分布式系统环境下由不同的服务之间通过网络远程协作完成事务称之为分布式事务。
比如存在一个订单的微服务,一个库存的微服务,当订单完成要同步减少库存,要在事务上确保完整和一致。
分布式事务产生的情景
分布式事务基础理论 提到分布式就要涉及到CAP理论了:
一个分布式系统最多只能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三项中的两项。
因此,要么AP,要么CP,要么AC,但是不存在CAP。
而分布式系统中由于拆分成多个微服务部署在不同的网络分区,总是要有 P 的。分区容忍性又是不可或缺的。
Two Phase Commitment Protocol: 2PC 即两阶段提交协议,是将整个事务流程分为两个阶段: 准备阶段(Prepare phase)、提交阶段(commit phase)
二阶段提交是一种强一致性设计,它引入一个事务协调者的角色来协调管理各个参与者(也可称之为各本地资源)的提交和回滚。
它由两个阶段组成:
缺点: 最大缺点是它是一个阻塞协议。如果协调器永久失败,一些参与者将永远无法解决他们的事务。
2PC 适用于数据库层面的分布式事务场景,而我们业务需求有时候不仅仅关乎数据库,也有可能是上传图片发送文本消息等。
可选的技术方案有:JTA
TCC 是指 Try - Confirm - Cancel 。
TCC 提供了三个阶段约定,因此可适用于 业务操作,而不局限在数据库。
TCC 不会一直持有资源锁,它的理念是 “ 先检查资源可用,再执行,大不了就回滚 ” 1、try 是保证资源预留的业务逻辑的正确性。 2、confirm/cancel 执行的本地事务逻辑确认/取消预留资源,以保证最终一致性。
本地消息表其实就是利用了 各系统本地的事务来实现分布式事务。
本地消息表实现的是最终一致性,容忍了数据暂时不一致的情况。
优点是简单。 缺点是严重依赖数据库。
消息事务
利用消息中间件来实现,“事务发起方” 在执行完成本地事务后发出一条消息,然后 “事务参与方(接收消息的一方)" 接收消息并处理任务,它强调是最终一致性。
可以利用 RocketMQ 的 Half Message(半消息)实现一个消息事务。
可靠消息最终一致性方案。
如果不用RocketMQ,也可以基于 ActiveMQ 或者 RabbitMQ 自己封装一套这套逻辑出来。
即 用最大努力,多次反复尝试,直至任务完成,尽力最终让事务都一致了。
比如 用一个后台任务定时去查看未完成的消息,然后去执行对应的任务(调用服务),如果仍然都失败则记录下转由人工处理。
适用于对时间不敏感的业务,例如短信通知。
最大努力通知方案是最终一致性方案。
总结: 具体选型要看场景,TCC 是强一致方案适合要求严格的场景; 其他则考虑使用 可靠消息的最终一致性方案。单体架构操作多数据库则考虑 JPA。
Java Transaction API,通常称为JTA,是用于管理 Java中的事务的API 。它允许我们以资源无关的方式启动,提交和回滚事务。
JTA的真正强大之处在于它能够在单个事务中管理多个资源(如数据库,消息服务)。
JTA 是 两阶段提交 的实现。
在JTA中处理事务的第一种方法是使用@Transactional注解。
@Bean("dataSource1")
public DataSource dataSource() throws Exception {
return createDatasource("jdbc:hsqldb:mem:DB11");
}
@Bean("dataSource2")
public DataSource dataSourceAudit() throws Exception {
return createDatasource("jdbc:hsqldb:mem:DB22");
}
@Transactional
public void executeTrans(String para) {
ervice1.do(.....);
ervice21.do(.....);
if ( .... ) {
throw new RuntimeException();
}
}
2PC的传统方案是在数据库层面实现的,如 Oracle、MySQL 都支持 2PC 协议,为了统一标准减少行业内不必要的对接成本,需要制定标准化的处理模型及接口标准,国际开放标准组织 Open Group 定义了分布式事务处理模型DTP(Distributed Transaction Processing Reference Model)。
Seata 是由阿里中间件团队发起的开源项目,它是一个是开源的分布式事务框架。
传统 2PC 的问题在 Seata 中得到了解决,它通过对本地关系数据库的分支事务的协调来驱动完成全局事务,是工作在应用层的中间件。主要优点是性能较好,且不长时间占用连接资源,它以高效并且对业务 0 侵入的方式解决微服务场景下面临的分布式事务问题,它目前提供 AT 模式(即 2PC)及 TCC 模式的分布式事务解决方案。
利用 RocketMQ 的半消息实现。
TCC 模型涉及到 参与者,事务协调者,和应用, 各个需要提供的API。
参与者 API提供: 1、try 接口,用于try阶段的检查资源
GET /part/123
2、confirm 接口:confirm 阶段的操作
PUT /part/123
其应该有超时判断。
3、cancel 接口(可选实现):用于取消资源预留
PUT /part/123
如果取消失败,也不会影响结果。资源预留要有一个expires截止时间,超过这个截止时间,参与者就可以主动取消这个预留的资源。
事务协调者提供的API 职责有:
接口有: 1、confirm接口:
PUT /coordinator/confirm
然后协调器会对参与者逐个发起Confirm请求。
2、cancel接口
PUT /coordinator/cancel
然后协调器会对参与者逐个发起 cancel 请求。
https://www.oracle.com/java/technologies/jta.html https://github.com/changmingxie/tcc-transaction http://www.tianshouzhi.com/api/tutorials/distributed_transaction/388