Springcloud分布式事务_分布式事务解决方案框架

Springcloud分布式事务_分布式事务解决方案框架publicAjaxMessageEntityuserWithdraw(@RequestBodyTPayInfotPayInfo,HttpServletRequestrequest){if(参数校验){//参数校验没有通过,直接返回参数校验错误}if(通过redis做并发控制,使同时提现人数不能…

大家好,又见面了,我是你们的朋友全栈君。

参考资料:

  1. 每特教育四期分布式事务相关课程
  2. 网上的博客部分内容收集整理

分布式事务产生的背景

传统项目中的因为有且只有一个数据库,而本地数据库是自带事物管理的,所有传统项目中是不用考虑分布式事务的。

在微服务环境下,因为会根据不同的业务拆分成不同的服务,比如会员服务、订单服务、商品服务等,让专业的人做专业的事,每个服务都有自己独立的数据库,并且是独立运行,互不影响的。

服务与服务之间采用RPC远程调用技术进行调用,但是每个服务都有自己的独立的数据源,也就是自己独立的本地事物。两个服务相互通讯的时候,两个本地事物互不影响,从而出现分布式事物。

分布式事务简述

出现的场景

  1. 传统多数据项目中
  2. 分布式项目

解决分布式事务的基本思想Base与CAP理论

CAP(帽子原理)

一个分布式系统不可能同时满足 一致性(C:Consistency)、可用性(A:Availability)和分区容错性(P:Partition tolerance)这三个基本需求,最多只能同时满足其中两项。

一致性
在分布式系统中的所有的数据备份,在同一时刻具有同样的值,所有节点在同一时刻读取到的数据都是最新的数据副本。

可用性
好的响应性能。完全的可用性是指在任何故障类型下,服务都会在有限的时间内处理完成并进行响应。

分区容错性
分区容错性约束了一个分布式系统具有如下特性:分布式系统在遇到任何网络分区故障的时候,仍然需要能够保证对外提供满足一致性和可用性的服务,除非是整个网络环境都发生了故障。

网络分区是指在分布式系统中,不同的节点分布在不同的子网络(机房或异地网络)中,由于一些特殊的原因导致这些子网络出现网络不连通的状况,但各个子网络的内部网络是正常的,从而导致整个系统的网络环境被切分成了若干个孤立的区域。 需要注意的是,组成一个分布式系统的每个节点的加入与退出都可以看作是一个特殊的网络分区。

取舍
对于分布式数据系统,分区容错性是基本要求,否则就失去了价值。因此设计分布式数据系统,就是在可用性和一致性之间取得一个平衡。对于大多数的web应用,其实并不需要强一致性,因此牺牲一致性而换取高可用性,是目前多数分布式数据库产品的方向。

当然,牺牲一致性不是完全不管数据的一致性,否则数据是混乱的,那么系统可用性再高分布式再好也没有了价值。牺牲一致性,只是不再要求关系型数据库中的强一致性,而是只要系统能达到最终一致性即可,考虑到用户体验,这个最终一致性的时间窗口,要尽可能的对用户透明,也就是需要保障“用户感知到的一致性”。通常是通过数据的多份异步复制来实现系统的高可用和数据的最终一致性的,“用户感知到的一致性”的时间窗口取决于数据复制到一致状态的时间。

BASE理论

BASE理论是指,Basically Available(基本可用)、Soft/State(软状态/柔性事物)、Eventual Consistency(最终一致性)。是基于CAP定理演化而来的,是对CAP中的一致性和可用性权衡的结果。核心思想:即使无法做到强一致性,但每个业务根据自身的特点,采用适当的方式来使系统达到最终一致性。

  1. 基本可用:指分布式系统出现故障的时候,允许损失部分可用性,保证核心可用。但不等价于不可用。比如:搜索引擎0.5秒返回查询结果,但由于故障,2秒响应查询结果;网页访问量过大时,部分用户提供服务降级等。
  2. 软状态:软状态是指运行系统存在中间状态,并且该中间状态不会影响系统整体可用性。即允许系统在不同节点间副本同步的时候存在延迟。
  3. 最终一致性:系统中的所有数据副本经过一定的时间后,最终能够达到一致的状态,不需要实时保证系统数据的强一致性。最终一致性是弱一致性的一种特殊情况。BASE理论面向的是大型高可用可扩展的分布式系统,通过牺牲强一致性来获得可用性。

ACID是传统数据库常用的概念设计,追求强一致性模型。

柔性事务与刚性事务的区别

柔性事务满足BASE理论(基本可用,最终一致)

刚性事务满足ACID理论

柔性事务分为:

  1. 两阶段提交
  2. 补偿型
  3. 异步确保型
  4. 最大努力通知型

由于支付宝整个架构是SOA架构,因此传统单机环境下数据库的ACID事物满足了分布式环境下的业务需要,以上几种事务类似就是针对分布式环境下业务需要设定的。

理解解决分布式事务核心思想软状态与最终一致性思想

软状态
软状态指允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时

最终一致性
最终一致性强调的是所有的数据副本,在经过一段时间的同步之后,最终都能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。

分布式事物常见解决方案

两阶段提交协议

在这里插入图片描述

第一阶段
准备阶段:协调者向参与者发起指令,参与者评估自己的状态,如果参与者评估参与指令可以完成,则会写redo或者undo日志,然后锁定资源,执行操作,但并不提交

第二阶段
如果每个参与者明确返回准备成功,则协调者向参与者发送提交指令,参与者释放锁定的资源,如果任何一个参与者明确返回准备失败,则协调者会发送终止指令,参与者取消已经变更的事务,释放锁定的资源。

两阶段提交方案应用非常广泛,几乎所有商业OLTP数据库都支持XA协议。但是两阶段提交方案锁定资源时间长,对性能影响很大,基本不适合解决微服务事务问题。

缺点:如果协调者宕机,参与者没有协调者指挥,则会一直阻塞。

三阶段提交协议

三阶段提交协议是两阶段提交协议的改进版。它通过超时机制解决了阻塞问题,并且把两阶段增加为三个阶段。

询问阶段:协调者问参与者是否可以完成指令,协调者只需要回答是或者不是,而不需要做真正的操作,这个阶段超时导致终止。

准备阶段:如果在询问阶段所有的参与者都返回可以执行操作,协调者向参与者发送预执行请求,然后参与者写redo和undo日志,执行操作,但是不提交操作;如果询问阶段任何参与者返回不能执行操作的结果,则协调者向参与者发送中止请求,这里的逻辑与两阶段提交协议的准备阶段是相似的,这个阶段超时导致成功。

提交阶段:如果每个参与者在准备阶段返回准备成功,也就是预留资源和执行操作成功,协调者向参与者发起提交指令,参与者提交资源变更的事务,释放锁定的资源;如果任何一个参与者返回准备失败,也就是预留资源或者执行操作失败,协调者向参与者发起中止指令,参与者取消已经变更的事务,执行undo日志,释放锁定的资源,这里的逻辑与两阶段提交协议的提交阶段一致。

传统模式使用Jta+Atomikos

传统项目中,比如项目中使用到了多数据源的时候,大多数采用jta+Atomikos解决分布式事务问题,jta+Atomikos底层是基于XA协议的两阶段提交方案。

XA协议:XA事务的基础是两阶段提交协议。需要有一个事务协调者来保证所有的事务参与者都完成了准备工作(第一阶段)。如果协调者收到所有参与者都准备好的消息,就会通知所有的事务都可以提交了(第二阶段)。Mysql在这个XA事务中扮演的是参与者的角色,而不是协调者(事务管理器)

JTA:JTA(java Transation API)是JavaEE 13个开发规范之一。java事务api,运行应用程序执行分布式事务处理 —— 在两个或者多个网络计算机资源上访问并且更新数据。JDBC驱动程序的JTA支持极大地增强了数据访问能力。事务最简单最直接的目的就是保证数据的有效性,数据的一致性。

Atomikos:Atomikos TransactionEssentials是一个为java平台提供增值服务的并且开源类事务管理器。

2pc与3pc实现的区别

增加了一个询问阶段,询问阶段可以确保尽可能早的发现无法执行操作而需要中止的行为,但是它并不能发现所有的这种行为,只会减少这种情况的发生在准备阶段以后,协调者和参与者执行的任务中都增加了超时,一旦超时,协调者和参与者都继续提交事务,默认为成功,这也是根据概率统计上超时后默认成功的正确性最大。

三阶段提交协议与两阶段提交协议相比,具有如上的优点,但是一旦发生超时,系统仍然会发生不一致,只不过这种情况很少见罢了,好处就是至少不会阻塞和永久锁定资源。

使用阿里巴巴TCC补偿框架

使用可靠消息模式

RabbitMQ死信队列

死信队列介绍

  • 死信队列:DLX,dead-letter-exchange
  • 利用DLX,当消息在一个队列中变成死信 (dead message) 之后,它能被重新publish到另一个Exchange,这个Exchange就是DLX

消息变成死信有以下几种情况

  • 消息被拒绝(basic.reject / basic.nack),并且requeue = false
  • 消息TTL过期
  • 队列达到最大长度(队列满了,无法再添加数据到mq中)

应用场景分析
在定义业务队列的时候,可以考虑指定一个死信交换机,并绑定一个死信队列,当消息变成死信时,该消息就会被发送到该死信队列上,这样就方便我们查看消息失败的原因了。

死信处理过程

  • DLX也是一个正常的Exchange,和一般的Exchange没有区别,它能在任何的队列上被指定,实际上就是设置某个队列的属性。

  • 当这个队列中有死信时,RabbitMQ就会自动的将这个消息重新发布到设置的Exchange上去,进而被路由到另一个队列。

  • 可以监听这个队列中的消息做相应的处理。

      注意:之前已经创建好的没有绑定死信队列和死信交换机的队列,不能够更改绑定死信队列和死信交换机。
    

使用LCN框架解决分布式事物

LCN框架简单介绍

“LCN并不生产事务,LCN只是本地事务的搬运工”
兼容dubbo、springcloud、motan框架,支持各种关系型数据库
LCN官方文档地址:http://www.txlcn.org/zh-cn/index.html

LCN框架原理简介

官方文档原理介绍:http://www.txlcn.org/zh-cn/docs/principle/control.html

解决方案
在一个分布式系统下存在多个模块协调来完成一次业务。那么就存在一次业务事务下可能横跨多种数据源节点的可能。TX-LCN将可以解决这样的问题。

例如存在服务模块A 、B、 C。A模块是mysql作为数据源的服务,B模块是基于redis作为数据源的服务,C模块是基于mongo作为数据源的服务。若需要解决他们的事务一致性就需要针对不同的节点采用不同的方案,并且统一协调完成分布式事务的处理。

在这里插入图片描述

方案:若采用TX-LCN分布式事务框架,则可以将A模块采用LCN模式、B/C采用TCC模式就能完美解决。

TX-LCN由两大模块组成, TxClient、TxManager,TxClient作为模块的依赖框架,提供TX-LCN的标准支持,TxManager作为分布式事务的控制放。事务发起方或者参与反都由TxClient端来控制。

原理图:
在这里插入图片描述
核心步骤
创建事务组
是指在事务发起方开始执行业务代码之前先调用TxManager创建事务组对象,然后拿到事务标示GroupId的过程。

加入事务组
添加事务组是指参与方在执行完业务方法以后,将该模块的事务信息通知给TxManager的操作。

通知事务组
是指在发起方执行完业务代码以后,将发起方执行结果状态通知给TxManager,TxManager将根据事务最终状态和事务组的信息来通知相应的参与模块提交或回滚事务,并返回结果给事务发起方。

订单库存示例讲解:
在这里插入图片描述

springcloud使用步骤

  1. 环境准备
    安装TM需要依赖的中间件: JRE1.8+, Mysql5.6+, Redis3.2+
    如果需要手动编译源码, 还需要Git, Maven, JDK1.8+

  2. 初始化脚本
    创建MySQL数据库, 名称为: tx-manager

CREATE TABLE `t_tx_exception`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `group_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `unit_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `mod_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `transaction_state` tinyint(4) NULL DEFAULT NULL,
  `registrar` tinyint(4) NULL DEFAULT NULL,
  `remark` varchar(4096) NULL DEFAULT  NULL,
  `ex_state` tinyint(4) NULL DEFAULT NULL COMMENT '0 未解决 1已解决',
  `create_time` datetime(0) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
  1. TM下载与配置
  • 从历史版本TM下载找到5.0.2.RELEASE的TM, 下载.
  • 修改配置信息
spring.application.name=tx-manager
server.port=7970

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/tx-manager?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root

mybatis.configuration.map-underscore-to-camel-case=true
mybatis.configuration.use-generated-keys=true

#tx-lcn.logger.enabled=true
# TxManager Host Ip
#tx-lcn.manager.host=127.0.0.1
# TxClient连接请求端口
#tx-lcn.manager.port=8070
# 心跳检测时间(ms)
#tx-lcn.manager.heart-time=15000
# 分布式事务执行总时间
#tx-lcn.manager.dtx-time=30000
#参数延迟删除时间单位ms
#tx-lcn.message.netty.attr-delay-time=10000
#tx-lcn.manager.concurrent-level=128
# 开启日志
#tx-lcn.logger.enabled=true
#logging.level.com.codingapi=debug
#redis 主机
#spring.redis.host=127.0.0.1
#redis 端口
#spring.redis.port=6379
#redis 密码
#spring.redis.password=

更多TM配置

  1. TC引入pom依赖
 		<dependency>
            <groupId>com.codingapi.txlcn</groupId>
            <artifactId>txlcn-tc</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>com.codingapi.txlcn</groupId>
            <artifactId>txlcn-txmsg-netty</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
  1. TC开启分布式事务注解
@SpringBootApplication
@EnableDistributedTransaction
public class DemoAApplication { 
   

    public static void main(String[] args) { 
   
        SpringApplication.run(DemoDubboClientApplication.class, args);
    }

}
  1. TC微服务A业务方法配置
@Service
public class ServiceA { 
   
    
    @Autowired
    private ValueDao valueDao; //本地db操作
    
    @Autowired
    private ServiceB serviceB;//远程B模块业务
    
    @LcnTransaction //分布式事务注解
    @Transactional //本地事务注解
    public String execute(String value) throws BusinessException { 
   
        // step1. call remote service B
        String result = serviceB.rpc(value);  // (1)
        // step2. local store operate. DTX commit if save success, rollback if not.
        valueDao.save(value);  // (2)
        valueDao.saveBackup(value);  // (3)
        return result + " > " + "ok-A";
    }
}
  1. TC微服务B业务方法配置
@Service
public class ServiceB { 
   
    
    @Autowired
    private ValueDao valueDao; //本地db操作
    
    @LcnTransaction //分布式事务注解
    @Transactional  //本地事务注解
    public String rpc(String value) throws BusinessException { 
   
        valueDao.save(value);  // (4)
        valueDao.saveBackup(value);  // (5)
        return "ok-B";
    }
}

TC配置信息说明

# 默认之配置为TM的本机默认端口
tx-lcn.client.manager-address=127.0.0.1:8070 

官方demo运行测试

代码下载地址:https://github.com/codingapi/txlcn-demo

  1. 环境准备
  • 创建测试数据表:
create table `t_demo` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `kid` varchar(45) DEFAULT NULL,
  `group_id` varchar(64) DEFAULT NULL,
  `demo_field` varchar(255) DEFAULT NULL,
  `app_name` varchar(128) DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

注意:每个服务都有自己独立的数据库,都需要初始化表,协调者也单独一个数据库(协调者的表可以放到其他服务的数据库里面去,但是最好独立出来)
在这里插入图片描述

  • 启动redis,协调者用到了redis
    在这里插入图片描述
  • 启动console,服务使用的注册中心是console
    在这里插入图片描述
    console管理界面如下:
    在这里插入图片描述
  1. 修改协调者数据源和端口,并启动,结果如下:
    在这里插入图片描述

其中,协调者会开启监控两个端口,一个8069是管理界的端口,一个是8169,是其他参与者向协调者建立连接时的端口。

协调者管理界面如下(http://localhost:8069):
在这里插入图片描述
协调者管理界面,登录密码默认是codingapi,可以在配置文件中配置修改,配置如下:

# TM后台登陆密码,默认值为codingapi
tx-lcn.manager.admin-key=codingapi

协调者内部管理界面如下:
在这里插入图片描述
可以通过协调者控制台查看配置信息、异常记录、系统日志等,可以对异常数据进行补偿请求。

  1. 修改服务的数据源地址和协调者地址,如下:
    在这里插入图片描述

  2. 分别启动协调者、服务a、服务b、服务c,如下图:
    在这里插入图片描述

  3. 测试

正常提交事务
访问 发起方提供的Rest接口 /txlcn?value=the-value。发现事务全部提交

页面结果:
在这里插入图片描述
数据库结果:
在这里插入图片描述

回滚事务
访问 发起方提供的Rest接口 /txlcn?value=the-value&ex=throw。发现发起方由本地事务回滚,而参与方ServiceB、ServiceC,由于TX-LCN的协调,数据也回滚了。
error_result

测试的server代码

public String execute(String value, String exFlag) { 
   
        // step1. call remote ServiceD
// String dResp = serviceBClient.rpc(value);

        String dResp = restTemplate.getForObject("http://127.0.0.1:12002/rpc?value=" + value, String.class);

        // step2. call remote ServiceE
        String eResp = serviceCClient.rpc(value);

        // step3. execute local transaction
        Demo demo = new Demo();
        demo.setGroupId(TracingContext.tracing().groupId());
        demo.setDemoField(value);
        demo.setCreateTime(new Date());
        demo.setAppName(Transactions.getApplicationId());
        demoMapper.save(demo);

        // 置异常标志,DTX 回滚
        if (Objects.nonNull(exFlag)) { 
   
            throw new IllegalStateException("by exFlag");
        }

        return dResp + " > " + eResp + " > " + "ok-service-a";
    }

页面结果:
在这里插入图片描述

如下图,执行了回滚后三张表都没有新增数据:
在这里插入图片描述

TX-LCN 5.0.0 性能测试报告

官方地址:http://www.txlcn.org/zh-cn/docs/test.html
在这里插入图片描述

阿里GTS框架解决分布式事物

官方地址:http://seata.io/zh-cn/

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/125027.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • docker 离线安装_docker 离线安装

    docker 离线安装_docker 离线安装docker离线安装方法下载地址:https://download.docker.com/linux/static/stable/x86_64/参考文档:https://docs.docker.com/engine/install/binaries/机房设备无法访问互联网原因,需要进行离线安装K8S生态周报|Docker和containerd全版本漏洞公布,近期在Docker中发现了一个影响所有版本的安全漏洞CVE-2022-24769,该漏洞已经在Docker最新的版本v20

  • windows平台下载android源码

    最近在看《android内核剖析》,很多细节不具体看代码很难理解,记住了印象也不深,感觉还是跟着源码走一遍好些,回来下载android源码,遇到不少问题,终于开始下载了,整理下流程,鉴于网上很多教程时间久了都会失效,本文截止14年4月18日亲测有效。需要工具如下:下载msysgit,安装     官方下载:http://code.google.com/p/msysgit/downloads

  • python开发oa办公系统_python web开发框架

    python开发oa办公系统_python web开发框架https://www.odoo.com/zh_CN

    2022年10月28日
  • 孙鑫java基础视频教程_孙鑫老师JAVA无难事视频教程 最适合java入门学习打基础的课程 附源码讲义 12课…

    孙鑫java基础视频教程_孙鑫老师JAVA无难事视频教程 最适合java入门学习打基础的课程 附源码讲义 12课…课程介绍由孙鑫老师亲自授课录制。内容涵盖面广,从入门到精通,授课通俗易懂,分析问题独到精辟,学员通过本套光盘的学习,能够快速掌握Java编程语言,成为Java高手。由孙鑫老师亲自授课录制。内容涵盖面广,从入门到精通,授课通俗易懂,分析问题独到精辟,学员通过本套光盘的学习,能够快速掌握Java编程语言,成为Java高手。从自学者很难弄明白的Java环境配置开始讲解,一步步引领你成为Java高手。2、…

  • 非线程安全对象�

    非线程安全对象�

  • java接口和抽象类区别面试题_接口是一个特殊抽象类

    java接口和抽象类区别面试题_接口是一个特殊抽象类原文:http://blog.csdn.net/sunboard/article/details/38318231.概述一个软件设计的好坏,我想很大程度上取决于它的整体架构,而这个整体架构其实就是你对整个宏观商业业务的抽象框架,当代表业务逻辑的高层抽象层结构合理时,你底层的具体实现需要考虑的就仅仅是一些算法和一些具体的业务实现了。当你需要再开发另一个相近的项目时,你以前的抽象层说不定还可

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号