理解Java分布式事务的实现原理

本文详细探讨了Java分布式事务的使用场景、实现原理和常见方法。从简单的独立事务管理器到通过自定义连接类和AOP切面实现的升级方案,展示了如何在分布式环境中协调事务操作。难点包括在不提交状态下保持数据库连接和跨服务的事务通知。最后,提出了通过注解简化使用方式的思路。
摘要由CSDN通过智能技术生成

什么时候使用分布式事务

我们需要事物的时候一般是对数据库进行了写或者删除的操作。分布式事务常用在一个项目调用了另外一个项目的api , 如果这个api执行成功了并且调用的这个service方法也没有抛出异常调用方和被调用方都同时提交,只要有一方抛出了异常就全部回滚。

实现原理

简单分析下我们希望达到的效果是如果A调用了B
1. B如果抛出了异常就需要将A和B同时回滚,
2. A如果在调用B之前抛出了异常就只回滚A,
3. A在调用B之后抛出了异常就同时回滚A和B
4. A和B都没有抛出异常就都提交

最简单的实现方法

所能想到的最简单的方法就是A和B使用不同的事物管理器。B如果抛出了异常就先回滚自己的,A调用B失败在手动调用B提供的一个用于回滚的方法,然后在回滚自己的。但是这不够优雅,而且B必须提供一个用于回滚的api. 造成这么复杂的原因是因为他们使用了不同的事物管理器。

实现方法升级

A和B都使用同一个事物管理器。这个事物管理器可以是使用Netty创建的服务。

  1. A开始执行的时候向事物管理器发送一条消息创建一个事物组。
  2. A调用B的时候,B执行完真正的数据库操作后,但是不提交hold住,B也向事物管理器发送一条消息,把自己加入这个事物组中, 并且告诉管理器自己的状态是回滚还是提交
  3. A执行完真正的数据库操作后,但是不提交hold住,A向管理器发送一条自己的状态
  4. 管理器检查到所有的事物都执行完了后,向A和B发送一个提交或者回滚的命令,A和B才进行真正的回滚或者提交

大概过程就向下面这个图这样
在这里插入图片描述

实现的难点
  1. 什么实现A执行完真正的数据库操作后,不提交hold住?
    比较简单的实现方法就是在spring的事物基础上封装,

重写Connection类里面的commit和rollback方法。

public class LbConnection implements Connection {

    private Connection connection;
    private LbTransaction lbTransaction;

    public LbConnection(Connection connection, LbTransaction lbTransaction) {
        this.connection = connection;
        this.lbTransaction = lbTransaction;
    }

    public void commit() {
        new Thread(new Runnable() {
            public void run() {
                //先阻塞着
                lbTransaction.getTask().waitTask();
                try {
                    if (lbTransaction.getTransactionType().equals(TransactionType.commit)) {
                        connection.commit();
                    } else {
                        connection.rollback();
                    }
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    public void rollback() throws SQLException {

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    lbTransaction.getTask().waitTask();
                    connection.rollback();
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
 }

写一个切面当事物获取connection的时候获取我们自定义的connection

@Aspect
@Component
public class LbDataSourceAspect {

    @Around("execution(* javax.sql.DataSource.getConnection(..))")
    public Connection around(ProceedingJoinPoint point) throws Throwable {
        if (LbTransactionManager.getCurrent() != null) {
            Connection connection = (Connection) point.proceed();
            connection.setAutoCommit(false);
            return new LbConnection(connection, LbTransactionManager.getCurrent().get());
        } else {
            return (Connection) point.proceed();
        }
    }
}
  1. 管理器向A和B发送提交或者回滚的命令,A和B怎么提交或者回滚
    有了上面的基础这个就简单了,A和B接收到命令后直接将阻塞的唤醒就可以了。
 @Override
    public synchronized void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        String s = ((ByteBuf) msg).toString(CharsetUtil.UTF_8);
        System.out.println("接收数据" + s);
        JSONObject jsonObject = JSON.parseObject(s);
        String groupId = jsonObject.getString("groupId");
        String command = jsonObject.getString("command");
        LbTransaction lbTransaction = LbTransactionManager.getLbTransaction(groupId);
        if("rollback".equals(command)){
            lbTransaction.setTransactionType(TransactionType.rollback);
        }else{
            lbTransaction.setTransactionType(TransactionType.commit);
        }
        //执行rollback 或者commit
        lbTransaction.getTask().singleTask();
    }

  1. 怎么让使用更加方便,比如像spring事物那样,一个注解就使用了分布式事物。

可以定义一个注解,并且给加了这个注解的方法定义一个代理。只要检测到方法中有这个注解,就向事物管理器发送自己执行的结果。

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface LbTransactional {
    boolean isEnd() default false;
    boolean isStart() default false;
}

@Aspect
@Component
public class LbTransactionAspect implements Ordered {

    @Around("@annotation(com.sl.server.global.transation.annotation.LbTransactional)")
    public void invoke(ProceedingJoinPoint point) throws Exception {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        LbTransactional lbTransactional = method.getAnnotation(LbTransactional.class);

        String groupId = "";
        if (lbTransactional.isStart()) {
           //向事物管理器发送创建事物组的命令
            groupId = LbTransactionManager.createLbTransactionGroup();
        } else {
            groupId = LbTransactionManager.getCurrentGroupId();
        }
        LbTransaction lbTransaction = LbTransactionManager.createLbTransaction(groupId);
        try {
            //spring 开启mysql事物
            point.proceed();
            //向事物管理器发送消息
            LbTransactionManager.addLbtransaction(lbTransaction, lbTransactional.isEnd(), TransactionType.commit);
        } catch (Throwable throwable) {
            LbTransactionManager.addLbtransaction(lbTransaction, lbTransactional.isEnd(), TransactionType.rollback);
            throwable.printStackTrace();
        }
    }

    public int getOrder() {
        return 1000;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值