GitHub 开源项目 eval-tx 分布式事务解决方案

eval-tx

基于 redis 的分布式事务框架

2PC 两阶段提交,60s 超时机制,原理简单易懂

附简单 Demo

如果还没碰到过分布式事务问题,也可通过本文进行了解和学习。

项目地址: https://github.com/huajiexiewenfeng/eval-tx

特点

  • 侵入性低
  • 自动装配
  • @Enable注解激活,开箱即用
  • 基于 Spring 事务进行二次封装,学习成本低
  • 支持超时时间 + 超时策略
  • 支持注解,使用方便

原理&设计

在这里插入图片描述
原图地址:https://www.processon.com/view/5f1a82aee0b34d54dac851df

业务流程:

  • 服务A 操作数据库1

  • 服务A 调用 服务B 操作数据库2

  • 服务A 调用 服务C 操作数据库3

业务伪代码:

method(){
    globalId = "A";// 全局事务编号
    beginTransaction();// 开启事务
    RPC-method2(globalId,2);// 远程RPC调用,操作数据库2
    RPC-method3(globalId,3);// 远程RPC调用,操作数据库3
    method1(1);// 操作数据库1
}

要达到的效果:

要么三个操作一起成功,要么一起失败。

介绍

  • 使用 redis 来作为子事务的注册中心,保存各个子事务的状态,事务之前互相感知
  • 所有事务要么一起提交,要么一起回滚
  • 新增 @EvalTransactional 注解,支持超时时间和超时策略设置(内置三种策略)
  • 下阶段改造
    • 超时时间精确度
    • 子线程返回值处理

环境&版本

  • spring-cloud Finchley+
  • springboot 2.0+
  • mysql
  • redis

项目结构

  • eval-eureka 注册中心
  • eval-tx-core 核心代码实现(实际项目引入此模块即可)
  • eval-tx-demo 示例项目
    • eval-company 企业服务
    • eval-user 用户服务
    • eval-web 主调用服务

使用方式

API 编程

1.启动类增加 @EnableEvalTransactionManager 注解,激活分布式事务服务

@MapperScan("com.csdn.dao")
@SpringBootApplication
@EnableFeignClients
@EnableDiscoveryClient
@EnableEvalTransactionManager
public class WebBootstrap {
    public static void main(String[] args) {
        SpringApplication.run(WebBootstrap.class, args);
    }
}

2.依赖注入即可使用 EvalTransactionManager API

    @Autowired
    private EvalTransactionManager evalTxManager;

3.开启主事务,调用 RPC 应用

  • evalTxManager.beginEvalTransactionManager(); // 开启事务
  • 获取全局事务ID,并传到 RPC 接口中
  • evalTxManager.executeChildTask();// 执行 RPC 调用其他服务
    • 也支持 Future,可以获取返回值
  • evalTxManager.commit();// 提交
  • evalTxManager.rollback();// 回滚
    public String addUserTx() {
        String globalTxId = evalTxManager.beginEvalTransactionManager();
        try {
            // RPC 调用用户服务增加用户
            evalTxManager.executeChildTask(() -> {
                userFeignClient.addUserTx(globalTxId, "1", "user");
            });

            // RPC 调用企业服务增加企业
            evalTxManager.executeChildTask(() -> {
                companyFeignClient.addCompanyTx(globalTxId, "1", "company");
            });

            // 插入本地数据库成功标识
            webMapper.add("1", "success");
            evalTxManager.commit();
        } catch (Exception e) {
            e.printStackTrace();
            evalTxManager.rollback();
        }
        return "";
    }

4.开启子事务,注意需要传入全局事务ID(开启主事务获取)

  • evalTxManager.beginChildEvalTransactionManager(globalTxId);// 开启子事务
  • evalTxManager.commit();// 提交
  • evalTxManager.rollback();// 回滚
    @PostMapping(value = "/eval-user/api/addUserTx")
    public String addUserTx(String globalTxId, String id, String name) {
        evalTxManager.beginChildEvalTransactionManager(globalTxId);
        try {
            userMapper.addUser(id, name);
            evalTxManager.commit();
        } catch (Exception e) {
            evalTxManager.rollback();
            return "fail";
        }
        return "success";
    }

5.完成

Annotation 注解

1.启动类增加 @EnableEvalTransactionManager 注解,激活分布式事务服务

