模拟分布式事务

事务是什么?

一个完整的,不可分割的操作单元
要么都做完,要么都不成功

什么是分布式事务

分布式事务在分布式系统中的事务。它不是单机中完成操作单元,在一个事务中可能有若干个服务器参与。

两阶段提交

两阶段提交就是一个分布式事务的解决方案。

第一个阶段:提交阶段,所有的参与者发送本地操作是否完成,如果完成则给协调者发送操作完成的消息,否则发送失败的消息,
协调者等待参与者失败或者成功的消息,或者等待超时。第一阶段结束。
第二阶段:执行阶段。当协调者收到所有参与者执行成功的消息后,发送给参与者可以完成本地操作。
如果协调者收到一个参与者执行失败的消息或者超时,则发送给参与者回滚本地操作的消息。

使用 NIO 自己实现最简单的两阶段提交。

下面是协调者的代码:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @className: Coordinator
 * @Description:
 * 通信的协议:
 * 发送 32 bit ,也就是一个 int 类型的数值,0000 0000 0000 0000 0000 0000 0000 0000
 * 第一位置上代表身份,1 代表协调者,0 代表参与者
 * 参与者: 0010 代表本地 checkpoint 结束,
 *        0000 代表失败
 * 协调者: 0001 代表回滚
 *        0011 代表所有的服务器上的事务都成功了。
 *
 * @Author: wangyifei
 * @Date: 2023/2/19 8:42
 */
public class Coordinator {
    private static Logger logger = LoggerFactory.getLogger(Coordinator.class);

