【微服务】分布式事务的实现方法及替代方案
这两天正在研究微服务架构中分布式事务的处理方案, 做一个小小的总结, 作为备忘. 如有错误, 欢迎指正!
概念澄清
事务补偿机制: 在事务链中的任何一个正向事务操作, 都必须存在一个完全符合回滚规则的可逆事务.
CAP理论: CAP(Consistency, Availability, Partition Tolerance), 阐述了一个分布式系统的三个主要方面, 只能同时择其二进行实现. 常见的有CP系统, AP系统.
幂等性: 简单的说, 业务操作支持重试, 不会产生不利影响. 常见的实现方式: 为消息额外增加唯一ID.
BASE(Basically avaliable, soft state, eventually consistent): 是分布式事务实现的一种理论标准.
柔性事务 vs. 刚性事务
刚性事务是指严格遵循ACID原则的事务, 例如单机环境下的数据库事务.
柔性事务是指遵循BASE理论的事务, 通常用在分布式环境中, 常见的实现方式有: 两阶段提交(2PC), TCC补偿型提交, 基于消息的异步确保型, 最大努力通知型.
通常对本地事务采用刚性事务, 分布式事务使用柔性事务.
最佳实践
先上结论, 再分别介绍分布式事务的各种实现方式.
如果业务场景需要强一致性, 那么尽量避免将它们放在不同服务中, 也就是尽量使用本地事务, 避免使用强一致性的分布式事务.
如果业务场景能够接受最终一致性, 那么最好是使用基于消息的最终一致性的方案(异步确保型)来解决.
如果业务场景需要强一致性, 并且只能够进行分布式服务部署, 那么最好是使用TCC方案而不是2PC方案来解决.
注意: 以下每种方案都有不同的适用场合, 需要根据实际业务场景来选择.
两阶段提交(2PC)
两阶段提交(Two Phase Commit, 2PC), 具有强一致性, 是CP系统的一种典型实现.
两阶段提交, 常见的标准是XA, JTA等. 例如Oracle的数据库支持XA.
下图是两阶段提交的示意图:
图的上半是两阶段提交成功的演示, 下半是两阶段提交失败的演示. 关于两阶段提交网上有很多经典的讲解, 这里就不细说了
缺点
两阶段提交中的第二阶段, 协调者需要等待所有参与者发出yes请求, 或者一个参与者发出no请求后, 才能执行提交或者中断操作. 这会造成长时间同时锁住多个资源, 造成性能瓶颈, 如果参与者有一个耗时长的操作, 性能损耗会更明显.
实现复杂, 不利于系统的扩展, 不推荐.
TCC (Try-Confirm-Cancle)
TCC, 是基于补偿型事务的AP系统的一种实现, 具有最终一致性.
下面以客户购买商品时的付款操作为例进行讲解:
Try:
完成所有的业务检查(一致性),预留必须业务资源(准隔离性);
体现在本例中, 就是确认客户账户余额足够支付(一致性), 锁住客户账户, 商户账户(准隔离性).
Confirm:
使用Try阶段预留的业务资源执行业务(业务操作必须是幂等的), 如果执行出现异常, 要进行重试.
在这里就是执行客户账户扣款, 商户账户入账操作.
Cancle:
释放Try阶段预留的业务资源, 在这里就是释放客户账户和商户账户的锁;
如果任一子业务在Confirm阶段有操作无法执行成功, 会造成对业务活动管理器的响应超时, 此时要对其他业务执行补偿性事务. 如果补偿操作执行也出现异常, 必须进行重试, 若实在无法执行成功, 则事务管理器必须能够感知到失败的操作, 进行log(用于事后人工进行补偿性事务操作或者交由中间件接管在之后进行补偿性事务操作).
优点
对比与前面提到的两阶段提交法, 有两大优势:
TCC能够对分布式事务中的各个资源进行分别锁定, 分别提交与释放, 例如, 假设有AB两个操作, 假设A操作耗时短, 那么A就能较快的完成自身的try-confirm-cancel流程, 释放资源. 无需等待B操作. 如果事后出现问题, 追加执行补偿性事务即可.
TCC是绑定在各个子业务上的(除了cancle中的全局回滚操作), 也就是各服务之间可以在一定程度上”异步并行”执行.
注意事项
事务管理器(协调器)这个节点必须以带同步复制语义的高可用集群(HAC)方式部署.
事务管理器(协调器)还需要使用多数派算法来避免集群发生脑裂问题.
适用场景
严格一致性
执行时间短
实时性要求高
举例: 红包, 收付款业务.
异步确保型
通过将一系列同步的事务操作变为基于消息执行的异步操作, 避免了分布式事务中的同步阻塞操作的影响.
这个方案真正实现了两个服务的解耦, 解耦的关键就是异步消息和补偿性事务.
这里以一个例子作为讲解:
执行步骤如下:
MQ发送方发送远程事务消息到MQ Server;
MQ Server给予响应, 表明事务消息已成功到达MQ Server.
MQ发送方Commit本地事务.
若本地事务Commit成功, 则通知MQ Server允许对应事务消息被消费; 若本地事务失败, 则通知MQ Server对应事务消息应被丢弃.
若MQ发送方超时未对MQ Server作出本地事务执行状态的反馈, 那么需要MQ Servfer向MQ发送方主动回查事务状态, 以决定事务消息是否能被消费.
当得知本地事务执行成功时, MQ Server允许MQ订阅方消费本条事务消息.
需要额外说明的一点, 就是事务消息投递到MQ订阅方后, 并不一定能够成功执行. 需要MQ订阅方主动给予消费反馈(ack)
如果MQ订阅方执行远程事务成功, 则给予消费成功的ack, 那么MQ Server可以安全将事务消息移除;
如果执行失败, MQ Server需要对消息重新投递, 直至消费成功.
注意事项
消息中间件在系统中扮演一个重要的角色, 所有的事务消息都需要通过它来传达, 所以消息中间件也需要支持 HAC 来确保事务消息不丢失.
根据业务逻辑的具体实现不同,还可能需要对消息中间件增加消息不重复, 不乱序等其它要求.
适用场景
执行周期较长
实时性要求不高
例如:
跨行转账/汇款业务(两个服务分别在不同的银行中)
退货/退款业务
财务, 账单统计业务(先发送到消息中间件, 然后进行批量记账)
最大努力通知型
这是分布式事务中要求最低的一种, 也可以通过消息中间件实现, 与前面异步确保型操作不同的一点是, 在消息由MQ Server投递到消费者之后, 允许在达到最大重试次数之后正常结束事务.
适用场景
交易结果消息的通知等.
小结
不管是同步事务中的事务管理器(协调者), 还是异步事务中使用的消息中间件,若要达到一致性保证,都需要使用带有同步复制语义的 HAC 提供的高可用和高可靠特性,这些都是以性能为代价的,无疑成为了SOA 架构中的典型性能瓶颈之一.
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
云平台实践中的微服务设计原则
以下设计原则是在云平台架构实践(参考这里)中的一些经验总结,不一定适合所有微服务架构的体系。 业务原则 单一责任原则:对于一个微服务而言,具有有限的业务范围,可以帮助我们满足服务开发和交付的敏捷性; 适当的边界:关注微服务的范围,而不是一味的把服务做小。一个服务的大小应该等于满足某个特定业务能力所需要的大小; 业务分层: 先把业务分层,形成单向依赖,避免微服务之间的网状依赖关系; 颗粒度递增:初期先把业务划分到尽可能细,然后依据其它原则合并到适当颗粒度; 非唯一依赖:至少被2个以上其它微服务依赖,才有必要独立成一个微服务。 技术原则 部署独立性:能独立于其它微服务部署,一个微服务故障不影响其它微服务; 动态扩展:每个微服务都可以动态的进行x轴和z轴的扩展,并适应云环境下的自动化部署;( 参考这里 ) 领域和应用分层: 提供数据基本操作能力的领域服务层和执行业务逻辑的应用服务层解耦; 避免产生频繁的跨库查询; 避免产生频繁的分布式事务。 治理原则 根据业务和技术分层的情况,对微服务分组治理; 各个分组之间通过API网关集成; 通过API网关实现级轻量级消息路由,鉴权; 运行时管理,如性能...
- 下一篇
按业务领域分解模式划分微服务
场景 使用微服务架构开发一个大型复杂的应用程序,我们需要将应用程序细致,合理地分解为一组松散耦合的微服务。微服务架构的目标是通过实现持续交付/部署来加速软件开发。 目标 架构必须稳定; 服务必须高内聚 - 服务应该实现一小组强相关的功能; 服务必须符合开闭原则 - 将一同变更的内容打包在一起,以确保每个更改仅影响一个服务; 服务必须松耦合 - 每个服务都可以在不影响客户端的情况下更改实现; 服务应该是可测试的; 每项服务都小到足以由“两个披萨”团队开发,即一个6-10人的团队; 负责一个或多个服务的每个团队必须是自治的 - 团队能够在与其他团队尽量少的协作下,来开发和部署他们的服务。 方法 通过领域驱动设计(DDD),设计与 子域 相对应的服务。DDD通过分析问题空间和业务逻辑,将应用程序定义为域。域由多个子域组成。每个子域对应于业务的不同部分。 子域可分为以下几类: 核心类 - 业务的关键差异化因素和应用程序中最有价值的部分; 支持类 - 与业务有关,但与差异化无关;这些可以在内部实施或外包; 通用类 - 与业务无关,理想情况下可以使用现成的软件实现。 例子 一个在线商店的子域包括:...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Hadoop3单机部署,实现最简伪集群
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS8安装Docker,最新的服务器搭配容器使用
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS8编译安装MySQL8.0.19
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2更换Tomcat为Jetty,小型站点的福音