2.开启主事务

  • 对应方法加上 @EvalTransactional 注解

  • 方法入参需要加上 String globalTxId,框架会做特殊处理

  • timeoutSeconds 默认值 60 秒

  • 默认超时策略是回滚 DefaultRollbackPolicy

    @GetMapping("addUserTxAnnotation")
    @EvalTransactional(timeoutSeconds = 5,timeoutHandler = DefaultRollbackPolicy.class)
    public String addUserTxAnnotation(String globalTxId) {

        // RPC 调用用户服务增加用户
        evalTxManager.executeChildTask(() -> {
            userFeignClient.addUserTxAnnotation(globalTxId, "1", "user");
        });

        // RPC 调用企业服务增加企业
        evalTxManager.executeChildTask(() -> {
            companyFeignClient.addCompanyTxTimeoutAnnotation(globalTxId, "1", "company");
        });

        // 插入本地数据库成功标识
        webMapper.add("1", "success");
        return "";
    }

3.开启子事务

  • 对应方法加上 @EvalTransactional(type = EvalTransactionalConstants.TYPE_CHILD) 注解,type 的值 child 表示子事务

  • 方法入参需要加上 String globalTxId,框架会做特殊处理

    @PostMapping(value = "/eval-user/api/addUserTxAnnotation")
    @EvalTransactional(type = EvalTransactionalConstants.TYPE_CHILD)
    public String addUserTxAnnotation(String globalTxId, String id, String name) {
        userMapper.addUser(id, name);
        return "success";
    }

4.完成

高级特性

超时时间设置

可以通过两种方式进行设置

  • API,表示超时时间为 5 秒
evalTxManager.beginEvalTransactionManager(5);
  • Annotation 注解,表示超时时间为 5 秒
@EvalTransactional(timeoutSeconds = 5)

超时策略选择&自定义

  • API,表示超时时间为 5 秒,超时策略为大多数提交(2/3 以上提交)
evalTxManager.beginEvalTransactionManager(5, timeoutHandler = MostCommitPolicy.class);
  • Annotation 注解,表示超时时间为 5 秒,超时策略为大多数提交(2/3 以上提交)
@EvalTransactional(timeoutSeconds = 5, timeoutHandler = MostCommitPolicy.class)

目前内置三种策略

  • DefaultRollbackPolicy 默认策略,超时后回滚当前事务
  • MostCommitPolicy 2/3 以上提交成功,提交当前事务
  • FinalCommitPolicy 超时之后提交当前事务,记录相关日志

自定义超时策略

  • 实现 TimeoutExecutionHandler 接口即可
public class CustomedCommitPolicy implements TimeoutExecutionHandler {

    /**
     * @param successCount   成功的子事务数
     * @param sumCount       总事务数
     * @param transactionKey 当前事务ID
     * @return false : 回滚 ,true : 提交
     */
    @Override
    public boolean timeoutExecution(Integer successCount, Integer sumCount, String transactionKey) {
        System.out.println("自定义超时策略");
        return false;
    }
}

使用方式和其他策略一样,在注解或者 API 中直接使用即可。

 @EvalTransactional(timeoutHandler = CustomedCommitPolicy.class)

测试

启动应用

  • eval-eureka
  • eval-company
  • eval-user
  • eval-web

正常处理流程

浏览器输入 127.0.0.1:8185/addUserTx

控制台打印

控制台1:所有事务完成,提交当前子事务,事务id:[eval_tx_manager_05815b444af74445a613faedeba04088]
控制台2:所有事务完成,提交当前子事务,事务id:[eval_tx_manager_de092ee38311477a96ed7c815dc621dd]
控制台3:所有事务完成,提交当前子事务,事务id:[eval_tx_manager_d61eec62a3a549658a53ea75d7db33fd]

数据正常入库
在这里插入图片描述在这里插入图片描述在这里插入图片描述

异常处理流程

浏览器输入 127.0.0.1:8185/addUserTxException

控制台打印

控制台1:有其他子事务执行失败,回滚当前子事务,事务id:[eval_tx_manager_6edf86f914594fd7a171becf08eb5a41]
控制台2:有其他子事务执行失败,回滚当前子事务,事务id:[eval_tx_manager_2b17c07574b94861965a154bc0a193dd]
控制台3:事务回滚,事务id:[eval_tx_manager_2016bc563d314b9d88e7678c989372c6]

数据库没有变化

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值