    public static void main(String[] args) {
        try {
            List<ParticipantNode> participants = new ArrayList<>();
            int participantsReady = 3 ;
            ServerSocketChannel ssc = ServerSocketChannel.open();
            ssc.bind(new InetSocketAddress(6666));
            Selector selector = Selector.open();
            ssc.configureBlocking(false);
            ssc.register(selector , 0).interestOps(SelectionKey.OP_ACCEPT);
            Set<SelectionKey> selectionKeys = null ;
            Iterator<SelectionKey> iterator = null ;
            while(true){
                selector.select();
                selectionKeys = selector.selectedKeys();
                iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey next = iterator.next();
                    if(next.isAcceptable()){
                        ServerSocketChannel channel = (ServerSocketChannel)next.channel();
                        SocketChannel accept = channel.accept();
                        if(Objects.isNull(accept)){
                            continue;
                        }
                        accept.configureBlocking(false);
                        accept.register(selector , 0).interestOps(SelectionKey.OP_READ);
                        String[] IpAndPort = ParticipantUtils.getIpAndPort(accept.getRemoteAddress().toString());
                        participants.add(new ParticipantNode(IpAndPort[0] , IpAndPort[1] , System.currentTimeMillis()));
                        System.out.println("accept");
                    }else if(next.isReadable()){
                        SocketChannel channel = (SocketChannel)next.channel();
                        ByteBuffer buffer = ByteBuffer.allocate(32);
                        int read = channel.read(buffer);
                        buffer.flip();
                        long aLong = buffer.getLong();
                        long identity = ParseMsgUtils.getIdentity(aLong);
                        if(identity == 0L){
                            String[] ipAndPort = ParticipantUtils.getIpAndPort(channel.getRemoteAddress().toString());

                            if(ParseMsgUtils.getMsg(aLong) == ParseMsgUtils.PARTICIPANT_LOCAL_TX_SUCCESS){
                                ParticipantUtils.setParticipantStatus(participants , ipAndPort[0] , ipAndPort[1] , (byte)1);
                                System.out.println("ip(" + ipAndPort[0] + ") and (" + ipAndPort[1] + ") has commited success");
                            }else if(ParseMsgUtils.getMsg(aLong) == ParseMsgUtils.PARTICIPANT_LOCAL_TX_FAILED){
                                ParticipantUtils.setParticipantStatus(participants , ipAndPort[0] , ipAndPort[1] , (byte)0);
                                System.out.println("ip(" + ipAndPort[0] + ") and (" + ipAndPort[1] + ") has commited failed");
                            }
                        }
//                        if( 3L == ParticipantUtils.getCommitedParticipantCnt(participants)){
//                            ByteBuffer writeBuffer = MsgBuilder.builderCoordinatorAllCommited();
//                            writeBuffer.flip();
//                            System.out.println("all local tx finished");
//                            channel.write(writeBuffer);
//                        }
                        if(read < 0){
                            next.cancel();
                        }
                        channel.register(selector , 0).interestOps(SelectionKey.OP_WRITE);

                    }else if(next.isWritable()){
                        SocketChannel channel = (SocketChannel)next.channel();
                        if( 3L == ParticipantUtils.getCommitedParticipantCnt(participants)){
                            ByteBuffer writeBuffer = MsgBuilder.builderCoordinatorAllCommited();
                            writeBuffer.flip();
                            channel.write(writeBuffer);
                        }
                        channel.register(selector,0).interestOps(SelectionKey.OP_READ);
                    }
                }
                TimeUnit.SECONDS.sleep(1);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

下面是参与者代码:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Objects;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @className: Participant
 * @Description:
 * @Author: wangyifei
 * @Date: 2023/2/19 8:43
 */
public class Participant {
    private static Logger logger = LoggerFactory.getLogger(Participant.class);

    public static void main(String[] args) {
        try {
            InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 6666);
            SocketChannel sc = SocketChannel.open(inetSocketAddress);
            sc.configureBlocking(false);
            Selector selector = Selector.open();
            SelectionKey register = sc.register(selector, 0);
            register.interestOps(SelectionKey.OP_WRITE);
            Scanner scanner = new Scanner(System.in);
            while(true){
                selector.select();
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey next = iterator.next();
                    if (next.isReadable()) {
                        SocketChannel channel = (SocketChannel)next.channel();
                        System.out.println("read in , channel is " + channel);
                        if (Objects.isNull(channel)) {
                            continue;
                        }
                        ByteBuffer buffer = ByteBuffer.allocate(32);
                        channel.read(buffer);
                        buffer.flip();
                        long aLong = buffer.getLong();
                        System.out.println(aLong);
                        if(ParseMsgUtils.COORDINATOR_ALL_COMMITED == aLong){
                            System.out.println("all participant has finished local tx , and execution");
                        }
                    }else if(next.isWritable()){
                        System.out.println("input:");
                        scanner.nextLine();
                        SocketChannel channel = (SocketChannel)next.channel();
                        ByteBuffer writeBuffer = ByteBuffer.allocate(32);
                        ByteBuffer buffer = MsgBuilder.builderCommit();
                        buffer.flip();
                        channel.write(buffer);
                        register.interestOps(SelectionKey.OP_READ);
                    }

                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


import java.nio.ByteBuffer;

/**
 * @className: MsgBuilder
 * @Description: 实现消息的构建
 * @Author: wangyifei
 * @Date: 2023/2/19 11:29
 */
public class MsgBuilder {
    public static ByteBuffer builderCommit(){
        return builder(ParseMsgUtils.PARTICIPANT_LOCAL_TX_SUCCESS);
    }
    public static ByteBuffer builderFailed(){
        return builder(ParseMsgUtils.PARTICIPANT_LOCAL_TX_FAILED);
    }
    public static ByteBuffer builderCoordinatorAllCommited(){
        return builder(ParseMsgUtils.COORDINATOR_ALL_COMMITED);
    }
    public static ByteBuffer builderCoordinatorAllRollup(){
        return builder(ParseMsgUtils.COORDINATOR_ALL_ROLLUP);
    }
    public static ByteBuffer builder(long input){
        ByteBuffer allocate = ByteBuffer.allocate(32);
        return allocate.putLong(input);
    }
}


/**
 * @className: ParseMsgUtils
 * @Description: 对消息进行解析
 * @Author: wangyifei
 * @Date: 2023/2/19 9:46
 */
public class ParseMsgUtils {
    private static long IDENTITY_POSITION = (1<<0);
    private static long MSG_POSITION = (
            1<<1 | 1<<2 | 1<<3
            );
    /**
     * participent 执行本地事务失败
     * */
    public static long PARTICIPANT_LOCAL_TX_FAILED = 0x0000000000000000;
    public static long PARTICIPANT_LOCAL_TX_SUCCESS = 0x0000000000000002;
    public static long COORDINATOR_ALL_COMMITED = 0x0000000000000003;
    public static long COORDINATOR_ALL_ROLLUP = 0x0000000000000001;

    public static long getIdentity(long msg ){
        return msg&IDENTITY_POSITION;
    }
    public static long getMsg(long msg){
        return (msg&MSG_POSITION) ;
    }

    public static void main(String[] args) {
        System.out.println(getIdentity(11L) == 1L);
        System.out.println(getIdentity(10L) == 0L);
        // 0
        System.out.println(getMsg(0x0000000000000000) == PARTICIPANT_LOCAL_TX_FAILED);
        System.out.println(getMsg(0x0000000000000002) == PARTICIPANT_LOCAL_TX_SUCCESS);
    }
}




import java.util.Objects;

/**
 * @className: ParticipantNode
 * @Description:代表参与者的 bean 类
 * 记录参与的信息
 * @Author: wangyifei
 * @Date: 2023/2/19 9:35
 */
public class ParticipantNode {
    private String IP ;
    private String port ;
    private long lastestTouchTimestamp ;
    private long timeout;
    private byte txStatus ;

    public byte getTxStatus() {
        return txStatus;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        ParticipantNode that = (ParticipantNode) o;
        return Objects.equals(IP, that.IP) && Objects.equals(port, that.port);
    }

    @Override
    public int hashCode() {
        return Objects.hash(IP, port);
    }

    public void setTxStatus(byte txStatus) {
        this.txStatus = txStatus;
    }

    public ParticipantNode(String IP, String port, long lastestTouchTimestamp, long timeout) {
        this.IP = IP;
        this.port = port;
        this.lastestTouchTimestamp = lastestTouchTimestamp;
        this.timeout = timeout;
    }
    public ParticipantNode(String IP, String port, long timeout) {
        this(IP, port , 0 , timeout);
    }

    public String getIP() {
        return IP;
    }

    public void setIP(String IP) {
        this.IP = IP;
    }

    public String getPort() {
        return port;
    }

    public void setPort(String port) {
        this.port = port;
    }

    public long getLastestTouchTimestamp() {
        return lastestTouchTimestamp;
    }

    public void setLastestTouchTimestamp(long lastestTouchTimestamp) {
        this.lastestTouchTimestamp = lastestTouchTimestamp;
    }

    public long getTimeout() {
        return timeout;
    }

    public void setTimeout(long timeout) {
        this.timeout = timeout;
    }

    @Override
    public String toString() {
        return "ParticipantNode{" +
                "IP='" + IP + '\'' +
                ", port='" + port + '\'' +
                ", txStatus=" + txStatus +
                '}';
    }
}

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;

/**
 * @className: ParticipantUtils
 * @Description: 对参与者进行操作的工具类
 * @Author: wangyifei
 * @Date: 2023/2/19 10:45
 */
public class ParticipantUtils {
    public static String[] getIpAndPort(String input){
        String[] split = input.split(":");
        split[0] = split[0].replace("/","") ;
        return split ;
    }
    public static void setParticipantStatus(List<ParticipantNode> list
            , String ip , String port , byte status ){
        if(Objects.isNull(list) || list.isEmpty()){
            throw new RuntimeException("list is empty");
        }
        list.stream().filter(x -> {
            boolean rs = (x.getIP().equals(ip) && x.getPort().equals(port));
            return rs ;
        }).forEach(x->{
            x.setTxStatus(status);
        });
    }
    public static long getCommitedParticipantCnt(List<ParticipantNode> list){
        long count = list.stream().filter(x->x.getTxStatus()==(byte) 1).count();
        return count ;
    }
    public static void main(String[] args) {
        List<ParticipantNode> list = new ArrayList<>();
        list.add(new ParticipantNode("127.0.0.1" , "9999" , System.currentTimeMillis()));
        list.add(new ParticipantNode("127.0.0.1" , "8888" , System.currentTimeMillis()));
        list.add(new ParticipantNode("127.0.0.1" , "7777" , System.currentTimeMillis()));
        setParticipantStatus(list , "127.0.0.1" , "8888" , (byte) 1);
        list.forEach(x -> {
            System.out.println(x);
        });
        for (String s : getIpAndPort("/127.0.0.1:9999")) {
            System.out.println(s);
        }
    }
}


import java.util.List;

/**
 * @className: TXGeneration
 * @Description: 未使用到,用来标记不同年代
 *   事务年代
 * @Author: wangyifei
 * @Date: 2023/2/19 10:20
 */
public class TXGeneration {
    private long epoch ;
    List<Participant> participant ;
}

流程图

mermaid

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值