微服务倡导将复杂的单体应用拆分为若干个功能简单、松耦合的服务,这样可以降低开发难度、增强扩展性、便于敏捷开发。概念一经提出迅速火遍全球。当前 Hailo 有160个不同服务构成,NetFlix 有大约600个服务。国内方面,阿里巴巴、腾讯、360、京东、58同城等很多互联网公司都进行了微服务化实践。分布式事务问题被著名架构师 Chris Richardson 称为微服务的三大难题之一,而且当前 Dubbo、SpringCloud 等微服务框架均为提供可行的分布式事务解决方案。
本场 Chat 将为大家解读 XA、TCC、消息一致性方案、GTS 四种微服务架构下分布式事务的解决方案。并重点为大家解答阿里巴巴提出的全新一代分布式事务解决方案 GTS。主要包括:
微服务倡导将复杂的单体应用拆分为若干个功能简单、松耦合的服务,这样可以降低开发难度、增强扩展性、便于敏捷开发。当前被越来越多的开发者推崇,很多互联网行业巨头、开源社区等都开始了微服务的讨论和实践。Hailo 有 160 个不同服务构成,NetFlix 有大约 600 个服务。国内方面,阿里巴巴、腾讯、360、京东、58 同城等很多互联网公司都进行了微服务化实践。当前微服务的开发框架也非常多,比较著名的有 Dubbo、SpringCloud、thrift 、grpc 等。
虽然微服务现在如火如荼,但对其实践其实仍处于探索阶段。很多中小型互联网公司,鉴于经验、技术实力等问题,微服务落地比较困难。如著名架构师 Chris Richardson 所言,目前存在的主要困难有如下几方面:
随着 RPC 框架的成熟,第一个问题已经逐渐得到解决。例如 dubbo 可以支持多种通讯协议,springcloud 可以非常好的支持 restful 调用。对于第三个问题,随着 docker、devops 技术的发展以及各公有云 paas 平台自动化运维工具的推出,微服务的测试、部署与运维会变得越来越容易。
而对于第二个问题,现在还没有通用方案很好的解决微服务产生的事务问题。分布式事务已经成为微服务落地最大的阻碍,也是最具挑战性的一个技术难题。 为此,本文将深入和大家探讨微服务架构下,分布式事务的各种解决方案,并重点为大家解读阿里巴巴提出的分布式事务解决方案——GTS。该方案中提到的 GTS 是全新一代解决微服务问题的分布式事务互联网中间件。
交易中间件与数据库通过 XA 接口规范,使用两阶段提交来完成一个全局事务, XA 规范的基础是两阶段提交协议。
第一阶段是表决阶段,所有参与者都将本事务能否成功的信息反馈发给协调者;第二阶段是执行阶段,协调者根据所有参与者的反馈,通知所有参与者,步调一致地在所有分支上提交或者回滚。
两阶段提交方案应用非常广泛,几乎所有商业 OLTP 数据库都支持 XA 协议。但是两阶段提交方案锁定资源时间长,对性能影响很大,基本不适合解决微服务事务问题。
TCC 方案在电商、金融领域落地较多。TCC 方案其实是两阶段提交的一种改进。其将整个业务逻辑的每个分支显式的分成了 Try、Confirm、Cancel 三个操作。Try 部分完成业务的准备工作,confirm 部分完成业务的提交,cancel 部分完成事务的回滚。基本原理如下图所示。
事务开始时,业务应用会向事务协调器注册启动事务。之后业务应用会调用所有服务的 try 接口,完成一阶段准备。之后事务协调器会根据try接口返回情况,决定调用 confirm 接口或者 cancel 接口。如果接口调用失败,会进行重试。
TCC 方案让应用自己定义数据库操作的粒度,使得降低锁冲突、提高吞吐量成为可能。 当然 TCC 方案也有不足之处,集中表现在以下两个方面:
上述原因导致 TCC 方案大多被研发实力较强、有迫切需求的大公司所采用。微服务倡导服务的轻量化、易部署,而 TCC 方案中很多事务的处理逻辑需要应用自己编码实现,复杂且开发量大。
消息一致性方案是通过消息中间件保证上、下游应用数据操作的一致性。基本思路是将本地操作和发送消息放在一个事务中,保证本地操作和消息发送要么两者都成功或者都失败。下游应用向消息系统订阅该消息,收到消息后执行相应操作。
消息方案从本质上讲是将分布式事务转换为两个本地事务,然后依靠下游业务的重试机制达到最终一致性。基于消息的最终一致性方案对应用侵入性也很高,应用需要进行大量业务改造,成本较高。
GTS 是一款分布式事务中间件,由阿里巴巴中间件部门研发,可以为微服务架构中的分布式事务提供一站式解决方案。
更多 GTS 资料请访问创始人微博。
性能超强
GTS 通过大量创新,解决了事务 ACID 特性与高性能、高可用、低侵入不可兼得的问题。单事务分支的平均响应时间在 2ms 左右,3 台服务器组成的集群可以支撑 3 万 TPS 以上的分布式事务请求。
应用侵入性极低
GTS 对业务低侵入,业务代码最少只需要添加一行注解(@TxcTransaction)声明事务即可。业务与事务分离,将微服务从事务中解放出来,微服务关注于业务本身,不再需要考虑反向接口、幂等、回滚策略等复杂问题,极大降低了微服务开发的难度与工作量。
完整解决方案
GTS 支持多种主流的服务框架,包括 EDAS,Dubbo,Spring Cloud等。 有些情况下,应用需要调用第三方系统的接口,而第三方系统没有接入 GTS。此时需要用到 GTS 的 MT 模式。GTS 的 MT 模式可以等价于 TCC 模式,用户可以根据自身业务需求自定义每个事务阶段的具体行为。MT模式提供了更多的灵活性,可能性,以达到特殊场景下的自定义优化及特殊功能的实现。
容错能力强
GTS 解决了 XA 事务协调器单点问题,实现真正的高可用,可以保证各种异常情况下的严格数据一致。
GTS 可应用在涉及服务调用的多个领域,包括但不限于金融支付、电信、电子商务、快递物流、广告营销、社交、即时通信、手游、视频、物联网、车联网等,详细介绍可以阅读 《GTS——阿里巴巴分布式事务全新解决方案》一文。
GTS 包括客户端(GTS Client)、资源管理器(GTS RM)和事务协调器(GTS Server)三个部分。GTS Client 主要用来界定事务边界,完成事务的发起与结束。GTS RM 完成事务分支的创建、提交、回滚等操作。GTS Server 主要负责分布式事务的整体推进,事务生命周期的管理。GTS 和微服务集成的结构图如下所示,GTS Client 需要和业务应用集成部署,RM 与微服务集成部署。
GTS 目前有三种输出形式:公有云输出、公网输出、专有云输出。
这种输出形式面向阿里云用户。如果用户的业务系统已经部署到阿里云上,可以申请开通公有云 GTS。开通后业务应用即可通过 GTS 保证服务调用的一致性。这种使用场景下,业务系统和 GTS间的网络环境比较理想,达到很好性能。
这种输出形式面向于非阿里云的用户,使用更加方便、灵活,业务系统只要能连接互联网即可享受 GTS 提供的云服务(与公有云输出的差别在于客户端部署于用户本地,而不在云上)。
在正常网络环境下,以包含两个本地事务的全局事务为例,事务完成时间在 20ms 左右,50 个并发就可以轻松实现 1000TPS 以上分布式事务,对绝大多数业务来说性能是足够的。在公网环境,网络闪断很难完全避免,这种情况下 GTS 仍能保证服务调用的数据一致性。
具体使用样例使用参见 4.7 节 GTS 的工程样例。
这种形式主要面向于已建设了自己专有云平台的大用户,GTS 可以直接部署到用户的专有云上,为专有云提供分布式事务服务。目前已经有 10 多个特大型企业的专有云使用 GTS 解决分布式事务难题,性能与稳定性经过了用户的严格检测。
GTS 对应用的侵入性非常低,使用也很简单。下面以订单存储应用为例说明。订单业务应用通过调用订单服务和库存服务完成订单业务,服务开发框架为 Dubbo。
在业务函数外围使用 @TxcTransaction 注解即可开启分布式事务。Dubbo 应用通过隐藏参数将 GTS 的事务 xid 传播到服务端。
@TxcTransaction(timeout = 1000 * 10)public void Bussiness(OrderService orderService, StockService stockService, String userId) { //获取事务上下文 String xid = TxcContext.getCurrentXid(); //通过RpcContext将xid传到一个服务端 RpcContext.getContext().setAttachment("xid", xid); //执行自己的业务逻辑 int productId = new Random().nextInt(100); int productNum = new Random().nextInt(100); OrderDO orderDO = new OrderDO(userId, productId, productNum, new Timestamp(new Date().getTime())); orderService.createOrder(orderDO); //通过RpcContext将xid传到另一个服务端 RpcContext.getContext().setAttachment("xid",xid); stockService.updateStock(orderDO);}
更新库存方法:
public int updateStock(OrderDO orderDO) { //获取全局事务ID,并绑定到上下文 String xid = RpcContext.getContext().getAttachment("xid"); TxcContext.bind(xid,null); //执行自己的业务逻辑 String sql = "update stock set amount = amount - ? where product_id = ?"; int ret = jdbcTemplate.update(sql,new Object[]{orderDO.getNumber(), orderDO.getProductId()}); TxcContext.unbind(); return ret;}
GTS 目前已经在淘宝、天猫、阿里影业、淘票票、阿里妈妈、1688 等阿里各业务系统广泛使用,经受了 16 年和 17 年两年双十一海量请求的考验。某线上业务系统最高流量已达十万 TPS(每秒钟 10 万笔事务)。
GTS 在公有云和专有云输出后,已经有了 100 多个线上用户,很多用户通过 GTS 解决 SpringCloud、Dubbo、Edas 等服务框架的分布式事务问题。业务领域涉及电力、物流、ETC、烟草、金融、零售、电商、共享出行等十几个行业,得到用户的一致认可。 上图是 GTS 与 SpringCloud 集成,应用于某共享出行系统。业务共享出行场景下,通过 GTS 支撑物联网系统、订单系统、支付系统、运维系统、分析系统等系各统应用的数据一致性,保证海量订单和数千万流水的交易。
GTS 的公有云样例可参考阿里云网站。在公网环境下提供 sample-txc-simple 和 sample-txc-dubbo 两个样例工程。(点击下载)
注意
4.7.1.1 样例业务逻辑
该样例是 GTS 的入门 sample,案例的业务逻辑是从 A 账户转账给B账户,其中A和B分别位于两个 MySQL 数据库中,使用 GTS 事务保证 A 和 B 账户钱的总数始终不变。
4.7.1.2 样例搭建方法
1) 准备本地数据库环境
2) 下载样例
将 sample-txc-simple (点击下载) 文件下载到本地,样例中已经包含了 GTS 的 SDK。
3) 修改配置
4) 编译样例
5) 运行样例
4.7.2.1 样例业务逻辑
本案例模拟了用户下订单、减库存的业务逻辑。客户端(Client)通过调用订单服务(OrderService)创建订单,之后通过调用库存服务(StockService)扣库存。其中订单服务读写订单数据库,库存服务读写库存数据库。由 GTS 保证跨服务事务的一致性。
4.7.2.2 样例搭建方法
1) 准备数据库环境
安装 MySQL,创建两个数据库 db1 和 db2。在 db1 和 db2 中分别创建 txc_undo_log 表。在 db1 库中创建 orders 表,在 db2 库中创建 stock 表。
2) 下载样例
将样例文件 (点击下载) 下载到本地机器,样例中已经包含了 GTS 的 SDK。
3) 修改配置
4) 编译样例
5) 运行样例
4.7.3.1 建表 txc_undo_log
CREATE TABLE `txc_undo_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键', `gmt_create` datetime NOT NULL COMMENT '创建时间', `gmt_modified` datetime NOT NULL COMMENT '修改时间', `xid` varchar(100) NOT NULL COMMENT '全局事务ID', `branch_id` bigint(20) NOT NULL COMMENT '分支事务ID', `rollback_info` longblob NOT NULL COMMENT 'LOG', `status` int(11) NOT NULL COMMENT '状态', `server` varchar(32) NOT NULL COMMENT '分支所在DB IP', PRIMARY KEY (`id`), KEY `unionkey` (`xid`,`branch_id`)) ENGINE=InnoDB AUTO_INCREMENT=211225994 DEFAULT CHARSET=utf8 COMMENT='事务日志表';
4.7.3.2 建表 user_money_a
CREATE TABLE `user_money_a` ( `id` int(11) NOT NULL AUTO_INCREMENT, `money` int(11) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
4.7.3.3 建表 user_money_b
CREATE TABLE `user_money_b` ( `id` int(11) NOT NULL AUTO_INCREMENT, `money` int(11) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
4.7.3.4 建表 orders
CREATE TABLE `orders` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `user_id` varchar(255) NOT NULL, `product_id` int(11) NOT NULL, `number` int(11) NOT NULL, `gmt_create` timestamp NOT NULL, PRIMARY KEY (`id`)) ENGINE=MyISAM AUTO_INCREMENT=351 DEFAULT CHARSET=utf8
4.7.3.5 建表 stock
CREATE TABLE `stock` ( `product_id` int(11) NOT NULL, `price` float NOT NULL, `amount` int(11) NOT NULL, PRIMARY KEY (`product_id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8
有对分布式事务 GTS 感兴趣的朋友,欢迎加入群进行交流。
本文首发于GitChat,未经授权不得转载,转载需与GitChat联系。
阅读全文: http://gitbook.cn/gitchat/activity/5ac364ae83d40954d3e36b88
您还可以下载 CSDN 旗下精品原创内容社区 GitChat App ,阅读更多 GitChat 专享技术内容哦。