精华内容
下载资源
问答
  • 微服务分布式事务解决方案之TCC,针对支付系统环节,采用消息对列的方式
  • alibaba 微服务分布式事务组件-seata 1 事务简介 事务transtction 是访问并可能更新数据库中各种数据项的一个程序执行单元。在关系型数据库中,一个事务有一组sql 组成,事务应该具有4个属性: 原子性:一个事务是...

    alibaba 微服务分布式事务组件-seata

    1 事务简介

    事务transtction 是访问并可能更新数据库中各种数据项的一个程序执行单元。在关系型数据库中,一个事务有一组sql 组成,事务应该具有4个属性:

    • 原子性:一个事务是一个不可分割的工作单位,事务中包括诸多操作,要么都做,要么都不做;

    • 一直性:事务必须是是数据库从一个一直状态变到另一个一直性状态,事务中间状态不能被观察到;

    • 隔离性:一个事务的执行官不能被其他事务干扰,即一个事务内部的操作及使用的数据库对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰;

    • 持久性:持久性也称为永久性,值一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其他操作或故障不应该对齐有任何影响;

      任何事务机制在实现时,都应该考虑事务的ACID特性;包括,本地事务,分布式事务,及时不能都很好的满足,也要考虑支持到什么程度;

    2 本地事务

    @Tranactional

    大多数场景下,我们的应用都只需要操作单一的数据库,这种情况下事务称为本地事务(local Tranaction)。本地事务的ACID特性是数据库直接提供支持。本地事务应用架构图如下;

    在这里插入图片描述

    在JDBC 编码中,我们通多java.sql.Connection对象来开启、关闭或者提交事务,代码如下:

    Connection conn =
        conn.setAutoCommit(false);// 关闭事务自动提交
    try{
        
        conn.commit(); //提交事务
        
    }catch(Exception e){
        conn.rollback();//事务回滚
    }finally{
        conn.close();//关闭连接
        
    }
    

    3 分布式事务:

    业务模型:

    在这里插入图片描述

    4 seata

    介绍:

    seata 是一款开源的分布式解决方案;致力于提供高性能和简单易用的分布式事务服务; seata 将为用户提供AT\TCC\SAGA和XA事务模式;为用户打造一站式分布式解决方案。AT模式是阿里首推的模式,阿里云上有上永伴的GTS (global transaction service 全局事务服务)

    官网地址: http://seata.io

    seata版 1.4.0

    4.1 seata的三大角色

    • TC 事务协调者:维护全局和分支事务的状态,驱动全局事务的提交或者回滚;

    • TM 事务管理器: 定义全局事务的方位,开始全局事务、提交或回滚全局事务;

    • RM 资源管理器: 管理分支事务处理的资源,与TC 交谈,以注册分支事务和报告分支事务的状态,并驱动分支事务的提供和回滚;

      其中,TC为单独部署的server服务端,TM和RM 为嵌入到应用中的client客户端;

    在这里插入图片描述

    TM 请求TC 开启一个全局事务,tc会生成一个XID 作为该全局事务的编号。xid,会再微服务的调用链路中传播,保证多个微服务的子事务关联在一起

    RM请求TC将本地事务注册为全局事务的分支事务,通过全局事务的xid进行关联;

    TM请求TC 告诉XID对应的全局事务是进行提交还是回滚;

    TC 驱动RM们将xid对应的自己的本地事务进行提交还是回滚;

    4.2 常见分布式事务解决方案

    • seata 阿里分布式框架 at
    • 消息队列 tcc
    • saga saga
    • xa xa

    他们都有两阶段(2PC).两阶段 是指完成分布式事务,划分成两个步骤。

    实际上,这个是四种常见的分布式解决方案,分别对应着分布式事务的四种模式:AT TCC SAGA XA(全局事务、基于可靠消息、最大努力通知、TCC)

    4.3 分布式事务理论基础

    解决分布式事务,也有相应的规范和协议。分布式事务相关的协议有2PC、3PC

    由于三段提交协议3PC 非常难实现,目前市面主流的分布式事务解决方案都是2PC协议。

    4.4 两阶段提交协议

    2pc two-phase comit

    顾名思义 分为两个阶段 prepare 和commit

    seata 参与者为数据库 ,tcc中间件 一般参与者指的是应用或者服务

    基本流程图

    在这里插入图片描述

    示例:

    在这里插入图片描述

    4.5 2PC的问题

    • 同步阻塞参与者在等待协调者的指令时,其实是在等待其他参与者的响应,在此过程中,参与者是无法进行其他操作的。也就是阻塞了其运行。倘若参与者与协调者之间的网络异常导致参与者一直收不到协调者信息,那么就会导致参与者一直阻塞下去。

    • 单点在2PC中,一切请求都来自协调者,所以协调者的地址直观重要,如果协调者单机,那么就会是参与者一直阻塞并一直占用事务资源。(避免单点故障,新的节点不能处理上一个事务)

    • 数据不一致,commit事务过程中,commit请求/rollback请求,可能因为协调者宕机或协调者与参与者网络异常丢失,那么就会导致部分参与者没有收到commit/rollback请求,而其他参与者则正常收到执行了commit/rollback操作,没有收到请求的参与者则继续阻塞,这时名参与者之间的数据就不在一致了。

      当参与者commit/rollback后会向协调者发送ACK ,然而协调者不论是否收到所有参与者的ack,该事务也不会再有其他的补救措施了,协调者能做的也就是等超时后想事务发起者返回一个 ‘我不确定事务是否成功’

    • 环境可靠性依赖,协调者 prepare请求后,等待响应,然而如果有参与者宕机或协调者之间网络中断,都会导致协调者无法收到所有参与者的响应,那么在2PC中,协调者都会等待一定时间,然后超时后,会触发事务中断,在这个过程中,协调者和所有其他参与者都是出于阻塞的。这种机制对网络问题常键的实现环境来说太苛刻。

    4.6 AT模式(auto transaction)

    4.61 AT 模式是一种无侵入的分布式事务解决方案;

    阿里 seata框架实现该模式;

    在AT模式下,用户只需关注自己的业务sql,用户的业务sql 作为第一个阶段,seata框架会自动声称该无的第二阶段提交和回滚操作;

    在这里插入图片描述

    4.6.2 AT 模式如何做到对业务的无入侵
    • 第一阶段,seata会拦截"业务sql" ,首先解析sql语义,找到“业务sql”要更新的业务数据,在业务数据被更新前,将其保存为“before image”,然后执行“业务sql”更新业务数据,在业务数据被更新之后,在将其保存成“after image”,最后生成杭锁。以上操作全部在一个数据库事务内完成,这样保证了一阶段操作原子性。

    在这里插入图片描述

    • 第二阶段提交

      第二阶段如果是提交的话,因为“业务sql”在一极端已经提交至数据库,所以seata框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可;

    在这里插入图片描述

    • 二阶段回滚

      二阶段如果是回滚的话,seata 就需要回滚一阶段已经执行的“业务sql” ,还原业务数据。回滚方式便是用before image 还原业务数据;但在还原之前首先要校验脏读,对比“数据库当前业务数据”和“after image”,如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就会说明有脏写,出现脏写就要转人工处理。

    在这里插入图片描述

    4.7 TCC 模式

    1、侵入性比较强,并且得自己实相关事务控制逻辑;

    2、在整个过程中没有锁的概念,性能更好;

    3、tcc 开源框架:beyeTcc 、tcc-transaction 、himly

    tcc模式需要用户根据自己的业务场景实现,try confirm 和cancel 三个操作。事务发起方在一阶段执行try方式,在二阶段执行提交confirm方法,二阶段回滚执行cancel 方法;

    在这里插入图片描述

    示例:
    在这里插入图片描述

    4.8 MQ 模式

    称为可靠消息最终一致性解决方案(rocketMq 有事务消息,所以解决起来比较方便)

    在这里插入图片描述

    4.9 seata at模式原理

    第一阶段:

    在这里插入图片描述

    第二阶段:

    分布式事务操作成功,TC通知rm异步删除undolog

    在这里插入图片描述

    分布式事务操作失败,TM 向TC 发送回滚请求,RM收到协调器TC发来的回滚请求,通过XID和branch ID 找到相应的回滚日志记录,通过回滚记录生成的方向更新sql并执行,以完成分支的回滚。

    在这里插入图片描述

    5 开始使用seata

    @GlobalTransational 开启全局分布式事务

    5.1 seata server 环境搭建

    server 端存储模式支持三种:

    • file 单机模式,全局事务回话信息内存中读取并持久化到本地文件root.data,性能较高(默认)
    • db,高可用模式,全局事务会化通过db共享,响应性能较差些
    • redis,seata-server1.3 及以上版本支持,性能较高,存在事务信息丢失风险,提前配置合适当前场景的redis持久化配置

    资源目录介绍:

    • client

      存放client端sql脚本(包含undo_log表),参数配置

    • config-center

      各个配置中心参数导入脚本,config.text(包含server和client,原名nacos-config.txt)为通过用参数文件

    • server

      server端数据库脚本(包含lock_table,brach_table和global_table)及各个容器配置

    5.2 搭建环境脑图

    在这里插入图片描述

    5.3 seata 高可用集群

    在这里插入图片描述

    在这里插入图片描述

    5.4 在版本对照表

    在使用springcloud alibaba 他们的组件的时候,记得使用版本对照表,要不然会出现一些莫名其妙的情况。

    在这里插入图片描述

    在这里插入图片描述

    5.5 springcloud 代码应用

    5.5.1 导入依赖
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-alibaba-seata</artifactId>
    </dependency>
    
    5.5.2 导入数据库表到微服务数据库中

    官方网站文件的位置:\script\client\at\db

    -- for AT mode you must to init this sql for you business database. the seata server not need it.
    CREATE TABLE IF NOT EXISTS `undo_log`
    (
        `branch_id`     BIGINT       NOT NULL COMMENT 'branch transaction id',
        `xid`           VARCHAR(128) NOT NULL COMMENT 'global transaction id',
        `context`       VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
        `rollback_info` LONGBLOB     NOT NULL COMMENT 'rollback info',
        `log_status`    INT(11)      NOT NULL COMMENT '0:normal status,1:defense status',
        `log_created`   DATETIME(6)  NOT NULL COMMENT 'create datetime',
        `log_modified`  DATETIME(6)  NOT NULL COMMENT 'modify datetime',
        UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
    ) ENGINE = InnoDB
      AUTO_INCREMENT = 1
      DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';
    
    
    5.5.3 配置事务分组

    这里对应service.vgroupMapping.my_test_tx_group=defaultmy_test_tx_group

    alibaba:
    	seata:
    		tx-service-group: txgroup
    
    5.5.4 配置seata client 访问 seata tc
    seata:
    	registry:
    		type: nacos
    		nacos: 
    			server-addr: 127.0.0.1:8848 #seata server 所在的nacos 服务地址
    			application: seata_server  #seata server 服务名
    			username: nacos  #seata server 所在的nacos 账号
    			password: nacos #seata server 所在的nacos 密码
    			group: SEATA_GROUP #seata server 所在的nacos 分组
    	config: #配置seata的配置中心,可以读取关于seata client的一些配置
    		type: nacos
    		nacos: 
    			server-addr: 127.0.0.1:8848 #seata server 所在的nacos 服务地址
    			application: seata_server  #seata server 服务名
    			username: nacos  #seata server 所在的nacos 账号
    			password: nacos #seata server 所在的nacos 密码
    			group: SEATA_GROUP #seata server 所在的nacos 分组
    
    5.5.5 使用@GobalTranactional

    这样就完成了 整个分布式事务,seata at 模式的事务

    展开全文
  • GTS让分布式事务简单高效》中,阿里声称提出了一种破解世界性难题之分布式事务的终极解决方案,无论是可靠性、还是处理速率都领先于市面上所有的技术。但令人遗憾的是一来项目未开源,二来还必须依赖阿里云的分布式...

    一、 前言

             阿里2017云栖大会《破解世界性技术难题!GTS让分布式事务简单高效》中,阿里声称提出了一种破解世界性难题之分布式事务的终极解决方案,无论是可靠性、还是处理速率都领先于市面上所有的技术。但令人遗憾的是一来项目未开源,二来还必须依赖阿里云的分布式数据库。毕竟,吃饭的家伙可不能轻易示人嘛

    虽然如此,但《世界难题...》一文中对事务还是归纳的还是蛮到位的:“一个看似简单的功能,内部可能需要调用多个“服务”并操作多个数据库或分片来实现,单一技术手段和解决方案已无法满足这些复杂应用场景。因此,分布式系统架构中分布式事务是一个绕不过去的挑战。

    什么是分布式事务?简单的说,就是一次大操作由不同小操作组成,这些小操作分布在不同服务器上,分布式事务需要保证这些小操作要么全部成功,要么全部失败。”

    举个栗子:

    你上Taobao买东西,需要先扣钱,然后商品库存-1吧。但扣款和库存分别属于两个服务,这两个服务中间要经过网络、网关、主机等一系列中间层,万一任何一个地方出了问题,比如网络抖动、突发异常等待,都会导致不一致,比如扣款成功了,但是库存没-1,就会出现超卖的现象,而这就是分布式事务需要解决的问题

    二 2阶段提交(2PC, 3PC等) 

             2阶段提交是分布式事务传统解决方案,先进为止还广泛存在。当一个事务跨越多个节点时,为了保持事务ACID特性,需要引入一个作为协调者来统一掌控所有节点(称作参与者)的操作结果并最终指示这些节点是否要把操作结果进行真正的提交(比如将更新后的数据写入磁盘等等)。因此,二阶段提交的算法思路可以概括为:参与者将操作成败通知协调者,再由协调者根据所有参与者的反馈情报决定各参与者是否要提交操作还是中止操作。

    以开会为例

    甲乙丙丁四人要组织一个会议,需要确定会议时间,不妨设甲是协调者,乙丙丁是参与者。

    投票阶段

    1. 甲发邮件给乙丙丁,周二十点开会是否有时间;
    2. 甲回复有时间;
    3. 乙回复有时间;
    4. 丙迟迟不回复,此时对于这个活动,甲乙丙均处于阻塞状态,算法无法继续进行;
    5. 丙回复有时间(或者没有时间);

    提交阶段

    1. 协调者甲将收集到的结果反馈给乙丙丁(什么时候反馈,以及反馈结果如何,在此例中取决与丙的时间与决定);
    2. 乙收到;
    3. 丙收到;
    4. 丁收到;

    不仅要锁住参与者的所有资源,而且要锁住协调者资源,开销大。一句话总结就是:2PC效率很低,对高并发很不友好。

    引用《世界性难题...》一文原话 "国外具有几十年历史和技术沉淀的基于XA模型的商用分布式事务产品,在相同软硬件条件下,开启分布式事务后吞吐经常有数量级的下降。"

    此外还有三阶段提交

     

    三 柔性事务

    所谓柔性事务是相对强制锁表的刚性事务而言。流程入下:服务器A的事务如果执行顺利,那么事务A就先行提交,如果事务B也执行顺利,则事务B也提交,整个事务就算完成。但是如果事务B执行失败,事务B本身回滚,这时事务A已经被提交,所以需要执行一个补偿操作,将已经提交的事务A执行的操作作反操作,恢复到未执行前事务A的状态。

    缺点是业务侵入性太强,还要补偿操作,缺乏普遍性,没法大规模推广。

     

    四 消息最终一致性解决方案之RocketMQ

          目前基于消息队列的解决方案有阿里的RocketMQ,它实现了半消息的解决方案,有点类似于Paxos算法

    第一阶段:上游应用执行业务并发送MQ消息

     

    1. 上游应用发送待确认消息到可靠消息系统
    2. 可靠消息系统保存待确认消息并返回
    3. 上游应用执行本地业务
    4. 上游应用通知可靠消息系统确认业务已执行并发送消息。

    可靠消息系统修改消息状态为发送状态并将消息投递到 MQ 中间件

     第二阶段:下游应用监听 MQ 消息并执行业务

    下游应用监听 MQ 消息并执行业务,并且将消息的消费结果通知可靠消息服务。

     

    1. 下游应用监听 MQ 消息组件并获取消息
    2. 下游应用根据 MQ 消息体信息处理本地业务
    3. 下游应用向 MQ
    4. 确认消息被消费
    5. 下游应用通知可靠消息系统消息被成功消费,可靠消息将该消息状态更改为已完成

      RocketMQ貌似是一种先进的实现方案了,但问题是缺乏文档,无论是在Apache项目主页,还是在阿里的页面上,最多只告诉你如何用,而原理性或者指导性的东西非常缺乏。

    当然,如果你在阿里云上专门购买了RocketMQ服务,想必是另当别论了。但如果你试图在自己的服务环境中部署和使用,想必要历经相当大的学习曲线。

     

     

    四、 消息最终一致性解决方案之RabbitMQ实现

      RabbitMQ遵循了AMQP规范,用消息确认机制来保证:只要消息发送,就能确保被消费者消费来做到了消息最终一致性。而且开源,文档还异常丰富,貌似是实现分布式事务的良好载体

     

    4.1 RabbitMQ消息确认机制

           

    rabbitmq的整个发送过程如下

    1. 生产者发送消息到消息服务

    2. 如果消息落地持久化完成,则返回一个标志给生产者。生产者拿到这个确认后,才能放心的说消息终于成功发到消息服务了。否则进入异常处理流程。

    3. 消息服务将消息发送给消费者

    4. 消费者接受并处理消息,如果处理成功则手动确认。当消息服务拿到这个确认后,才放心的说终于消费完成了。否则重发,或者进入异常处理。

    4.2 异常

    我们来看看可能发送异常的四种

    1. 直接无法到达消息服务

    网络断了,抛出异常,业务直接回滚即可。如果出现connection closed错误,直接增加 connection数即可

    connectionFactory.setChannelCacheSize(100);


     

    2. 消息已经到达服务器,但返回的时候出现异常

    rabbitmq提供了确认ack机制,可以用来确认消息是否有返回。因此我们可以在发送前在db中(内存或关系型数据库)先存一下消息,如果ack异常则进行重发. 其实就是生产者推送消息后,触发的回调方法里做处理。

        /**confirmcallback用来确认消息是否有送达消息队列*/     
        rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
        if (!ack) {
            //try to resend msg
        } else {
            //delete msg in db
        }
        });
         /**若消息找不到对应的Exchange会先触发returncallback */
        rabbitTemplate.setReturnCallback((message, replyCode, replyText, tmpExchange, tmpRoutingKey) -> {
            try {
                Thread.sleep(Constants.ONE_SECOND);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        
            log.info("send message failed: " + replyCode + " " + replyText);
            rabbitTemplate.send(message);
        });
    

    3. 消息送达后,消息服务自己挂了

    如果设置了消息持久化,那么ack= true是在消息持久化完成后,就是存到硬盘上之后再发送的,确保消息已经存在硬盘上,万一消息服务挂了,消息服务恢复是能够再重发消息

    4. 未送达消费者

    消息服务收到消息后,消息会处于"UNACK"的状态,直到客户端确认消息
     

    • deliveryTag:该消息的index

    • multiple:是否批量. true:将一次性ack所有小于deliveryTag的消息。

    channel.basicAck(envelope.getDeliveryTag(), false);

    5. 确认消息丢失

    消息返回时假设确认消息丢失了,那么消息服务会重发消息。注意,如果你设置了autoAck= false,但又没应答channel.baskAck也没有应答channel.baskNack,那么会导致非常严重的错误:消息队列会被堵塞住,所以,无论如何都必须应答

    6. 消费者业务处理异常

    消息监听接受消息并处理,假设抛异常了,第一阶段事物已经完成,如果要配置回滚则过于麻烦,即使做事务补偿也可能事务补偿失效的情况,所以这里可以做一个重复执行,比如guavaretry,设置一个指数时间来循环执行,如果n次后依然失败,发邮件、短信,用人肉来兜底。

     

    最后个人提一句:

     

    选择哪种解决方案,思维不能定死,文章里的顶多提供一些思路。

    是选择采取加入中间件? 还是采取手动保证事务? 还是采取补救方式?

    这些东西没有固定的说法,只有根据自己当前项目场景,团队情况,业务需求及可接受程度等等诸多因素 去选择适宜的方案。

     

     

     

    展开全文
  • 分布式事务解决方案微服务分布式事务解决方案.docx
  • 微服务分布式事务-TCC模式   一个完整的 TCC 业务由一个主业务服务和若干个从业务服务组成,主业务服务发起并完成整个业务活动,TCC 模式要求从服务提供三个接口:Try、Confirm、Cancel。 1. Try 1)完成所有业务...

    微服务分布式事务-TCC模式

      一个完整的 TCC 业务由一个主业务服务和若干个从业务服务组成,主业务服务发起并完成整个业务活动,TCC 模式要求从服务提供三个接口:Try、Confirm、Cancel。

    1. Try

    1)完成所有业务检查;
    2)预留必须业务资源。

    2. Confirm

    1)真正执行业务

    2)不作任何业务检查

    3)只使用 Try 阶段预留的业务资源

    4)Confirm 操作满足幂等性

    3. Cancel

    1)释放 Try 阶段预留的业务资源

    2)Cancel 操作满足幂等性

    3)整个 TCC 业务分成两个阶段完成:

    • 第一阶段:主业务服务分别调用所有从业务的 try 操作,并在活动管理器中登记所有从业务服务。当所有从业务服务的 try
      操作都调用成功或者某个从业务服务的 try 操作失败,进入第二阶段。
    • 第二阶段:活动管理器根据第一阶段的执行结果来执行 confirm 或 cancel 操作。

      如果第一阶段所有 try 操作都成功,则活动管理器调用所有从业务活动的 confirm操作。否则调用所有从业务服务的 cancel 操作。

      需要注意的是,第二阶段 confirm 或 cancel 操作本身也是满足最终一致性的过程,在调用 confirm 或 cancel 的时候也可能因为某种原因(比如网络)导致调用失败,所以需要活动管理支持重试的能力,同时这也就要求 confirm 和 cancel 操作具有幂等性。

      在补偿模式中一个比较明显的缺陷是,没有隔离性。从第一个工作服务步骤开始一直到所有工作服务完成(或者补偿过程完成),不一致是对其他服务可见的。另外最终一致性的保证还充分的依赖了协调服务的健壮性,如果协调服务异常,就没法达到一致性。

      TCC模式在一定程度上弥补了上述的缺陷,在TCC模式中直到明确的confirm动作,所有的业务操作都是隔离的(由业务层面保证)。另外工作服务可以通过指定 try 操作的超时时间,主动的 cancel 预留的业务资源,从而实现自治的微服务。

      TCC模式和补偿模式一样需要需要有协调服务和工作服务,协调服务也可以作为通用服务一般实现为框架。与补偿模式不同的是 TCC 服务框架不需要记录详细的业务流水,完成 confirm 和 cancel 操作的业务要素由业务服务提供。

      在第4步确认预订之前,订单只是pending状态,只有等到明确的confirm之后订单才生效。

      如果3个服务中某个服务try操作失败,那么可以向TCC服务框架提交cancel,或者什么也不做由工作服务自己超时处理。

      TCC 模式也不能百分百保证一致性,如果业务服务向 TCC 服务框架提交 confirm后,TCC 服务框架向某个工作服务提交 confirm 失败(比如网络故障),那么就会出现不一致,一般称为 heuristic exception。

      需要说明的是,为保证业务成功率,业务服务向 TCC 服务框架提交 confirm 以及TCC 服务框架向工作服务提交 confirm/cancel 时都要支持重试,这也就要confirm/cancel 的实现必须具有幂等性。如果业务服务向 TCC 服务框架提交confirm/cancel 失败,不会导致不一致,因为服务最后都会超时而取消。

      另外 heuristic exception 是不可杜绝的,但是可以通过设置合适的超时时间,以及重试频率和监控措施使得出现这个异常的可能性降低到很小。如果出现了heuristic exception 是可以通过人工的手段补救的。

    对账是最后的终极防线!!!

      如果有些业务由于瞬时的网络故障或调用超时等问题,通过上文所讲的 3 种模式一般都能得到很好的解决。但是在当今云计算环境下,很多服务是依赖于外部系统的可用性情况,在一些重要的业务场景下还需要周期性的对账来保证真实的一致性。比如支付系统和银行之间每天日终是都会有对账过程。

    转载自:https://blog.51cto.com/17099933344/2054648

    展开全文
  • 但是在当下流行的分布式微服务来说,普通的Spring事务处理已经无法满足场景,Spring事务也是基于jvm级别的,当多个服务系统之间进行调用,进行数据库操作,一旦失败就会发现事务会存在严重的问题,举个简单的例子 ...

    在以前传统的web应用当中,一个项目基本一个war/jar包走天下,对于事务处理相信很多的项目基本是使用到的spring的事务处理。但是在当下流行的分布式微服务来说,普通的Spring事务处理已经无法满足场景,Spring事务也是基于jvm级别的,当多个服务系统之间进行调用,进行数据库操作,一旦失败就会发现事务会存在严重的问题,举个简单的例子

    在上图中,为了减轻数据库的压力等,将数据库分成了2个不同的物理机数据库,在订单系统中需要对2个数据库有数据交互的情况下,在订单库操作操作mysql的时候,成功了,但是在进行库存操作时失败了,平时使用的Spring事务已经完全派不上用场了。

    分布式事务-垮库事务

    2PC:二阶段提交协议,即将事务的提交过程分为两个阶段来进行处理:准备阶段和提交阶段。事务的发起者称协调者,事务的执行者称参与者。

    准备阶段

    提交阶段

    第一阶段==》准备阶段

           协调者向参与者发起询问,询问是否可以提交事务等。然后等待参与者答复,参与者全部答复允许/yes后

           参与者进行sql等一系列事务操作(没有提交事务),预提交

           参与者都成功,给协调者返回yes,只要一方参与者执行失败,返回no,告诉协调者不可提交

    第二阶段==》提交阶段

          当都可以进行数据库操作之后,需要进行事务提交。

          当参与者事务提交全部返回yes后即成功,若其中一个返回no即全部不可提交,进行回滚

    提交事务流程:

                 协调者向参与者发起事务提交请求commit,参与者commit提交事务并释放所占用的系统资源,参与者向协调者返回提交事务的结果,协调者收到全部成功后即完成事务提交

    市面上有的的框架比如jta atomikos,就能完成上面的2PC的功能,在本人第一次遇到上面的垮库场景时,就是用的jta atomikos。原理一样的,使用起来很简单,但可能会有性能一定的影响,如果在需求业务场景允许的条件下,可以尝试

    JTA:java transaction api,是java两阶段提交协议的一种规范,java提供一套规范,atomikos框架来进行实现,和JPA一套规范,hibernate来进行实现一样的道理

    代码实现:如果使用的Spring Boot项目

    pom.xml:

    <dependency> 
            <groupId>org.springframework.boot</groupId> 
        <artifactId>spring-boot-starter-jta-atomikos</artifactId> 
    </dependency>

    application.yml:

    spring: 
        datasource:
            druid: 
              type: com.alibaba.druid.pool.DruidDataSource
              driverClassName: com.mysql.jdbc.Driver
              driver-class-name: com.mysql.jdbc.Driver
              platform: mysql
              default: 
                url: jdbc:mysql://xxx.xxx.xxx.xxx:3306/mypinyu?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false
                username: root
                password: Goodlan@123
              second: 
                url: jdbc:mysql://localhost:3306/mypinyu?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false
                username: root
                password: admin
              initialSize: 5
              minIdle: 5
              maxActive: 20
              maxWait: 60000
              timeBetweenEvictionRunsMillis: 60000
              minEvictableIdleTimeMillis: 300000
              validationQuery: SELECT1FROMDUAL
              testWhileIdle: true
              testOnBorrow: false
              testOnReturn: false
              logSlowSql: true
            
    logging.config:
      classpath: log4j2.xml
        
    mybatis: 
      typeAliasesPackage: com.pinyu.system.entity
      mapper-locations: classpath:mapper/**/*Mapper.xml
      
    #返回视图的前缀   目录对应src/main/webapp下
    spring.mvc.view.prefix: /WEB-INF/jsp/
    #返回的后缀
    spring.mvc.view.suffix: .jsp
    package com.pinyu.system.global.config.datasource;
    
    public interface DataSourceNames {
    
    	public static final String DEFAULT = "default";
    
    	public static final String SECOND = "second";
    
    }
    
    package com.pinyu.system.global.config.datasource;
    
    import javax.sql.DataSource;
    import javax.transaction.UserTransaction;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.boot.web.servlet.ServletRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    import org.springframework.core.env.Environment;
    import org.springframework.transaction.jta.JtaTransactionManager;
    
    import com.alibaba.druid.filter.stat.StatFilter;
    import com.alibaba.druid.support.http.StatViewServlet;
    import com.alibaba.druid.support.http.WebStatFilter;
    import com.alibaba.druid.wall.WallConfig;
    import com.alibaba.druid.wall.WallFilter;
    import com.atomikos.icatch.jta.UserTransactionImp;
    import com.atomikos.icatch.jta.UserTransactionManager;
    
    
    @Configuration
    public class DruidConfig {
    
    	@Bean(DataSourceNames.DEFAULT)
        @Primary
        @Autowired
        @ConfigurationProperties(prefix = "spring.datasource.druid."+DataSourceNames.DEFAULT)
        public DataSource systemDataSource(Environment env) {
    		return new AtomikosDataSourceBean();
    
        }
    
        @Autowired
        @Bean(name = DataSourceNames.SECOND)
        @ConfigurationProperties(prefix = "spring.datasource.druid."+DataSourceNames.SECOND)
        public AtomikosDataSourceBean businessDataSource(Environment env) {
        	return new AtomikosDataSourceBean();
        }
    
    
        /**
         * 注入事物管理器
         * @return
         */
        @Bean("transactionManager")
        public JtaTransactionManager regTransactionManager () {
            UserTransactionManager userTransactionManager = new UserTransactionManager();
            UserTransaction userTransaction = new UserTransactionImp();
            return new JtaTransactionManager(userTransaction, userTransactionManager);
        }
    
    }
    
    package com.pinyu.system.global.config.datasource;
    
    import javax.sql.DataSource;
    
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.SqlSessionTemplate;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    import org.springframework.core.io.support.ResourcePatternResolver;
    
    
    @Configuration
    @MapperScan(basePackages = "com.pinyu.system.mapper",sqlSessionFactoryRef = "defaultSqlSessionFactory")
    public class MybatisDataSourceDefaultConfig {
    
    
        @Bean
        public SqlSessionFactory defaultSqlSessionFactory(@Qualifier(DataSourceNames.DEFAULT)DataSource ds) throws Exception {
            SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
            factoryBean.setDataSource(ds);
            //指定mapper xml目录
            ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            factoryBean.setMapperLocations(resolver.getResources("classpath:mapper/**/*Mapper.xml"));
            return factoryBean.getObject();
    
        }
    
        @Bean
        public SqlSessionTemplate defaultSqlSessionTemplate(@Qualifier("defaultSqlSessionFactory")SqlSessionFactory sf) throws Exception {
            SqlSessionTemplate template = new SqlSessionTemplate(sf); // 使用上面配置的Factory
            return template;
        }
    
    }
    
    package com.pinyu.system.global.config.datasource;
    
    import javax.sql.DataSource;
    
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.SqlSessionTemplate;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    import org.springframework.core.io.support.ResourcePatternResolver;
    
    /** 
    * @author ypp
    * 创建时间:2018年11月9日 上午10:18:03 
    * @Description: TODO(用一句话描述该文件做什么) 
    */
    @Configuration
    @MapperScan(basePackages = "com.pinyu.system.mapper2",sqlSessionFactoryRef = "secondSqlSessionFactory")
    public class MybatisDataSourceSecondConfig {
    
    	@Bean
        public SqlSessionFactory secondSqlSessionFactory(@Qualifier(DataSourceNames.SECOND)DataSource ds) throws Exception {
            SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
            factoryBean.setDataSource(ds);
            //指定mapper xml目录
            ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            factoryBean.setMapperLocations(resolver.getResources("classpath:mapper2/**/*Mapper.xml"));
            return factoryBean.getObject();
    
        }
    
        @Bean
        public SqlSessionTemplate secondSqlSessionTemplate(@Qualifier("secondSqlSessionFactory")SqlSessionFactory sf) throws Exception {
            SqlSessionTemplate template = new SqlSessionTemplate(sf); // 使用上面配置的Factory
            return template;
        }
    
    }
    

    mapper和mapper2里面都是mapper接口,操作不同的数据库,在其他地方注入哪个mapper就是操作的哪个数据库,自行配置即可

    在service注入就行,但是必须贴上注解@Transactional,

    @Service
    @Transactional
    public class UserServiceImpl implements UserService{
    
    	@Autowired
    	private UserMapper userMapper;
    
        @Autowired
    	private UserMapper2 userMapper2;
    }

    有兴趣的同学可以试试,每个mapper配置不同的数据库操作,一个异常,全部会进行回滚。配置代码可以进一步优化

     

    在以上的还是有一定的缺陷,比如突然一个数据库连接不上了,网络故障等。在以前某宝出现过网线被挖掘机挖断,导致很多脏数据。

    缺陷:1、同步阻塞,一直会占用资源,对性能有一定的影响

            2、脑裂,部分参与者提交commit请求,节点数据错乱

            3、单点,协调者挂了,参与者一直处于锁定状态

     

    3PC:三阶段提交协议,在2PC基础之上更进一步,如果出现网络故障等情况,会进行重试操作,超时连接等。

    比较适合的框架有jta atomikos(三阶段提交协议收费,抛弃)、tcc transaction、spring-cloud-rest-tcc、支付宝内部也有一个框架(名字忘了)。

    在实际应用中,要用分布式事务的话,建议还是使用分布式消息队列来实现分布式事务数据最终一致性,高可用方案。强一致性就是以性能高可用换数据一致性。有兴趣的同学可以去了解下分布式系统CAP理论,从本人理解来说jta atomikos这种就是一个CP解决方案。用消息队列实现最终一致性就是AP解决方案,也是很多公司选择的技术实现(大牛勿喷)

    后续对分布式消息队列实现最终一致性进行整理,只要学不死,就往死里学

    展开全文
  • #资源达人分享计划#
  • 本资源是微服务分布式事务实战博客文章(10篇)的相关资源包,包括文章中的提到的sql,微服务和服务器
  • Redis 的安装和启动 (1)下载redis 解压缩 (2) 启动和测试redis 1进入redis 目录 2双击redis-server.exe 程序即可启动 3 启动客户端测试 启动:双击redis-cli.exe 测试:输入命令 ......
  • 分布式事务产生背景 传统单体项目单库大部分情况下,不会产生分布式事务。但随着系统数据量增大,单个数据库承受越来越大压力,系统开始变慢,单库出现性能瓶颈。用户开始抱怨,慢慢当前系统结构开始无法适应业务...
  • 微服务分布式事务的实现方法及替代方案这两天正在研究微服务架构中分布式事务的处理方案, 做一个小小的总结, 作为备忘. 如有错误, 欢迎指正!概念澄清事务补偿机制: 在事务链中的任何一个正向事务操作, 都必须存在...
  • 微服务架构的分布式事务解决方案,完整31讲。,百度云资源 非加密,无密码,
  • 我们在上一章已经测试了Seata的AT模式的分布式事务,不过每个SpringBoot中都是直接用IP+端口的模式去调用其他服务的,现在我们将上一章中的4个微服务注册到Nacos,并且通过Feign来实现远程调用。 接入Nacos和...
  • 完整版微服务架构的分布式事务解决方案
  • 微服务架构本质上就是分布式服务化架构,微服务架构的流行,让分布式事务问题日益突出!尤其是在订单业务、资金业务等系统核心业务流程中,一定要有可靠的分布式事务解决方案来保证业务数据的可靠性和准确性。为了...
  • https://www.processon.com/view/link/5b2144d7e4b001a14d3d2d30
  • 同时我们阐述了分布式事务产生的背景,及分布式事务实践相关基础理论,并分析了 "CAP理论" 以及分布式系统实践中"CAP"该如何进行取舍。 其实软件行业前辈们在分布式系统设计中,基于CAP理论基础进行了反复的实践,...
  • 最新,最新,最新,经过个人整理的,视频文档齐全,代码全部调试通过的,你值得拥有。抓紧时间学习吧.
  • 微服务倡导将复杂的单体应用拆分为若干个功能简单的、松耦合的服务,这样可以降低开发难度、增强扩展性、便于敏捷开发。概念2012年提出迅速火遍全球,被越来越多的开发者推崇,很多互联网行业巨头、开源社区等都开始...
  • 微服务架构本质上就是分布式服务化架构,微服务架构的流行,让分布式事务问题日益突出!尤其是在订单业务、资金业务等系统核心业务流程中,一定要有可靠的分布式事务解决方案来保证业务数据的可靠性和准确性。 为了...
  • 上一篇文章写了微服务分布式事务问题的示例 https://blog.csdn.net/daziyuanazhen/article/details/100825240 这里在上一篇的代码基础上,引入TX-LCN框架解决分布式事务问题。 TX-LCN框架 原理 创建一个...
  • 本章我们主要了解一下分布式事务的概念、目前市面上的解决方案、以及在微服务中如何实现分布式事务。 什么是事务,什么是ACID 首先,提到分布式事务,咱们得明白什么是事务(Transaction),百科的链接放这里咯,...
  • 微服务架构的分布式事务解决方案
  • 微服务分布式事务落地方案

    千次阅读 2016-11-10 12:19:21
    网上很多关于微服务的文章都是虚无缥缈的,长编大论玩文字概念。本文我想跟大家探讨一下微服的具体落地方案。 在架构设计领域,有一个跟微服务很相似的概念叫“微内核”。微内核实质就是强化面向接口编程,内核只...
  • 微服务架构,分布式事务处理机制,包含常用的分布式事务解决方案。TCC型分布式事务方案,最大努力通知方案,可靠消息服务的设计与实现等。包含分布式服务框架duboo 的原理及使用案例。
  • 微服务架构下的分布式事务

    千次阅读 2020-06-20 19:30:58
    微服务架构下的分布式事务
  • 龙果学院中华石杉主讲的大型网站分布式微服务分布式事务处理教程,很全配资料,希望对你有帮助的。 微服务架构的分布式事务解决方案,完整31讲-龙果学院
  • 然后创建一个整合服务,调用微服务实现数据的保存到2个不同的数据库,要求采用分布式事务,要么都成功,要么都失败。 案例拓扑图: 实现步骤: 1 分布式事务处理器的编译和运行 Redis 安装 注册中心编写 ...
  • 微服务分布式事务

    千次阅读 多人点赞 2019-09-02 18:16:19
    课程分享: 课程分享:Docker+...这篇文章将介绍什么是分布式事务分布式事务解决什么问题,对分布式事务实现的难点,解决思路,不同场景下方案的选择,通过图解的方式进行梳理、总结和比较。 相信耐心看...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 46,187
精华内容 18,474
关键字:

微服务分布式事务