事务是什么?
一个完整的,不可分割的操作单元
要么都做完,要么都不成功
什么是分布式事务
分布式事务在分布式系统中的事务。它不是单机中完成操作单元,在一个事务中可能有若干个服务器参与。
两阶段提交
两阶段提交就是一个分布式事务的解决方案。
第一个阶段:提交阶段,所有的参与者发送本地操作是否完成,如果完成则给协调者发送操作完成的消息,否则发送失败的消息,
协调者等待参与者失败或者成功的消息,或者等待超时。第一阶段结束。
第二阶段:执行阶段。当协调者收到所有参与者执行成功的消息后,发送给参与者可以完成本地操作。
如果协调者收到一个参与者执行失败的消息或者超时,则发送给参与者回滚本地操作的消息。
使用 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 ;
}