heim分布式事务----day01

base是对ap的扩展的。

分布式事务的问题,张三给李四付款,李四消费了,但是没有返回,则张三回滚,此时会出现事务问题。

cap的理论:

结合主从数据库的读取。

c一致性:指的是写操作后,读的操作可以读到最新的状态。当数据分布在多个节点,任意节点都是最新的数据。一般写是主数据库,读是从数据库。

a:可用性:任何事务操作都可以得到相应的结果,且不会出现超时或者响应错误。从数据库及时得到结果不允许超时或者错误。

p分区容忍性:异步同步到从数据库,添加从数据库节点。

------------------------------------------------------------------------02---08-----------------------------------------------------------------------------------

两阶段提交:2pc

解决方案:XA 事务的管理器,事务的参与者

问题:依赖数据库,准备阶段锁定资源,要等到都好了才会释放,是在数据库实现的。

扫描二维码关注公众号,回复: 9380940 查看本文章

解决方案:Seata

问题:不要求数据库支持XA协议,工作在业务层,XA基于数据库的。对业务是0侵入的。

支持:AT和TCC。

RM是数据库的实例。

和XA不同,第一阶段就把锁释放了,并提交本地事务。不依赖数据库就行回滚而是依赖应用层。

回滚就是业务的反向操作。

TC:事务协调器,维护全局事务的运行状态,接受TM指令,发起全局事务的提交与回滚,负责与RM通信。

TM:事务管理器,负责开启全局事务,向TC发起全局提交或者回滚。

RM:数据库实例

AP:略。

工作过程:向TC注册,TC发事务ID,TM通知TC回滚还是提交。

----------------------------------------09--10------------------------------------------------------------------------------------

代码:

TM和RM是jar包的。

表:

要点说明:

构建工程:

第一步导入sql

表的结构:

四个表:account_info*2 undo_log*2

数据库:192.168.244.130/131  bank1  bank2 root 123456

第二步:Seata服务端

打开:

第三步:打开eureka的注册中心

第四步:打开eureka

第五步:导入dtx把bank1和bank2

第六步:分析提交

1.TM和RM向TC注册

2.在哪里开启全局事务TM就在哪力

标识开启全局事务的起点。

3.TC返回全局事务的ID

4.RM向TC注册分支事务,分支事务的ID和全局事务的ID已经绑定了

5.写扣减逻辑。

6.向undo写入数据代理的。

7.提交事务。

8.上报事务的处理结果。

9.通过feign接口携带事务ID去增加金额。

10.向TC注册分支事务。

11.

12.

第七步:分析回滚

-------------------------------------------------------11--------12------------------------------------------------------------------

分析一些配置:

第一步:

引入这个TM和RM就有了

第二步:拷贝文件,我们在TC事务协调器拷贝这两个文件

到:

第三步:

file.conf的分组的说明,我们是文件的不是数据库的就修改这个:

service {
  #vgroup->rgroup
  vgroup_mapping.seata-demo-bank1-fescar-service-group = "default"
  #only support single node
  default.grouplist = "127.0.0.1:8888"
  #degrade current not support
  enableDegrade = false
  #disable
  disable = false
}

修改1就行,2是固定的。

第一个名字就是微服务的注册的名字。这个是十分重要的。

default:TCC事务协调的默认的名字

第四步:修改配置类,配置代理数据源,Rm通过代理数据源在事务提交的时候,与TC通信交流,记录undo_log日志等。

@Configuration
public class DatabaseConfiguration {


    private final ApplicationContext applicationContext;

    public DatabaseConfiguration(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }


    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.ds0")
    public DruidDataSource ds0() {
        DruidDataSource druidDataSource = new DruidDataSource();
        return druidDataSource;
    }


    @Primary
    @Bean
    public DataSource dataSource(DruidDataSource ds0)  {
        DataSourceProxy pds0 = new DataSourceProxy(ds0);
        return pds0;
    }



}

我们根据这个去配置去yml文件查看下。

---------------------------------------------------------------------13-----------------------------------------------------------------

案例是张三给李四转账:

第一步:写DAO

第二步:写Service

/**
 * @author Administrator
 * @version 1.0
 **/
@Service
@Slf4j
public class AccountInfoServiceImpl implements AccountInfoService {

    @Autowired
    AccountInfoDao accountInfoDao;

    @Autowired
    Bank2Client bank2Client;

    @Transactional //开启本地事务
    @GlobalTransactional//开启全局事务 只开启一次
    @Override
    public void updateAccountBalance(String accountNo, Double amount) {
        log.info("bank1 service begin,XID:{}", RootContext.getXID());
        //扣减张三的金额
        accountInfoDao.updateAccountBalance(accountNo,amount *-1);
        //调用李四微服务,转账
        String transfer = bank2Client.transfer(amount);
        if("fallback".equals(transfer)){
            //调用李四微服务异常
            throw new RuntimeException("调用李四微服务异常");
        }
        if(amount == 2){
            //人为制造异常
            throw new RuntimeException("bank1 make exception..");
        }
    }
}

第三步:写Feign

@FeignClient(value="seata-demo-bank2",fallback=Bank2ClientFallback.class)
public interface Bank2Client {
    //远程调用李四的微服务
    @GetMapping("/bank2/transfer")
    public  String transfer(@RequestParam("amount") Double amount);
}

第四步:写controller。

---------------------------------------------------------------------14-----------------------------------------------------------------

李四的微服务:

  @Transactional
    @Override
    public void updateAccountBalance(String accountNo, Double amount) {
        log.info("bank2 service begin,XID:{}",RootContext.getXID());
        //李四增加金额
        accountInfoDao.updateAccountBalance(accountNo,amount);
        if(amount==3){
            //人为制造异常
            throw new RuntimeException("bank2 make exception..");
        }
    }

---------------------------------------------------------------------15-----------------------------------------------------------------

测试:

第一步:启动三个工程,启动TC 协调器。

第二步:测试

1.正常的时候:http://localhost:56081/bank1/transfer?amount=1

2.给李四转的是3元,但是李四是异常的。

通过上下文拿到事务的id。

李四失败了张三回滚,张三失败了李四回滚。

在都未处理完成之后undolog日志会记录更新前和更新后的账户的信息的。

抛异常后自动生成update语句进行回滚。

3.超时:也会回滚

注意一点:每个数据库都要有undo_log这个表是本地事务一致性的关键,因为本地事务执行完都要提交的。

---------------------------------------------------------------------16-----------------------------------------------------------------

undolog日志,记录更新前的数据和更新后的数据。根据undolog日志自动生成update语句进行更新。

发布了308 篇原创文章 · 获赞 11 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_28764557/article/details/104399604