GTS教程-原理、使用

产品概述

全局事务服务(Global Transaction Service,简称 GTS)是一款高性能、高可靠、接入简单的分布式事务中间件,用于解决分布式环境下的数据一致性问题。

一个完整的业务往往需要调用多个子业务或服务,随着业务的不断增多,涉及的服务及数据也越来越多,越来越复杂。传统的系统难以支撑,出现了应用和数据库等的分布式系统。分布式系统又带来了数据一致性的问题,从而产生了分布式事务。

分布式事务是指事务发起者、资源管理器、事务协调者及资源分别位于不同的分布式系统的不同节点之上。

GTS 的架构如下图所示:

白俊遥博客

  • GTS 服务端:即事务协调器。负责分布式事务的推进,管理事务生命周期。

  • GTS 客户端:即事务发起者。通过事务协调器,开启、提交、回滚分布式事务。同时还包含部分资源管理器组件,负责管理和控制资源,与 GTS 服务器进行交互。

  • 服务框架:GTS 可以服务框架配合使用,管理服务框架中的事务。服务框架可以集成资源管理器组件,管理和控制资源。

  • 资源: 包括 RDS、DRDS、MySQL 以及其它数据库事务,还包括 MQ 消息事务。


说明:服务端的 GTS 相关配置通过内部组件进行下发或同步。

在单机数据库下很容易维持事务的 ACID(Atomicity、Consistency、Isolation、Durability)特性,但在分布式系统中并不容易,GTS 可以保证分布式系统中的分布式事务的 ACID 特性。

GTS 支持 DRDS、RDS、Oracle、MySQL、PostgreSQL、OceanBase 和 Petadata 等多种数据源,可以配合 EDAS、Dubbo 和 Spring Cloud 等微服务框架使用, 兼容 MQ 实现事务消息。通过各种组合,可以轻松实现分布式数据库事务、多库事务、消息事务、服务链路级事务等多种业务需求

应用场景

GTS 可应用在多个领域,包括但不限于金融支付、电信、电子商务、快递物流、广告营销、社交、即时通信、手游、视频、物联网、车联网等,提供微服务架构中分布式事务的一站式解决方案。典型的应用场景如下:

解决跨服务事务问题

现代 IT 应用中,服务化的模式得到了广泛使用。比如大型电商应用中,为了系统解耦,常常将整个应用划分为多个系统,如商品系统、商家系统、用户系统、账务系统、物流系统等,各个系统会提供各自的服务。

商品加入购物车的操作,会调用商品系统服务来减掉库存,调用购物车系统服务增加记录,调用结算系统服务变更待结算金额等操作。使用 GTS 可以将调用这些服务的操作加入到一个全局事务中,让他们要么同时成功,要么同时失败,保证了各个系统的数据一致性。

白俊遥博客

GTS 配置 MQ 解决事务消息问题

有些应用系统在使用数据库保证系统内数据一致的同时, 也会使用消息队列(MQ) 和其他系统间进行消息传递, 保证不同系统间的数据一致。

一个典型的场景,A 系统成功将本地数据 1 保存到数据库后, 通过 MQ 向 B 系统发送一条通知消息, B 系统收到消息后保存与数据 1 关联的数据 2, A、B 两个系统保持数据一致。但是当 A 系统成功保存数据但是未能成功调用消息系统发送通知时, 会导致 A 系统中有数据 1 而 B 系统中没有相应的数据 2, 即 A、B 两个系统出现数据不一致, 造成系统故障。

对于上述类似场景,GTS 能够将 A 系统向数据库写入数据 1 的本地事务, 和通过 MQ 向 B 系统发送通知放到一个全局事务中, 保证数据写入则消息一定发出,数据未写入则消息一定不会发出。


GTS对应用的侵入性非常低,使用也很简单。下面以订单存储应用为例说明。订单业务应用通过调用订单服务和库存服务完成订单业务,服务开发框架为Dubbo。

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);
}

dubbo提供者

public int updateStock(OrderDO orderDO) {

//获取全局事务ID,并绑定到上下文

String xid = RpcContext.getContext().getAttachment("xid");

TxcContext.bind(xid,null);

//执行自己的业务逻辑

int ret = jdbcTemplate.update("update stock set amount = amount - ? where product_id = ?",new Object[]{orderDO.getNumber(), orderDO.getProductId()});

TxcContext.unbind();

return ret;

}


程序员之家
请先登录后发表评论
  • 最新评论
  • 总共0条评论