package com.i9i.raft;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Map;
import java.util.Random;
import java.util.TreeMap;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.i9i.raft.data.ElectItem;
import com.i9i.raft.data.Message;
import com.i9i.raft.data.VoteItem;
import com.i9i.raft.enums.MessageType;
import com.i9i.raft.enums.NodeType;
import com.i9i.raft.net.UdpNet;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
public class Node {
private Integer id;
private String name;
private String host;
private Integer port;
private volatile ConcurrentHashMap<Integer, AtomicInteger> voteForResult = new ConcurrentHashMap<Integer, AtomicInteger>(
16);
private volatile Integer leaderId;
private volatile Integer term = 0;
private volatile Integer termIndex = 0;
private volatile Integer voteFor = null;
private volatile Long lastHeartBeatTime;
private volatile Long lastElectLeaderTime;
private Map<Integer, String> clusterIps;
private Map<Long, String> log = new TreeMap<>();
private volatile NodeType nodeType = NodeType.Follower;
private volatile FutureTask<Object> electThread;
DatagramSocket datagramSocket = null;
ExecutorService executor=Executors.newSingleThreadExecutor();
public void init() {
new Thread(new MessageReceiveTask(this)).start();
// new Thread(new CheckHeartBeatTask(this)).start();
electThread=new FutureTask<>(new ElectCallable(this));
java.util.Random r = new Random();
try {
TimeUnit.MICROSECONDS.sleep(r.nextLong(50, 50+id*500));
} catch (InterruptedException e) {
e.printStackTrace();
}
if(lastHeartBeatTime!=null&&System.currentTimeMillis()-lastHeartBeatTime<3000*1000)
return;
Executors.newSingleThreadExecutor().submit(electThread);
try {
datagramSocket = new DatagramSocket();
} catch (SocketException e) {
e.printStackTrace();
}
}
public void createElect() throws Exception {
java.util.Random r = new Random();
TimeUnit.MILLISECONDS.sleep(r.nextLong(getId() * 1000L));
setVoteFor(id);
setNodeType(NodeType.Candidate);
setLastElectLeaderTime(System.currentTimeMillis());
Message<ElectItem> electMessage = new Message<>();
electMessage.setMessageType(MessageType.ELECT);
electMessage.setTerm(getTerm() + 1);
electMessage.setTermIndex(1);
electMessage.setFrom(getId());
electMessage.setData(new ElectItem().setTerm(electMessage.getTerm()).setVoteFor(getId()));
System.out.println(getId() +" term:"+getTerm()+ ": create elect leader");
voteForResult.put(id, new AtomicInteger(1));
for (Integer key : getClusterIps().keySet()) {
if(key==id)
continue;
electMessage.setTo(key);
String clusterIp =getClusterIps().get(key);
String[] ipPort = clusterIp.split(":");
UdpNet.send(ipPort[0], Integer.valueOf(ipPort[1]), electMessage);
}
}
public <T> void board(Message<T> message) throws Exception {
byte[] bytes = message.toBytes();
try {
for (Integer key : clusterIps.keySet()) {
message.setTo(key);
String[] ipport = clusterIps.get(key).split(":");
InetAddress addr = Inet4Address.getByName(ipport[0]);
DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length, addr, Integer.valueOf(ipport[1]));
datagramSocket.send(packet);
packet=null;
}
} catch (SocketException e) {
throw new RuntimeException(e);
} finally {
// if (datagramSocket != null)
// datagramSocket.close();
}
}
public <T> void send(int nodeId,Message<T> message) throws Exception {
byte[] bytes = message.toBytes();
try {
message.setTo(nodeId);
String[] ipport = clusterIps.get(nodeId).split(":");
InetAddress addr = Inet4Address.getByName(ipport[0]);
DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length, addr, Integer.valueOf(ipport[1]));
datagramSocket.send(packet);
} catch (SocketException e) {
throw new RuntimeException(e);
} finally {
// if (datagramSocket != null)
// datagramSocket.close();
// datagramSocket.close();
}
}
class ElectCallable implements Callable<Object> {
private Node node;
ElectCallable(Node node) {
this.node = node;
}
@Override
public Object call() throws Exception {
TimeUnit.SECONDS.sleep(2);
node.createElect();
return null;
}
}
class CheckHeartBeatTask implements RaftTask {
private volatile Node node;
public CheckHeartBeatTask(Node node) {
this.node = node;
}
/**
* Runs this operation.
*/
@Override
public void run() {
boolean first = true;
while (true) {
if (node.getLastHeartBeatTime() == null) {
node.setLastHeartBeatTime(System.currentTimeMillis());
try {
TimeUnit.MILLISECONDS.sleep(500);
first = false;
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
continue;
}
// System.out.println(node.getId()+"time
// gap"+(System.currentTimeMillis()-node.getLastHeartBeatTime()));
if (System.currentTimeMillis() - node.getLastHeartBeatTime() > 30 * 1000 && node.getVoteFor() == null) {
try {
node.createElect();
} catch (Exception e) {
throw new RuntimeException(e);
}
} else {
try {
TimeUnit.MILLISECONDS.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
// public void createElect() throws Exception {
// java.util.Random r = new Random();
// TimeUnit.MILLISECONDS.sleep(r.nextLong(node.getId() * 1000L));
// if (node.getLastElectLeaderTime() != null
// && System.currentTimeMillis() - node.getLastElectLeaderTime() > 30000) {
// return;
// }
// node.setVoteFor(node.getId());
// node.setNodeType(NodeType.Candidate);
// node.setLastElectLeaderTime(System.currentTimeMillis());
// Message<ElectItem> electMessage = new Message<>();
// electMessage.setMessageType(MessageType.ELECT);
// electMessage.setTerm(node.getTerm() + 1);
// electMessage.setTermIndex(1);
// electMessage.setFrom(node.getId());
// electMessage.setData(new ElectItem().setTerm(electMessage.getTerm()).setVoteFor(node.getId()));
// System.out.println(node.getId() + ": create elect leader");
// for (Integer key : node.getClusterIps().keySet()) {
// electMessage.setTo(key);
// String clusterIp = node.getClusterIps().get(key);
// String[] ipPort = clusterIp.split(":");
// UdpNet.send(ipPort[0], Integer.valueOf(ipPort[1]), electMessage);
// }
//
// }
}
static class MessageReceiveTask implements RaftTask {
private final Node node;
DatagramSocket datagramSocket;
public MessageReceiveTask(Node node) {
this.node = node;
try {
String[] ipPorts = node.getClusterIps().get(node.getId()).split(":");
datagramSocket = new DatagramSocket(Integer.valueOf(ipPorts[1]));
} catch (SocketException e) {
throw new RuntimeException(e);
}
}
/**
* Runs this operation.
*/
@Override
public void run() {
while (true) {
try {
byte[] bytes = new byte[1024];
DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length);
datagramSocket.receive(packet);
Message<JSONObject> message = Message.fromBytes(packet.getData(), packet.getLength());
if (!checkMessage(message))
continue;
// System.out.println(
// node.getId() + " nodeType:" + node.getNodeType() + " leaderId:" + node.getLeaderId()
// + "--receive type:" + message.getMessageType().name() + " content:" + message);
switch (message.getMessageType()) {
case HEARTBEAT:
heartBeat(message);
break;
case ELECT:
elect(message);
break;
case VOTE:
vote(message);
break;
default:
System.out.println(message);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
// 处理收到的心跳信息,执行修改最后心跳时间
private void heartBeat(Message<JSONObject> message) {
if (node.electThread != null) {
node.electThread.cancel(true);
node.electThread=null;
// System.out.println("task cancel");
}
node.setLastHeartBeatTime(System.currentTimeMillis());
node.setTerm(message.getTerm());
node.setTermIndex(message.getTermIndex());
if(node.getLeaderId()==null) {
System.out.println(node.getId()+"收到心跳");
node.setLeaderId(message.getFrom());
node.setVoteFor(null);
}
node.electThread = new FutureTask<>(node.new ElectCallable(node));
try {
node.getExecutor().submit(node.electThread);
// System.out.println("task add");
} catch (Exception e) {
e.printStackTrace();
}
}
private void elect(Message<JSONObject> message) {
if (node.getTerm() > message.getTerm() || node.getTerm() == message.getTerm() && node.getVoteFor() != null)
return;
node.setTerm(message.getTerm());
node.setTermIndex(message.getTermIndex());
node.setLeaderId(null);
JSONObject jo = message.getData();
ElectItem electItem = JSON.to(ElectItem.class, jo);
// 发起投票
node.setVoteFor(electItem.getVoteFor());
Message<VoteItem> voteMessage = new Message<>();
voteMessage.setFrom(node.getId());
voteMessage.setMessageType(MessageType.VOTE);
voteMessage.setTerm(message.getTerm());
voteMessage.setTermIndex(message.getTermIndex() + 1);
VoteItem item = new VoteItem().setLeaderId(electItem.getVoteFor());
voteMessage.setData(item);
try {
node.send(message.getFrom(), voteMessage);
} catch (Exception e) {
e.printStackTrace();
}
}
private void vote(Message<JSONObject> message) {
JSONObject jo = message.getData();
VoteItem item = JSON.to(VoteItem.class, jo);
ConcurrentHashMap<Integer, AtomicInteger> r = node.getVoteForResult();
if (r.get(item.getLeaderId()) != null) {
int voteCount = r.get(item.getLeaderId()).incrementAndGet();
System.out.println(node.getId() + "当前得"+voteCount+"票 from:"+message.getFrom());
if (voteCount > node.getClusterIps().size() / 2) {
System.out.println(node.getId() + "选举成功! leaderId:" + item.getLeaderId());
node.setVoteFor(null);
if (node.getId() == item.getLeaderId() && node.getLeaderId() == null) {
node.setLeaderId(item.getLeaderId());
node.setNodeType(NodeType.Leader);
node.setVoteFor(null);
node.setLastElectLeaderTime(System.currentTimeMillis());
new Thread(new HeartTask(node)).start();
} else if (node.getId() != item.getLeaderId() && node.getNodeType() == NodeType.Leader) {
node.setNodeType(NodeType.Follower);
node.setLeaderId(item.getLeaderId());
node.setVoteFor(null);
node.setLastElectLeaderTime(System.currentTimeMillis());
} else {
node.setNodeType(NodeType.Follower);
node.setLeaderId(item.getLeaderId());
node.setVoteFor(null);
node.setLastElectLeaderTime(System.currentTimeMillis());
}
}
} else {
r.put(item.getLeaderId(), new AtomicInteger(1));
System.out.println(node.getId() + "获得1票 from:"+message.getFrom());
}
}
private boolean checkMessage(Message message) {
if (message.getTerm() < node.getTerm()) {
return false;
}
if (message.getTerm() > node.getTerm()) {
node.setTerm(message.getTerm());
node.setTermIndex(message.getTermIndex());
}
return true;
}
}
}
package com.i9i.raft;
import java.util.LinkedHashMap;
import java.util.Map;
public class Server {
public static void main(String[] args) {
// Map<Integer, String> clusterIps = Map.of(1, "127.0.0.1:8891", 2, "127.0.0.1:8892", 3, "127.0.0.1:8893");
Map<Integer, String> clusterIps = new LinkedHashMap<>();
int nodes=3;
for(int i=1;i<=nodes;i++) {
clusterIps.put(i, "127.0.0.1:"+(8890+i));
}
for (int i = 1; i <=nodes; i++) {
Node node = new Node();
node.setId(i);
node.setClusterIps(clusterIps);
node.init();
}
}
}