├── LICENSE ├── README.md ├── RocksRaft.iml ├── log4j.properties ├── log4j.xml.bak ├── pom.xml ├── src ├── main │ ├── java │ │ ├── Client.java │ │ ├── ClientExample.java │ │ ├── Start.java │ │ ├── Start2.java │ │ ├── Start3.java │ │ ├── com │ │ │ └── google │ │ │ │ └── protobuf │ │ │ │ ├── BytesCarrier.java │ │ │ │ └── ZeroByteStringHelper.java │ │ ├── config │ │ │ ├── LogManagerOptions.java │ │ │ ├── LogStorageOptions.java │ │ │ ├── RaftOptionsLoader.java │ │ │ └── ReplicatorOptions.java │ │ ├── core │ │ │ ├── CustomStateMachine.java │ │ │ ├── FSMCaller.java │ │ │ ├── FSMCallerImpl.java │ │ │ ├── HeartbeatThreadFactory.java │ │ │ ├── IteratorImpl.java │ │ │ ├── IteratorWrapper.java │ │ │ ├── Lifecycle.java │ │ │ ├── LogEntryCodecFactory.java │ │ │ ├── LogManagerImpl.java │ │ │ ├── LogManagerImplNew.java │ │ │ ├── LogStorageImpl.java │ │ │ ├── NodeImpl.java │ │ │ ├── NodeManager.java │ │ │ ├── NodeManagerImpl.java │ │ │ ├── RaftGroupService.java │ │ │ ├── ReadIteratorImpl.java │ │ │ ├── Replicator.java │ │ │ ├── ReplicatorGroup.java │ │ │ ├── ReplicatorGroupImpl.java │ │ │ ├── ReplicatorStateListener.java │ │ │ ├── RocksDBStorage.java │ │ │ ├── RocksDBStorageImpl.java │ │ │ ├── StateMachine.java │ │ │ └── StateMachineAdapter.java │ │ ├── entity │ │ │ ├── Ballot.java │ │ │ ├── BallotBox.java │ │ │ ├── BallotBoxForApply.java │ │ │ ├── Checksum.java │ │ │ ├── Closure.java │ │ │ ├── ConfigurationEntry.java │ │ │ ├── CurrentNodeOptions.java │ │ │ ├── ElectionTimeOutClosure.java │ │ │ ├── Endpoint.java │ │ │ ├── FSMCallerOptions.java │ │ │ ├── Heartbeat.java │ │ │ ├── Iterator.java │ │ │ ├── KVEntity.java │ │ │ ├── LeaderChangeContext.java │ │ │ ├── LogEntry.java │ │ │ ├── LogEntryCodecFactory.java │ │ │ ├── LogEntryDecoder.java │ │ │ ├── LogEntryEncoder.java │ │ │ ├── LogEntryEvent.java │ │ │ ├── LogId.java │ │ │ ├── LogManager.java │ │ │ ├── Node.java │ │ │ ├── NodeId.java │ │ │ ├── NodeOptions.java │ │ │ ├── Options.java │ │ │ ├── OtherNodes.java │ │ │ ├── PeerId.java │ │ │ ├── RaftError.java │ │ │ ├── ReadEvent.java │ │ │ ├── ReadTask.java │ │ │ ├── ReadTaskResponse.java │ │ │ ├── ReadTaskSchedule.java │ │ │ ├── ReadTaskWaiter.java │ │ │ ├── ReplicatorGroupOptions.java │ │ │ ├── ReplicatorType.java │ │ │ ├── RocksBatch.java │ │ │ ├── RpcOptions.java │ │ │ ├── RpcResult.java │ │ │ ├── StateMachine.java │ │ │ ├── Status.java │ │ │ ├── Task.java │ │ │ ├── TimeOutChecker.java │ │ │ └── TimeOutClosure.java │ │ ├── exceptions │ │ │ ├── ElectionException.java │ │ │ ├── LogExceptionHandler.java │ │ │ ├── LogStorageException.java │ │ │ └── RaftException.java │ │ ├── rpc │ │ │ ├── ClientRpcService.java │ │ │ ├── ClientRpcServiceImpl.java │ │ │ ├── EnClosureClientRpcRequest.java │ │ │ ├── EnClosureRpcRequest.java │ │ │ ├── EnumOutter.java │ │ │ ├── LogEntryCodecFactory.java │ │ │ ├── LogEntryEncoder.java │ │ │ ├── LogEntryEncoderV2.java │ │ │ ├── LogEntryV2CodecFactory.java │ │ │ ├── LogOuter.java │ │ │ ├── RaftOutter.java │ │ │ ├── RaftRpcServerFactory.java │ │ │ ├── RpcRequestClosure.java │ │ │ ├── RpcRequests.java │ │ │ ├── RpcResponseClosure.java │ │ │ ├── RpcResponseFactory.java │ │ │ ├── RpcServices.java │ │ │ ├── RpcServicesImpl.java │ │ │ ├── TaskRpcResponseClosure.java │ │ │ ├── TaskRpcServices.java │ │ │ ├── TaskServicesImpl.java │ │ │ └── processor │ │ │ │ └── AppendEntriesRequestProcessor.java │ │ ├── service │ │ │ ├── ElectionService.java │ │ │ ├── ElectionServiceImpl.java │ │ │ ├── RaftServerService.java │ │ │ ├── RaftServiceFactory.java │ │ │ └── RaftServiceFactoryImpl.java │ │ ├── storage │ │ │ └── LogStorage.java │ │ └── utils │ │ │ ├── ArrayDeque.java │ │ │ ├── AsciiStringUtil.java │ │ │ ├── Bits.java │ │ │ ├── BootYaml.java │ │ │ ├── CRC64.java │ │ │ ├── CatchUpClosure.java │ │ │ ├── Copiable.java │ │ │ ├── CrcUtil.java │ │ │ ├── DisruptorBuilder.java │ │ │ ├── Ints.java │ │ │ ├── LogThreadPoolExecutor.java │ │ │ ├── NonReentrantLock.java │ │ │ ├── RandomTimeUtil.java │ │ │ ├── Recyclable.java │ │ │ ├── RecycleUtil.java │ │ │ ├── Recyclers.java │ │ │ ├── Requires.java │ │ │ ├── SegmentList.java │ │ │ ├── ThreadId.java │ │ │ ├── ThreadPoolUtil.java │ │ │ ├── TimeHelper.java │ │ │ ├── TimerManager.java │ │ │ ├── Utils.java │ │ │ └── ZeroByteStringHelper.java │ └── resources │ │ ├── META-INF │ │ └── services │ │ │ └── service.RaftServiceFactory │ │ ├── log4j.properties │ │ ├── log4j.xml.bak │ │ ├── properties.yml │ │ ├── properties2.yml │ │ ├── properties3.yml │ │ └── protobuf │ │ ├── RPC.proto │ │ ├── enum.proto │ │ ├── logOuter.proto │ │ ├── raft.proto │ │ └── rpc.bat └── test │ └── java │ ├── TestHeartbeatChecker.java │ └── TestTimeManager.java └── 论文github开源版.pdf /README.md: -------------------------------------------------------------------------------- 1 | # RocksRaft 2 | 3 | [![LICENSE](https://img.shields.io/hexpm/l/plug)](https://github.com/CoderiGenius/RocksRaft/blob/master/LICENSE) 4 | 5 | - 基于Raft一致性协议的分布式存储系统,参考阿里巴巴SOFAJRaft并使用Java从零实现。 6 | - Distributed storage system based on Raft consistency protocol, referencing Alibaba SOFAJRaft and implemented from scratch using Java 7 | ## Reference 8 | - Alibaba [SOFAJRaft](https://github.com/sofastack/sofa-jraft) 9 | ## Related papers 10 | - [Graduation thesis of QUST in 2020 for bachelor's degree ](https://github.com/CoderiGenius/RocksRaft/blob/master/%E8%AE%BA%E6%96%87github%E5%BC%80%E6%BA%90%E7%89%88.pdf) 11 | ## Quick start 12 | - Basic requirement 13 | - JDK 1.8+ 14 | - IntelliJ idea 15 | - maven 16 | - Clone project 17 | ```` 18 | git clone https://github.com/CoderiGenius/RocksRaft.git 19 | ```` 20 | - Import in to idea as maven project 21 | - Config and start the example of 3 nodes scenario. 22 | - Config the three yml files in **src/main/resources** 23 | - Start the main function with files listed below 24 | - src/main/java/Start.java 25 | - src/main/java/Start2.java 26 | - src/main/java/Start3.java 27 | - Wait until the nodes come up. 28 | - Run the src/main/java/ClientExample.java 29 | - You should be able to see the log of appendEnties and the procedures of applying to statemachine. 30 | - Log example: 31 | ``` 32 | WARN:appendEntriesRequest CHECK:index 0 dataIsEmpty:false 33 | WARN:checkBallotBoxToApply applied:Voting currentIndex:0 length:1 stableLogIndex:0 34 | INFO:Ballot box invokes apply at index 0 length 1 with grant list [PeerId{endpoint=localhost:12220, checksum=0}, PeerId{endpoint=localhost:12230, checksum=0}] 35 | INFO:doCommitted at 1 iterImpl:IteratorImpl [fsm=core.CustomStateMachine@22d116d, logManager=core.LogManagerImplNew@61295f0, currentIndex=1, committedIndex=1, currEntry=entity.LogEntry@ceafe80, applyingIndex=1, error=null,isGood=true] isGood:true 36 | INFO:Apply to fsm at 1 37 | INFO:Log 1 has been applied to stateMachine 38 | INFO:Receive follower raft-3 applied firstIndex :0 lastIndex:0 39 | INFO:Receive follower raft-1 applied firstIndex :0 lastIndex:0 40 | ``` 41 | -------------------------------------------------------------------------------- /RocksRaft.iml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /log4j.properties: -------------------------------------------------------------------------------- 1 | ### set log levels ### 2 | log4j.rootLogger=DEBUG 3 | 4 | ### direct log messages to stdout ### 5 | log4j.appender.A1=org.apache.log4j.ConsoleAppender 6 | log4j.appender.A1.Target=System.out 7 | log4j.appender.A1.layout=org.apache.log4j.PatternLayout 8 | log4j.appender.A1.layout.ConversionPattern=%-2p %m%n 9 | 10 | ### direct messages to file framework.log ### 11 | log4j.appender.A2=org.apache.log4j.DailyRollingFileAppender 12 | ##log4j.appender.A2.File=D:/logs/resmanm.log 13 | log4j.appender.A2.DatePattern='.'yyyy-MM-dd 14 | log4j.appender.A2.layout=org.apache.log4j.PatternLayout 15 | log4j.appender.A2.layout.ConversionPattern=%-5p(%10c{1}) %m%n 16 | 17 | ### application log config ### 18 | #log4j.logger.com.linkage=ERROR,A2 19 | log4j.logger.com.ch1=DEBUG,A1,A2 20 | ##log4j.logger.org.quartz.impl.StdSchedulerFactory=DEBUG,A1,A2 -------------------------------------------------------------------------------- /src/main/java/Client.java: -------------------------------------------------------------------------------- 1 | import com.alipay.sofa.rpc.config.ConsumerConfig; 2 | import com.alipay.sofa.rpc.config.ProviderConfig; 3 | import com.alipay.sofa.rpc.config.ServerConfig; 4 | import core.CustomStateMachine; 5 | import entity.KVEntity; 6 | import entity.ReadTask; 7 | import entity.Task; 8 | import org.joda.time.DateTime; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import rpc.*; 12 | import utils.Utils; 13 | 14 | import java.io.ByteArrayOutputStream; 15 | import java.io.IOException; 16 | import java.io.ObjectOutputStream; 17 | import java.nio.ByteBuffer; 18 | import java.util.concurrent.ExecutorService; 19 | import java.util.concurrent.Executors; 20 | import java.util.concurrent.atomic.AtomicInteger; 21 | import java.util.concurrent.atomic.AtomicLong; 22 | import java.util.zip.DataFormatException; 23 | 24 | /** 25 | * Created by 周思成 on 2020/4/25 12:28 26 | */ 27 | 28 | public class Client { 29 | private static final Logger LOG = LoggerFactory.getLogger(Client.class); 30 | 31 | public static boolean flag = true; 32 | public static AtomicInteger atomicIntegerForTansactions = new AtomicInteger(0); 33 | public static void main(String[] args) throws IOException, InterruptedException { 34 | 35 | ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); 36 | 37 | ServerConfig serverConfig = new ServerConfig() 38 | .setProtocol("bolt") 39 | .setPort(12299) 40 | .setDaemon(false); 41 | 42 | ProviderConfig providerConfig = new ProviderConfig() 43 | .setInterfaceId(ClientRpcService.class.getName()) 44 | .setRef(new ClientRpcServiceImpl()) 45 | .setServer(serverConfig); 46 | 47 | providerConfig.export(); 48 | 49 | 50 | ConsumerConfig consumerConfigForTasks = new ConsumerConfig() 51 | .setInvokeType("callback") 52 | .setOnReturn(new TaskRpcResponseClosure()) 53 | .setProtocol("bolt") 54 | .setConnectTimeout(2000) 55 | .setDirectUrl("bolt" 56 | //+ "://" + "172.24.234.215" + ":" + 12221) 57 | + "://" + "localhost" + ":" + 12221) 58 | .setInterfaceId(TaskRpcServices.class.getName()); 59 | 60 | long startTime = Utils.monotonicMs(); 61 | 62 | 63 | TaskRpcServices taskServices = consumerConfigForTasks.refer(); 64 | 65 | 66 | 67 | 68 | 69 | 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/Start.java: -------------------------------------------------------------------------------- 1 | import core.NodeImpl; 2 | import core.RaftGroupService; 3 | import entity.Endpoint; 4 | import entity.Node; 5 | import entity.NodeOptions; 6 | import entity.PeerId; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | /** 11 | * Created by 周思成 on 2020/3/13 23:32 12 | */ 13 | 14 | public class Start { 15 | public static void main(String[] args) throws InterruptedException { 16 | 17 | 18 | Runnable runnable = () -> { 19 | NodeOptions nodeOptions = NodeOptions.getNodeOptions(); 20 | 21 | RaftGroupService raftGroupService = 22 | new RaftGroupService( nodeOptions 23 | ,Start.class.getResource("properties.yml").getPath()); 24 | 25 | try { 26 | Node node = raftGroupService.start(); 27 | } catch (InterruptedException e) { 28 | e.printStackTrace(); 29 | } 30 | }; 31 | Thread thread = new Thread(runnable); 32 | 33 | thread.start(); 34 | 35 | 36 | Thread.currentThread().join(); 37 | 38 | 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/Start2.java: -------------------------------------------------------------------------------- 1 | import core.RaftGroupService; 2 | import entity.Node; 3 | import entity.NodeOptions; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | /** 8 | * Created by 周思成 on 2020/3/13 23:32 9 | */ 10 | 11 | public class Start2 { 12 | public static void main(String[] args) throws InterruptedException { 13 | 14 | 15 | 16 | Runnable runnable2 = () -> { 17 | NodeOptions nodeOptions2 = NodeOptions.getNodeOptions(); 18 | 19 | RaftGroupService raftGroupService2 = new RaftGroupService( nodeOptions2,Start.class.getResource("properties2.yml").getPath()); 20 | try { 21 | Node node2 = raftGroupService2.start(); 22 | } catch (InterruptedException e) { 23 | e.printStackTrace(); 24 | } 25 | }; 26 | Thread thread2 = new Thread(runnable2); 27 | 28 | thread2.start(); 29 | 30 | Thread.currentThread().join(); 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | //// 使用 node 提交任务 41 | // Task task = .... 42 | // node.apply(task); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/Start3.java: -------------------------------------------------------------------------------- 1 | import core.RaftGroupService; 2 | import entity.Node; 3 | import entity.NodeOptions; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | /** 8 | * Created by 周思成 on 2020/3/13 23:32 9 | */ 10 | 11 | public class Start3 { 12 | public static void main(String[] args) throws InterruptedException { 13 | 14 | 15 | Runnable runnable3 = () -> { 16 | NodeOptions nodeOptions3 = NodeOptions.getNodeOptions(); 17 | 18 | RaftGroupService raftGroupService3 = new RaftGroupService( nodeOptions3,Start.class.getResource("properties3.yml").getPath()); 19 | try { 20 | Node node3 = raftGroupService3.start(); 21 | } catch (InterruptedException e) { 22 | e.printStackTrace(); 23 | } 24 | }; 25 | Thread thread3 = new Thread(runnable3); 26 | 27 | thread3.start(); 28 | 29 | Thread.currentThread().join(); 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | //// 使用 node 提交任务 40 | // Task task = .... 41 | // node.apply(task); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/google/protobuf/BytesCarrier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package com.google.protobuf; 18 | 19 | import java.io.IOException; 20 | import java.nio.ByteBuffer; 21 | 22 | /** 23 | * 24 | * @author jiachun.fjc 25 | */ 26 | public class BytesCarrier extends ByteOutput { 27 | 28 | private byte[] value; 29 | private boolean valid; 30 | 31 | public byte[] getValue() { 32 | return value; 33 | } 34 | 35 | public boolean isValid() { 36 | return valid; 37 | } 38 | 39 | @Override 40 | public void write(final byte value) { 41 | this.valid = false; 42 | } 43 | 44 | @Override 45 | public void write(final byte[] value, final int offset, final int length) { 46 | doWrite(value, offset, length); 47 | } 48 | 49 | @Override 50 | public void writeLazy(final byte[] value, final int offset, final int length) { 51 | doWrite(value, offset, length); 52 | } 53 | 54 | @Override 55 | public void write(final ByteBuffer value) throws IOException { 56 | this.valid = false; 57 | } 58 | 59 | @Override 60 | public void writeLazy(final ByteBuffer value) throws IOException { 61 | this.valid = false; 62 | } 63 | 64 | private void doWrite(final byte[] value, final int offset, final int length) { 65 | if (this.value != null) { 66 | this.valid = false; 67 | return; 68 | } 69 | if (offset == 0 && length == value.length) { 70 | this.value = value; 71 | this.valid = true; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/config/LogManagerOptions.java: -------------------------------------------------------------------------------- 1 | package config; 2 | 3 | import entity.LogManager; 4 | import entity.Options; 5 | import storage.LogStorage; 6 | 7 | /** 8 | * Created by 周思成 on 2020/4/7 20:28 9 | */ 10 | 11 | public class LogManagerOptions { 12 | 13 | private LogStorage logStorage; 14 | private Options options; 15 | private int disruptorBufferSize = 1024; 16 | 17 | public LogManagerOptions(LogStorage logStorage, Options options) { 18 | this.logStorage = logStorage; 19 | this.options = options; 20 | 21 | } 22 | 23 | public int getDisruptorBufferSize() { 24 | return disruptorBufferSize; 25 | } 26 | 27 | public void setDisruptorBufferSize(int disruptorBufferSize) { 28 | this.disruptorBufferSize = disruptorBufferSize; 29 | } 30 | 31 | public void setLogStorage(LogStorage logStorage) { 32 | this.logStorage = logStorage; 33 | } 34 | 35 | public Options getOptions() { 36 | return options; 37 | } 38 | 39 | public void setOptions(Options options) { 40 | this.options = options; 41 | } 42 | 43 | public LogStorage getLogStorage() { 44 | return this.logStorage; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/config/LogStorageOptions.java: -------------------------------------------------------------------------------- 1 | package config; 2 | 3 | import core.LogManagerImpl; 4 | import org.rocksdb.Options; 5 | import org.rocksdb.RocksDB; 6 | import org.rocksdb.RocksDBException; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | /** 11 | * Created by 周思成 on 2020/4/7 20:53 12 | */ 13 | 14 | public class LogStorageOptions { 15 | private static final Logger LOG = LoggerFactory 16 | .getLogger(LogStorageOptions.class); 17 | 18 | 19 | private String logStoragePath; 20 | private String logStorageName; 21 | 22 | public LogStorageOptions(String logStoragePath, String logStorageName) { 23 | this.logStoragePath = logStoragePath; 24 | this.logStorageName = logStorageName; 25 | } 26 | 27 | public String getLogStoragePath() { 28 | return logStoragePath; 29 | } 30 | 31 | public void setLogStoragePath(String logStoragePath) { 32 | this.logStoragePath = logStoragePath; 33 | } 34 | 35 | public String getLogStorageName() { 36 | return logStorageName; 37 | } 38 | 39 | public void setLogStorageName(String logStorageName) { 40 | this.logStorageName = logStorageName; 41 | } 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/config/RaftOptionsLoader.java: -------------------------------------------------------------------------------- 1 | package config; 2 | 3 | import core.NodeImpl; 4 | import entity.*; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import utils.BootYaml; 8 | 9 | import java.io.FileNotFoundException; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.concurrent.ConcurrentHashMap; 13 | import java.util.concurrent.CopyOnWriteArrayList; 14 | 15 | /** 16 | * Created by 周思成 on 2020/3/13 12:03 17 | */ 18 | 19 | public class RaftOptionsLoader { 20 | private static final Logger LOG = LoggerFactory.getLogger(RaftOptionsLoader.class); 21 | 22 | public RaftOptionsLoader(String name) throws FileNotFoundException { 23 | Options options = BootYaml.getYaml(name); 24 | NodeImpl.getNodeImple().setOptions(options); 25 | LOG.info("load raftOptions from {} options:{}",name,options.toString()); 26 | CurrentNodeOptions currentNodeOptions = options.getCurrentNodeOptions(); 27 | OtherNodes[] otherNodes = options.getOtherNodes(); 28 | 29 | NodeOptions.getNodeOptions().setDaemon(currentNodeOptions.isDaemon()); 30 | NodeOptions.getNodeOptions().setElectionTimeOut(currentNodeOptions.getElectionTimeOut()); 31 | NodeOptions.getNodeOptions().setMaxHeartBeatTime(currentNodeOptions.getMaxHeartBeatTime()); 32 | NodeOptions.getNodeOptions().setRpcProtocol(currentNodeOptions.getRpcProtocol()); 33 | NodeOptions.getNodeOptions().setSerialization(currentNodeOptions.getSerialization()); 34 | NodeOptions.getNodeOptions().setPort(currentNodeOptions.getPort()); 35 | NodeOptions.getNodeOptions().setTaskPort(currentNodeOptions.getTaskPort()); 36 | NodeOptions.getNodeOptions().setPeerId(currentNodeOptions.getPeerId()); 37 | NodeOptions.getNodeOptions().setClientAddress(currentNodeOptions.getClientAddress()); 38 | NodeOptions.getNodeOptions().setClientPort(currentNodeOptions.getClientPort()); 39 | Endpoint endpoint = new Endpoint(currentNodeOptions.getAddress(),currentNodeOptions.getPort()); 40 | PeerId peerId = new PeerId(); 41 | peerId.setPeerName(currentNodeOptions.getName()); 42 | peerId.setId(currentNodeOptions.getPeerId()); 43 | peerId.setEndpoint(endpoint); 44 | NodeId nodeId = new NodeId(currentNodeOptions.getGroupId(),peerId); 45 | List listOtherNode = new CopyOnWriteArrayList<>(); 46 | Map peerIdMap = new ConcurrentHashMap<>(); 47 | for (OtherNodes o:otherNodes 48 | ) { 49 | 50 | PeerId peerId1 = new PeerId(o.getPeerId(),o.getName() 51 | ,o.getAddress(),o.getPort(),o.getTaskPort()); 52 | listOtherNode.add(peerId1); 53 | peerIdMap.put(o.getPeerId(),peerId1); 54 | } 55 | NodeImpl.getNodeImple().setPeerIdList(listOtherNode); 56 | NodeImpl.getNodeImple().setPeerIdConcurrentHashMap(peerIdMap); 57 | NodeImpl.getNodeImple().setNodeId(nodeId); 58 | } 59 | 60 | 61 | 62 | // public void RaftOptionsLoader0(String name) throws FileNotFoundException { 63 | // //this.map= BootYaml.getYaml(name); 64 | // LOG.info("load raftOptions from "+name+" options:"+map); 65 | // Map nodeOptionsMap = (Map)map.get("NodeOptions"); 66 | // NodeOptions.getNodeOptions().setDaemon((boolean)nodeOptionsMap.get("daemon")); 67 | // NodeOptions.getNodeOptions().setElectionTimeOut(((Integer)nodeOptionsMap.get("electionTimeOut")).longValue()); 68 | // NodeOptions.getNodeOptions().setMaxHeartBeatTime(((Integer)nodeOptionsMap.get("maxHeartBeatTime")).longValue()); 69 | // NodeOptions.getNodeOptions().setRpcProtocol((String)nodeOptionsMap.get("rpcProtocol")); 70 | // NodeOptions.getNodeOptions().setSerialization((String)nodeOptionsMap.get("serialization")); 71 | // NodeOptions.getNodeOptions().setPort((int)nodeOptionsMap.get("port")); 72 | // Endpoint endpoint = new Endpoint((String)nodeOptionsMap.get("address"),(int)nodeOptionsMap.get("port")); 73 | // PeerId peerId = new PeerId(); 74 | // peerId.setPeerName((String)nodeOptionsMap.get("name")); 75 | // peerId.setId((String)nodeOptionsMap.get("peerId")); 76 | // peerId.setEndpoint(endpoint); 77 | // NodeId nodeId = new NodeId((String)nodeOptionsMap.get("groupId"),peerId); 78 | // List listOtherNode = new CopyOnWriteArrayList<>(); 79 | // List list = (List)map.get("OtherNodes"); 80 | // for (Object o:list 81 | // ) { 82 | // Map tempMap = (Map)o; 83 | // PeerId peerId1 = new PeerId(tempMap.get("peerId").toString(),tempMap.get("name").toString() 84 | // ,tempMap.get("address").toString(),(Integer) tempMap.get("port")); 85 | // listOtherNode.add(peerId1); 86 | // } 87 | // System.out.println(listOtherNode.size()); 88 | // NodeImpl.getNodeImple().setNodeId(nodeId); 89 | // } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/config/ReplicatorOptions.java: -------------------------------------------------------------------------------- 1 | package config; 2 | 3 | import core.NodeImpl; 4 | import entity.*; 5 | import utils.TimerManager; 6 | 7 | /** 8 | * Created by 周思成 on 2020/4/6 7:32 9 | */ 10 | 11 | public class ReplicatorOptions { 12 | private long dynamicHeartBeatTimeoutMs; 13 | private long electionTimeoutMs; 14 | private String groupId; 15 | private PeerId serverId; 16 | private PeerId peerId; 17 | private LogManager logManager; 18 | 19 | private NodeImpl node; 20 | private long term; 21 | private TimerManager timerManager; 22 | private ReplicatorType replicatorType; 23 | 24 | public ReplicatorOptions(){ 25 | 26 | } 27 | public ReplicatorOptions( PeerId peerId) { 28 | this.peerId = peerId; 29 | this.dynamicHeartBeatTimeoutMs= NodeOptions.getNodeOptions().getMaxHeartBeatTime(); 30 | this.setElectionTimeoutMs(NodeOptions.getNodeOptions().getElectionTimeOut()); 31 | 32 | } 33 | public ReplicatorOptions(long dynamicHeartBeatTimeoutMs, long electionTimeoutMs 34 | , String groupId, PeerId peerId, LogManager logManager 35 | , NodeImpl node, long term, TimerManager timerManager 36 | , ReplicatorType replicatorType) { 37 | this.dynamicHeartBeatTimeoutMs = dynamicHeartBeatTimeoutMs; 38 | this.electionTimeoutMs = electionTimeoutMs; 39 | this.groupId = groupId; 40 | 41 | this.peerId = peerId; 42 | this.logManager = logManager; 43 | //this.ballotBox = ballotBox; 44 | this.node = node; 45 | this.term = term; 46 | this.timerManager = timerManager; 47 | this.replicatorType = replicatorType; 48 | } 49 | 50 | public long getDynamicHeartBeatTimeoutMs() { 51 | return dynamicHeartBeatTimeoutMs; 52 | } 53 | 54 | public void setDynamicHeartBeatTimeoutMs(long dynamicHeartBeatTimeoutMs) { 55 | this.dynamicHeartBeatTimeoutMs = dynamicHeartBeatTimeoutMs; 56 | } 57 | 58 | public long getElectionTimeoutMs() { 59 | return electionTimeoutMs; 60 | } 61 | 62 | public void setElectionTimeoutMs(long electionTimeoutMs) { 63 | this.electionTimeoutMs = electionTimeoutMs; 64 | } 65 | 66 | public String getGroupId() { 67 | return groupId; 68 | } 69 | 70 | public void setGroupId(String groupId) { 71 | this.groupId = groupId; 72 | } 73 | 74 | public PeerId getServerId() { 75 | return serverId; 76 | } 77 | 78 | public void setServerId(PeerId serverId) { 79 | this.serverId = serverId; 80 | } 81 | 82 | public PeerId getPeerId() { 83 | return peerId; 84 | } 85 | 86 | public void setPeerId(PeerId peerId) { 87 | this.peerId = peerId; 88 | } 89 | 90 | public LogManager getLogManager() { 91 | return logManager; 92 | } 93 | 94 | public void setLogManager(LogManager logManager) { 95 | this.logManager = logManager; 96 | } 97 | 98 | 99 | 100 | public NodeImpl getNode() { 101 | return node; 102 | } 103 | 104 | public void setNode(NodeImpl node) { 105 | this.node = node; 106 | } 107 | 108 | public long getTerm() { 109 | return term; 110 | } 111 | 112 | public void setTerm(long term) { 113 | this.term = term; 114 | } 115 | 116 | public TimerManager getTimerManager() { 117 | return timerManager; 118 | } 119 | 120 | public void setTimerManager(TimerManager timerManager) { 121 | this.timerManager = timerManager; 122 | } 123 | 124 | public ReplicatorType getReplicatorType() { 125 | return replicatorType; 126 | } 127 | 128 | public void setReplicatorType(ReplicatorType replicatorType) { 129 | this.replicatorType = replicatorType; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/core/CustomStateMachine.java: -------------------------------------------------------------------------------- 1 | package core; 2 | 3 | import entity.Iterator; 4 | import entity.ReadTask; 5 | import entity.RpcResult; 6 | import entity.Task; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.nio.ByteBuffer; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.concurrent.locks.Lock; 14 | import java.util.concurrent.locks.ReentrantLock; 15 | 16 | /** 17 | * Created by 周思成 on 2020/5/7 20:07 18 | */ 19 | 20 | public class CustomStateMachine extends StateMachineAdapter{ 21 | private static final Logger LOG = LoggerFactory.getLogger(CustomStateMachine.class); 22 | private final Lock lock = new ReentrantLock(); 23 | private final RocksDBStorage rocksDBStorage = RocksDBStorageImpl.getRocksDBStorage(); 24 | 25 | @Override 26 | public void onApply(Iterator iter) { 27 | LOG.debug("onApply:{} operation:{}",iter.getIndex(),"READ".equals(iter.getOperation())); 28 | this.lock.lock(); 29 | try { 30 | switch (iter.getOperation()){ 31 | case "LOG": 32 | while (iter.hasNext()){ 33 | LOG.debug("CustomStateMachine handle LOG:{}",iter.getSize()); 34 | rocksDBStorage.put(toBytes(iter.getIndex()),getByteArrayFromByteBuffer(iter.getData())); 35 | NodeImpl.getNodeImple().handleLogApplied(iter.getIndex()); 36 | LOG.debug("Get from rocksdb test:key:{} value:{}",iter.getIndex(),new String( 37 | rocksDBStorage.get(toBytes(iter.getIndex())))); 38 | ReadTask task = new ReadTask(getByteArrayFromByteBuffer(iter.getData())); 39 | NodeImpl.getNodeImple().getClientRpcService().readResult(task); 40 | iter.next(); 41 | } 42 | break; 43 | case "READ": 44 | LOG.debug("CustomStateMachine handle READ:{}",iter.getSize()); 45 | List list = new ArrayList<>(iter.getSize()); 46 | while (iter.hasNext()){ 47 | ReadTask readTask = new ReadTask( 48 | rocksDBStorage.get(getByteArrayFromByteBuffer(iter.getData()))); 49 | list.add(readTask); 50 | iter.next(); 51 | } 52 | NodeImpl.getNodeImple().getEnClosureClientRpcRequest() 53 | .handleNotifyClient(list,true,new RpcResult()); 54 | break; 55 | default: 56 | LOG.error("unknow apply operation"); 57 | } 58 | 59 | 60 | // LOG.debug("CustomStateMachine handle READ:{}",iter.getSize()); 61 | // List list = new ArrayList<>(iter.getSize()); 62 | // while (iter.hasNext()){ 63 | // LOG.debug("DATA:{}",getByteArrayFromByteBuffer(iter.getData())); 64 | //// ReadTask readTask = new ReadTask( 65 | //// rocksDBStorage.get(getByteArrayFromByteBuffer(iter.getData()))); 66 | // ReadTask readTask = new ReadTask("1111".getBytes()); 67 | // LOG.debug("readTask:{}",readTask.getTaskBytes()); 68 | // list.add(readTask); 69 | // iter.next(); 70 | // } 71 | // NodeImpl.getNodeImple().getEnClosureClientRpcRequest() 72 | // .handleNotifyClient(list,true,new RpcResult()); 73 | 74 | 75 | 76 | } catch (Exception e) { 77 | LOG.error("CustomStateMachine apply error {}",e.getMessage()); 78 | e.printStackTrace(); 79 | }finally { 80 | this.lock.unlock(); 81 | } 82 | } 83 | 84 | public static byte[] toBytes(long val) { 85 | //System.out.println( "原来的长整形数据:"+val ); 86 | byte [] b = new byte[8]; 87 | for (int i = 7; i > 0; i--) { 88 | //强制转型,后留下长整形的低8位 89 | b[i] = (byte) val; 90 | String str = Long.toBinaryString( val) ; 91 | String lb = Long.toBinaryString( b[i] ) ; 92 | String lb2 = Long.toBinaryString( b[i]&0xff ) ; 93 | 94 | 95 | //System.out.println("转换为字节:"+ str ); 96 | //System.out.println( lb ); 97 | //System.out.println( lb2 ); 98 | //向右移动8位,则第二次循环则计算第二个8位数 99 | val >>>= 8; 100 | } 101 | b[0] = (byte) val; 102 | return b; 103 | } 104 | private static byte[] getByteArrayFromByteBuffer(ByteBuffer byteBuffer) { 105 | byte[] bytesArray = new byte[byteBuffer.remaining()]; 106 | byteBuffer.get(bytesArray, 0, bytesArray.length); 107 | return bytesArray; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/core/FSMCaller.java: -------------------------------------------------------------------------------- 1 | package core; 2 | 3 | import entity.FSMCallerOptions; 4 | import entity.LeaderChangeContext; 5 | import exceptions.RaftException; 6 | 7 | /** 8 | * Created by 周思成 on 2020/4/22 0:48 9 | */ 10 | 11 | public interface FSMCaller { 12 | 13 | boolean init(final FSMCallerOptions opts); 14 | 15 | /** 16 | * Called when log entry committed 17 | * 18 | * @param committedIndex committed log indexx 19 | */ 20 | boolean onCommitted(final long committedIndex); 21 | 22 | /** 23 | * Called when the leader starts. 24 | * 25 | * @param term current term 26 | */ 27 | boolean onLeaderStart(final long term); 28 | 29 | /** 30 | * Called when start following a leader. 31 | * 32 | * @param ctx context of leader change 33 | */ 34 | boolean onStartFollowing(final LeaderChangeContext ctx); 35 | 36 | /** 37 | * Called when stop following a leader. 38 | * 39 | * @param ctx context of leader change 40 | */ 41 | boolean onStopFollowing(final LeaderChangeContext ctx); 42 | 43 | /** 44 | * Called when error happens. 45 | * 46 | * @param error error info 47 | */ 48 | boolean onError(final RaftException error); 49 | 50 | /** 51 | * Returns the last log entry index to apply state machine. 52 | */ 53 | long getLastAppliedIndex(); 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/core/HeartbeatThreadFactory.java: -------------------------------------------------------------------------------- 1 | package core; 2 | 3 | import entity.NodeOptions; 4 | 5 | import java.util.concurrent.ThreadFactory; 6 | 7 | /** 8 | * Created by 周思成 on 2020/3/24 23:46 9 | */ 10 | 11 | public class HeartbeatThreadFactory implements ThreadFactory { 12 | 13 | @Override 14 | public Thread newThread(Runnable r) { 15 | return new Thread(r,"Raft timeout thread"); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/core/IteratorWrapper.java: -------------------------------------------------------------------------------- 1 | package core; 2 | 3 | import entity.Closure; 4 | import entity.Iterator; 5 | import entity.LogEntry; 6 | import entity.Status; 7 | import rpc.EnumOutter; 8 | 9 | import java.nio.ByteBuffer; 10 | 11 | /** 12 | * Created by 周思成 on 2020/4/22 13:54 13 | */ 14 | 15 | public class IteratorWrapper implements Iterator { 16 | 17 | private final IteratorImpl impl; 18 | 19 | private final String OPERATION = "LOG"; 20 | 21 | 22 | public IteratorWrapper(IteratorImpl iterImpl) { 23 | super(); 24 | this.impl = iterImpl; 25 | } 26 | 27 | @Override 28 | public boolean hasNext() { 29 | return this.impl.isGood() ; 30 | } 31 | 32 | @Override 33 | public ByteBuffer next() { 34 | final ByteBuffer data = getData(); 35 | if (hasNext()) { 36 | this.impl.next(); 37 | } 38 | return data; 39 | } 40 | 41 | @Override 42 | public String getOperation() { 43 | return getOPERATION(); 44 | } 45 | 46 | @Override 47 | public ByteBuffer getData() { 48 | final LogEntry entry = this.impl.entry(); 49 | return entry != null ? entry.getData() : null; 50 | } 51 | 52 | @Override 53 | public long getIndex() { 54 | return this.impl.getIndex(); 55 | } 56 | 57 | @Override 58 | public long getTerm() { 59 | return this.impl.entry().getId().getTerm(); 60 | } 61 | 62 | @Override 63 | public Closure done() { 64 | //return this.impl.done(); 65 | return null; 66 | } 67 | 68 | @Override 69 | public void setErrorAndRollback(final long ntail, final Status st) { 70 | 71 | //this.impl.setErrorAndRollback(ntail, st); 72 | } 73 | 74 | @Override 75 | public int getSize() { 76 | return 0; 77 | } 78 | 79 | public IteratorImpl getImpl() { 80 | return impl; 81 | } 82 | 83 | public String getOPERATION() { 84 | return OPERATION; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/core/Lifecycle.java: -------------------------------------------------------------------------------- 1 | package core; 2 | 3 | /** 4 | * Created by 周思成 on 2020/3/13 16:56 5 | */ 6 | 7 | public interface Lifecycle { 8 | 9 | /** 10 | * Initialize the service. 11 | * 12 | * @return true when successes. 13 | */ 14 | boolean init(final T opts); 15 | 16 | /** 17 | * Dispose the resources for service. 18 | */ 19 | void shutdown(); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/core/LogEntryCodecFactory.java: -------------------------------------------------------------------------------- 1 | package core; 2 | 3 | import entity.LogEntryDecoder; 4 | import entity.LogEntryEncoder; 5 | 6 | /** 7 | * Created by 周思成 on 2020/4/7 20:54 8 | */ 9 | 10 | public interface LogEntryCodecFactory { 11 | /** 12 | * Returns a log entry encoder. 13 | * @return encoder instance 14 | */ 15 | LogEntryEncoder encoder(); 16 | 17 | /** 18 | * Returns a log entry decoder. 19 | * @return encoder instance 20 | */ 21 | LogEntryDecoder decoder(); 22 | } -------------------------------------------------------------------------------- /src/main/java/core/NodeManager.java: -------------------------------------------------------------------------------- 1 | package core; 2 | 3 | import entity.Endpoint; 4 | import entity.Node; 5 | import entity.PeerId; 6 | 7 | import java.util.Collection; 8 | import java.util.List; 9 | import java.util.stream.Collectors; 10 | 11 | /** 12 | * Created by 周思成 on 2020/3/18 12:03 13 | */ 14 | 15 | public interface NodeManager { 16 | 17 | /** 18 | * Return true when RPC service is registered. 19 | */ 20 | boolean serverExits(final Endpoint addr); 21 | 22 | /** 23 | * Remove a RPC service address. 24 | */ 25 | boolean removeAddress(final Endpoint addr); 26 | 27 | /** 28 | * Adds a RPC service address. 29 | */ 30 | void addAddress(final Endpoint addr); 31 | 32 | /** 33 | * Adds a node. 34 | */ 35 | boolean addNode(final Node node); 36 | /** 37 | * Remove a node. 38 | */ 39 | boolean removeNode(final Node node); 40 | 41 | /** 42 | * Get node by groupId and peer. 43 | */ 44 | Node get(final String groupId, final PeerId peerId); 45 | 46 | /** 47 | * Get all nodes in a raft group. 48 | */ 49 | List getNodesByGroupId(final String groupId); 50 | 51 | /** 52 | * Get all nodes 53 | */ 54 | List getAllNodes() ; 55 | 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/core/NodeManagerImpl.java: -------------------------------------------------------------------------------- 1 | package core; 2 | 3 | import com.alipay.remoting.util.ConcurrentHashSet; 4 | import entity.Endpoint; 5 | import entity.Node; 6 | import entity.NodeId; 7 | import entity.PeerId; 8 | 9 | import java.util.List; 10 | import java.util.concurrent.ConcurrentHashMap; 11 | import java.util.concurrent.ConcurrentMap; 12 | 13 | /** 14 | * Created by 周思成 on 2020/3/18 11:52 15 | * @author Mike 16 | */ 17 | 18 | public class NodeManagerImpl implements NodeManager { 19 | 20 | private static final NodeManagerImpl INSTANCE = new NodeManagerImpl(); 21 | 22 | private final ConcurrentMap nodeMap = new ConcurrentHashMap<>(); 23 | private final ConcurrentHashSet addressSet = new ConcurrentHashSet<>(); 24 | 25 | public static NodeManager getInstance() { 26 | return INSTANCE; 27 | } 28 | 29 | @Override 30 | public boolean serverExits(Endpoint addr) { 31 | return this.addressSet.contains(addr); 32 | } 33 | 34 | @Override 35 | public boolean removeAddress(Endpoint addr) { 36 | return this.addressSet.remove(addr); 37 | } 38 | 39 | @Override 40 | public void addAddress(Endpoint addr) { 41 | this.addressSet.add(addr); 42 | } 43 | 44 | @Override 45 | public boolean addNode(Node node) { 46 | return false; 47 | } 48 | 49 | @Override 50 | public boolean removeNode(Node node) { 51 | return false; 52 | } 53 | 54 | @Override 55 | public Node get(String groupId, PeerId peerId) { 56 | return null; 57 | } 58 | 59 | @Override 60 | public List getNodesByGroupId(String groupId) { 61 | return null; 62 | } 63 | 64 | @Override 65 | public List getAllNodes() { 66 | return null; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/core/ReadIteratorImpl.java: -------------------------------------------------------------------------------- 1 | package core; 2 | 3 | import entity.Closure; 4 | import entity.Iterator; 5 | import entity.ReadEvent; 6 | import entity.Status; 7 | 8 | import java.nio.ByteBuffer; 9 | import java.util.List; 10 | 11 | /** 12 | * Created by 周思成 on 2020/5/15 15:12 13 | */ 14 | 15 | public class ReadIteratorImpl implements Iterator { 16 | 17 | byte[] data; 18 | private final String OPERATION = "READ"; 19 | int size; 20 | int currentIndex; 21 | List list; 22 | 23 | public ReadIteratorImpl(List list) { 24 | this.list = list; 25 | this.size = list.size(); 26 | this.currentIndex = 0; 27 | this.data = list.get(0).entry; 28 | } 29 | 30 | @Override 31 | public String getOperation() { 32 | return OPERATION; 33 | } 34 | 35 | @Override 36 | public ByteBuffer getData() { 37 | ByteBuffer byteBuffer = ByteBuffer.wrap(data); 38 | return byteBuffer; 39 | } 40 | 41 | @Override 42 | public long getIndex() { 43 | return currentIndex; 44 | } 45 | 46 | @Override 47 | public long getTerm() { 48 | return 0; 49 | } 50 | 51 | @Override 52 | public Closure done() { 53 | return null; 54 | } 55 | 56 | @Override 57 | public void setErrorAndRollback(long ntail, Status st) { 58 | 59 | } 60 | 61 | @Override 62 | public int getSize() { 63 | return size; 64 | } 65 | 66 | @Override 67 | public boolean hasNext() { 68 | return (currentIndex+1)<(size); 69 | } 70 | 71 | @Override 72 | public ByteBuffer next() { 73 | this.currentIndex = currentIndex +1; 74 | 75 | this.data = list.get(currentIndex).entry; 76 | return getData(); 77 | } 78 | 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/core/ReplicatorStateListener.java: -------------------------------------------------------------------------------- 1 | package core; 2 | 3 | /** 4 | * Created by 周思成 on 2020/4/6 0:02 5 | */ 6 | 7 | import entity.PeerId; 8 | import entity.Status; 9 | 10 | /** 11 | * User can implement the ReplicatorStateListener interface by themselves. 12 | * So they can do some their own logic codes when replicator created, destroyed or had some errors. 13 | * 14 | */ 15 | public interface ReplicatorStateListener { 16 | 17 | /** 18 | * Called when this replicator has been created. 19 | * 20 | * @param peer replicator related peerId 21 | */ 22 | void onCreated(final PeerId peer); 23 | 24 | /** 25 | * Called when this replicator has some errors. 26 | * 27 | * @param peer replicator related peerId 28 | * @param status replicator's error detailed status 29 | */ 30 | void onError(final PeerId peer, final Status status); 31 | 32 | /** 33 | * Called when this replicator has been destroyed. 34 | * 35 | * @param peer replicator related peerId 36 | */ 37 | void onDestroyed(final PeerId peer); 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/core/RocksDBStorage.java: -------------------------------------------------------------------------------- 1 | package core; 2 | 3 | import entity.RocksBatch; 4 | import org.rocksdb.RocksDBException; 5 | import org.rocksdb.WriteBatch; 6 | import org.rocksdb.WriteOptions; 7 | 8 | import java.util.ArrayList; 9 | 10 | /** 11 | * Created by 周思成 on 2020/4/17 12:19 12 | */ 13 | 14 | public interface RocksDBStorage { 15 | 16 | boolean put(byte[] var1,byte[] var2); 17 | 18 | boolean putBatch(ArrayList var); 19 | 20 | boolean init(); 21 | 22 | void write(WriteOptions var1, WriteBatch var2) throws RocksDBException; 23 | 24 | byte[] get(byte[] var) throws RocksDBException; 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/core/RocksDBStorageImpl.java: -------------------------------------------------------------------------------- 1 | package core; 2 | 3 | import entity.RocksBatch; 4 | import org.apache.commons.io.FilenameUtils; 5 | import org.rocksdb.*; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.io.File; 10 | import java.util.ArrayList; 11 | import java.util.concurrent.locks.Lock; 12 | import java.util.concurrent.locks.ReadWriteLock; 13 | import java.util.concurrent.locks.ReentrantReadWriteLock; 14 | 15 | /** 16 | * Created by 周思成 on 2020/4/17 12:26 17 | */ 18 | 19 | public class RocksDBStorageImpl implements RocksDBStorage { 20 | public static final Logger LOG = LoggerFactory.getLogger(RocksDBStorageImpl.class); 21 | static{ 22 | RocksDB.loadLibrary(); 23 | 24 | } 25 | private static RocksDBStorageImpl rocksDBStorage=null; 26 | private boolean started = false; 27 | 28 | private RocksDBStorageImpl() { 29 | init(); 30 | } 31 | 32 | private static RocksDB rocksDB; 33 | TransactionDB transactionDB; 34 | private final static String LOG_PATH = FilenameUtils.concat(NodeImpl.getNodeImple() 35 | .getOptions().getCurrentNodeOptions().getLogStoragePath() 36 | ,NodeImpl.getNodeImple().getOptions().getCurrentNodeOptions().getLogStorageName()); 37 | //private final static String LOG_PATH = "E://Log"; 38 | private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); 39 | protected final Lock writeLock = this.readWriteLock.writeLock(); 40 | protected final Lock readLock = this.readWriteLock.readLock(); 41 | 42 | @Override 43 | public boolean put(byte[] var1, byte[] var2) { 44 | try { 45 | rocksDB.put(var1,var2); 46 | return true; 47 | } catch (RocksDBException e) { 48 | LOG.error("***Fatal error*** rocksDB PUT error {}",new String(var1)); 49 | } 50 | return false; 51 | } 52 | 53 | @Override 54 | public boolean putBatch(ArrayList var) { 55 | getWriteLock().lock(); 56 | try { 57 | for (RocksBatch rocksBatch : 58 | var) { 59 | rocksDB.put(rocksBatch.getKey(),rocksBatch.getValue()); 60 | } 61 | return true; 62 | } catch (RocksDBException e) { 63 | LOG.error("***Fatal error*** rocksDB PUT batch error {}",var); 64 | } 65 | return false; 66 | } 67 | 68 | @Override 69 | public boolean init() { 70 | getWriteLock().lock(); 71 | if(started){ 72 | return true; 73 | } 74 | 75 | LOG.info("Start to init RocksDBStorageImpl on logPath {}",LOG_PATH); 76 | 77 | try { 78 | Options options = new Options(); 79 | options.setCreateIfMissing(true); 80 | //check if dir exist 81 | isChartPathExist(NodeImpl.getNodeImple() 82 | .getOptions().getCurrentNodeOptions().getLogStoragePath()); 83 | 84 | rocksDB = RocksDB.open(options, LOG_PATH); 85 | started = true; 86 | return true; 87 | } catch (RocksDBException e) { 88 | LOG.error("***Fatal error*** rocksDB open error {}",e.getMessage()); 89 | return false; 90 | }finally { 91 | getWriteLock().unlock(); 92 | } 93 | } 94 | 95 | @Override 96 | public void write(WriteOptions var1, WriteBatch var2) throws RocksDBException { 97 | rocksDB.write(var1,var2); 98 | } 99 | 100 | @Override 101 | public byte[] get(byte[] var) throws RocksDBException { 102 | return rocksDB.get(var); 103 | } 104 | 105 | 106 | public static RocksDB getRocksDB() { 107 | return rocksDB; 108 | } 109 | 110 | public static void setRocksDB(RocksDB rocksDB) { 111 | RocksDBStorageImpl.rocksDB = rocksDB; 112 | } 113 | 114 | public static String getLogPath() { 115 | return LOG_PATH; 116 | } 117 | 118 | 119 | 120 | public ReadWriteLock getReadWriteLock() { 121 | return readWriteLock; 122 | } 123 | 124 | public Lock getWriteLock() { 125 | return writeLock; 126 | } 127 | 128 | public Lock getReadLock() { 129 | return readLock; 130 | } 131 | 132 | public static RocksDBStorageImpl getRocksDBStorage() { 133 | if(rocksDBStorage==null){ 134 | rocksDBStorage = new RocksDBStorageImpl(); 135 | } 136 | return rocksDBStorage; 137 | } 138 | private static void isChartPathExist(String dirPath) { 139 | 140 | File file = new File(dirPath); 141 | if (!file.exists()) { 142 | file.mkdirs(); 143 | } 144 | } 145 | 146 | } 147 | -------------------------------------------------------------------------------- /src/main/java/core/StateMachine.java: -------------------------------------------------------------------------------- 1 | package core; 2 | 3 | import entity.Iterator; 4 | import entity.LeaderChangeContext; 5 | import entity.Status; 6 | import exceptions.RaftException; 7 | 8 | /** 9 | * Created by 周思成 on 2020/4/22 13:16 10 | */ 11 | 12 | public interface StateMachine { 13 | 14 | /** 15 | * Update the StateMachine with a batch a tasks that can be accessed 16 | * through |iterator|. 17 | * 18 | * Invoked when one or more tasks that were passed to Node#apply(Task) have been 19 | * committed to the raft group (quorum of the group peers have received 20 | * those tasks and stored them on the backing storage). 21 | * 22 | * Once this function returns to the caller, we will regard all the iterated 23 | * tasks through |iter| have been successfully applied. And if you didn't 24 | * apply all the the given tasks, we would regard this as a critical error 25 | * and report a error whose type is ERROR_TYPE_STATE_MACHINE. 26 | * 27 | * @param iter iterator of states 28 | */ 29 | void onApply(final Iterator iter); 30 | 31 | 32 | /** 33 | * Invoked once when the raft node was shut down. 34 | * Default do nothing 35 | */ 36 | void onShutdown(); 37 | 38 | 39 | 40 | /** 41 | * Invoked when the belonging node becomes the leader of the group at |term| 42 | * Default: Do nothing 43 | * 44 | * @param term new term num 45 | */ 46 | void onLeaderStart(final long term); 47 | 48 | /** 49 | * Invoked when this node steps down from the leader of the replication 50 | * group and |status| describes detailed information 51 | * 52 | * @param status status info 53 | */ 54 | void onLeaderStop(final Status status); 55 | 56 | 57 | /** 58 | * This method is called when a critical error was encountered, after this 59 | * point, no any further modification is allowed to applied to this node 60 | * until the error is fixed and this node restarts. 61 | * 62 | * @param e raft error message 63 | */ 64 | void onError(final RaftException e); 65 | 66 | /** 67 | * This method is called when a follower stops following a leader and its leaderId becomes null, 68 | * situations including: 69 | * 1. handle election timeout and start preVote 70 | * 2. receive requests with higher term such as VoteRequest from a candidate 71 | * or appendEntries request from a new leader 72 | * 3. receive timeoutNow request from current leader and start request vote. 73 | * 74 | * the parameter ctx gives the information(leaderId, term and status) about the 75 | * very leader whom the follower followed before. 76 | * User can reset the node's information as it stops following some leader. 77 | * 78 | * @param ctx context of leader change 79 | */ 80 | void onStopFollowing(final LeaderChangeContext ctx); 81 | 82 | 83 | /** 84 | * This method is called when a follower or candidate starts following a leader and its leaderId 85 | * (should be NULL before the method is called) is set to the leader's id, 86 | * situations including: 87 | * 1. a candidate receives appendEntries request from a leader 88 | * 2. a follower(without leader) receives appendEntries from a leader 89 | * 90 | * the parameter ctx gives the information(leaderId, term and status) about 91 | * the very leader whom the follower starts to follow. 92 | * User can reset the node's information as it starts to follow some leader. 93 | * 94 | * @param ctx context of leader change 95 | */ 96 | void onStartFollowing(final LeaderChangeContext ctx); 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/core/StateMachineAdapter.java: -------------------------------------------------------------------------------- 1 | package core; 2 | 3 | import entity.Iterator; 4 | import entity.LeaderChangeContext; 5 | 6 | import entity.Status; 7 | import exceptions.RaftException; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | /** 12 | * Created by 周思成 on 2020/3/12 13:46 13 | */ 14 | 15 | /** 16 | * chule 17 | */ 18 | public abstract class StateMachineAdapter implements StateMachine { 19 | 20 | private static final Logger LOG = LoggerFactory.getLogger(StateMachineAdapter.class); 21 | @Override 22 | public void onShutdown() { 23 | LOG.info("Current StateMachine shutdown"); 24 | } 25 | 26 | @Override 27 | public void onLeaderStart(long term) { 28 | LOG.info("Current leader start|term:"+term); 29 | } 30 | 31 | @Override 32 | public void onLeaderStop(Status status) { 33 | LOG.info("Current leader stop|status:"+status.toString()); 34 | } 35 | 36 | @Override 37 | public void onError(RaftException e) { 38 | LOG.error("Current statemachine error|detail:"+e.toString()); 39 | } 40 | 41 | @Override 42 | public void onStopFollowing(LeaderChangeContext ctx) { 43 | LOG.info("Current node stop following|detail:"+ctx.toString()); 44 | } 45 | 46 | @Override 47 | public void onStartFollowing(LeaderChangeContext ctx) { 48 | LOG.info("Current node start following|detail:"+ctx.toString()); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/entity/Ballot.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | import core.NodeImpl; 4 | import service.ElectionService; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.concurrent.CopyOnWriteArrayList; 9 | import java.util.concurrent.atomic.AtomicInteger; 10 | 11 | /** 12 | * Created by 周思成 on 2020/3/27 19:56 13 | */ 14 | 15 | public class Ballot { 16 | 17 | List peerList; 18 | List grantedPeerList; 19 | private AtomicInteger quorum; 20 | public Ballot(List list) { 21 | this.peerList = list; 22 | this.quorum = new AtomicInteger(this.peerList.size() / 2 + 1); 23 | this.grantedPeerList = new ArrayList<>(peerList.size()); 24 | } 25 | 26 | public void grant(final String peerId) { 27 | 28 | if(findPeer(peerId)){ 29 | this.quorum.decrementAndGet(); 30 | } 31 | 32 | } 33 | 34 | private boolean findPeer(final String peerId) { 35 | for (PeerId p: 36 | peerList) { 37 | if(p.getId().equals(peerId)){ 38 | grantedPeerList.add(peerId); 39 | return true; 40 | } else if (NodeImpl.getNodeImple().getNodeId().getPeerId().getId().equals(peerId)) { 41 | grantedPeerList.add(peerId); 42 | return true; 43 | } 44 | } 45 | return false; 46 | } 47 | 48 | /** 49 | * Returns true when the ballot is granted. 50 | * 51 | * @return true if the ballot is granted 52 | */ 53 | public boolean isGranted(){ 54 | return this.quorum.get() <=0; 55 | } 56 | 57 | public List getGranted(){ 58 | return grantedPeerList; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/entity/BallotBoxForApply.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | import core.NodeImpl; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import java.util.List; 8 | import java.util.concurrent.atomic.AtomicInteger; 9 | import java.util.concurrent.atomic.AtomicReference; 10 | import java.util.concurrent.locks.Lock; 11 | import java.util.concurrent.locks.ReadWriteLock; 12 | import java.util.concurrent.locks.ReentrantReadWriteLock; 13 | 14 | /** 15 | * Created by 周思成 on 2020/4/14 15:33 16 | */ 17 | 18 | public class BallotBoxForApply { 19 | public static final Logger LOG = LoggerFactory.getLogger(NodeImpl.class); 20 | 21 | enum BallotBoxState{ 22 | //wait for follower to vote 23 | Voting, 24 | //most of the follower granted,could be applied but haven't applied yet 25 | Granted, 26 | //already been applied 27 | Applied 28 | } 29 | List peerList; 30 | private final AtomicReference ballotBoxState; 31 | private AtomicInteger quorum; 32 | private final long currentIndex; 33 | private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); 34 | protected final Lock writeLock = this.readWriteLock.writeLock(); 35 | protected final Lock readLock = this.readWriteLock.readLock(); 36 | 37 | //private final long currentTerm; 38 | public BallotBoxForApply(long currentIndex) { 39 | this.peerList = NodeImpl.getNodeImple().getPeerIdList(); 40 | this.currentIndex = currentIndex; 41 | 42 | //this.currentTerm = currentTerm; 43 | this.quorum = new AtomicInteger(this.peerList.size() / 2 + 1); 44 | this.ballotBoxState = new AtomicReference(); 45 | this.ballotBoxState.set(BallotBoxState.Voting); 46 | grant(NodeImpl.getNodeImple().getNodeId().getPeerId().getId()); 47 | } 48 | 49 | public void grant(final String peerId) { 50 | 51 | if(findPeer(peerId)){ 52 | this.quorum.decrementAndGet(); 53 | } 54 | checkBallotBoxToApply(); 55 | 56 | } 57 | 58 | public void checkGranted(final String peerId,final long currentIndex, final long length) { 59 | if (this.currentIndex == currentIndex) { 60 | grant(peerId); 61 | } 62 | } 63 | 64 | private boolean findPeer(final String peerId) { 65 | for (PeerId p: 66 | peerList) { 67 | if(p.getId().equals(peerId)){ 68 | return true; 69 | } 70 | } 71 | return false; 72 | } 73 | 74 | /** 75 | * check if the ballot box can apply the log to the state machine 76 | */ 77 | public void checkBallotBoxToApply(){ 78 | 79 | setBallotBoxState(BallotBoxState.Applied); 80 | NodeImpl.getNodeImple().getAppliedLogIndex().set(currentIndex); 81 | 82 | // if (isGranted() && 83 | // this.currentIndex == (NodeImpl.getNodeImple().getStableLogIndex().get())) { 84 | // //check if it is sequential 85 | // //on state machine apply 86 | // 87 | //// NodeImpl.getNodeImple().getFsmCaller().onCommitted(currentIndex+length); 88 | //// NodeImpl.getNodeImple().getBallotBoxConcurrentHashMap() 89 | //// .get(this.currentIndex + length).checkBallotBoxToApply(); 90 | // 91 | // } 92 | 93 | } 94 | 95 | /** 96 | * Returns true when the ballot is granted. 97 | * 98 | * @return true if the ballot is granted 99 | */ 100 | public boolean isGranted(){ 101 | 102 | if (BallotBoxState.Granted.equals(getBallotBoxState()) || this.quorum.get() <= 0) { 103 | setBallotBoxState(BallotBoxState.Granted); 104 | return true; 105 | } 106 | return false; 107 | } 108 | 109 | public List getPeerList() { 110 | return peerList; 111 | } 112 | 113 | public void setPeerList(List peerList) { 114 | this.peerList = peerList; 115 | } 116 | 117 | public BallotBoxState getBallotBoxState() { 118 | return ballotBoxState.get(); 119 | } 120 | 121 | public void setBallotBoxState(BallotBoxState ballotBoxState) { 122 | this.ballotBoxState.set(ballotBoxState); 123 | } 124 | 125 | public AtomicInteger getQuorum() { 126 | return quorum; 127 | } 128 | 129 | public void setQuorum(AtomicInteger quorum) { 130 | this.quorum = quorum; 131 | } 132 | 133 | public long getCurrentIndex() { 134 | return currentIndex; 135 | } 136 | 137 | 138 | 139 | public ReadWriteLock getReadWriteLock() { 140 | return readWriteLock; 141 | } 142 | 143 | public Lock getWriteLock() { 144 | return writeLock; 145 | } 146 | 147 | public Lock getReadLock() { 148 | return readLock; 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/main/java/entity/Checksum.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | /** 4 | * Created by 周思成 on 2020/3/10 15:30 5 | */ 6 | 7 | /** 8 | * Checksum for entity. 9 | * 10 | * @author boyan(boyan@antfin.com) 11 | * @since 1.2.6 12 | */ 13 | public interface Checksum { 14 | 15 | /** 16 | * Caculate a checksum value for this entity. 17 | * @return checksum value 18 | */ 19 | long checksum(); 20 | 21 | /** 22 | * Returns the checksum value of two long values. 23 | * 24 | * @param v1 first long value 25 | * @param v2 second long value 26 | * @return checksum value 27 | */ 28 | default long checksum(final long v1, final long v2) { 29 | return v1 ^ v2; 30 | } 31 | } -------------------------------------------------------------------------------- /src/main/java/entity/Closure.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | /** 4 | * Created by 周思成 on 2020/3/11 16:02 5 | */ 6 | /** 7 | * Callback closure. 8 | * 9 | * @author boyan (boyan@alibaba-inc.com) 10 | * 11 | * 2018-Apr-03 11:07:05 AM 12 | */ 13 | public interface Closure { 14 | 15 | /** 16 | * Called when task is done. 17 | * 18 | * @param status the task status. 19 | */ 20 | void run(final Status status); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/entity/ConfigurationEntry.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | /** 4 | * Created by 周思成 on 2020/4/14 16:27 5 | */ 6 | 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.HashSet; 11 | import java.util.Set; 12 | 13 | /** 14 | * A configuration entry with current peers and old peers. 15 | * @author boyan (boyan@alibaba-inc.com) 16 | * 17 | * 2018-Apr-04 2:25:06 PM 18 | */ 19 | public class ConfigurationEntry { 20 | 21 | private static final Logger LOG = LoggerFactory.getLogger(ConfigurationEntry.class); 22 | 23 | private LogId id = new LogId(0, 0); 24 | 25 | 26 | public LogId getId() { 27 | return this.id; 28 | } 29 | 30 | public void setId(final LogId id) { 31 | this.id = id; 32 | } 33 | 34 | 35 | 36 | 37 | 38 | } -------------------------------------------------------------------------------- /src/main/java/entity/CurrentNodeOptions.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | /** 4 | * Created by 周思成 on 2020/3/30 21:28 5 | * @author Mike 6 | */ 7 | 8 | public class CurrentNodeOptions{ 9 | private long electionTimeOut; 10 | private long maxHeartBeatTime; 11 | private String rpcProtocol; 12 | private String serialization; 13 | private int port; 14 | private int taskPort; 15 | private boolean daemon; 16 | private String groupId; 17 | private String address; 18 | private String name; 19 | private String peerId; 20 | private String logStoragePath; 21 | private String logStorageName; 22 | private String taskExecuteMethod; 23 | 24 | private String clientAddress; 25 | private String clientPort; 26 | 27 | public String getClientAddress() { 28 | return clientAddress; 29 | } 30 | 31 | public void setClientAddress(String clientAddress) { 32 | this.clientAddress = clientAddress; 33 | } 34 | 35 | public String getClientPort() { 36 | return clientPort; 37 | } 38 | 39 | public void setClientPort(String clientPort) { 40 | this.clientPort = clientPort; 41 | } 42 | 43 | public String getTaskExecuteMethod() { 44 | return taskExecuteMethod; 45 | } 46 | 47 | public void setTaskExecuteMethod(String taskExecuteMethod) { 48 | this.taskExecuteMethod = taskExecuteMethod; 49 | } 50 | 51 | public int getTaskPort() { 52 | return taskPort; 53 | } 54 | 55 | public void setTaskPort(int taskPort) { 56 | this.taskPort = taskPort; 57 | } 58 | 59 | @Override 60 | public String toString() { 61 | return "CurrentNodeOptions{" + 62 | "electionTimeOut=" + electionTimeOut + 63 | ", maxHeartBeatTime=" + maxHeartBeatTime + 64 | ", rpcProtocol='" + rpcProtocol + '\'' + 65 | ", serialization='" + serialization + '\'' + 66 | ", port=" + port + 67 | ", taskPort=" + taskPort + 68 | ", daemon=" + daemon + 69 | ", groupId='" + groupId + '\'' + 70 | ", address='" + address + '\'' + 71 | ", name='" + name + '\'' + 72 | ", peerId='" + peerId + '\'' + 73 | ", logStoragePath='" + logStoragePath + '\'' + 74 | ", logStorageName='" + logStorageName + '\'' + 75 | ", taskExecuteMethod='" + taskExecuteMethod + '\'' + 76 | ", clientAddress='" + clientAddress + '\'' + 77 | ", clientPort='" + clientPort + '\'' + 78 | '}'; 79 | } 80 | 81 | public String getLogStoragePath() { 82 | return logStoragePath; 83 | } 84 | 85 | public void setLogStoragePath(String logStoragePath) { 86 | this.logStoragePath = logStoragePath; 87 | } 88 | 89 | public String getLogStorageName() { 90 | return logStorageName; 91 | } 92 | 93 | public void setLogStorageName(String logStorageName) { 94 | this.logStorageName = logStorageName; 95 | } 96 | 97 | public long getElectionTimeOut() { 98 | return electionTimeOut; 99 | } 100 | 101 | public void setElectionTimeOut(long electionTimeOut) { 102 | this.electionTimeOut = electionTimeOut; 103 | } 104 | 105 | public long getMaxHeartBeatTime() { 106 | return maxHeartBeatTime; 107 | } 108 | 109 | public void setMaxHeartBeatTime(long maxHeartBeatTime) { 110 | this.maxHeartBeatTime = maxHeartBeatTime; 111 | } 112 | 113 | public String getRpcProtocol() { 114 | return rpcProtocol; 115 | } 116 | 117 | public void setRpcProtocol(String rpcProtocol) { 118 | this.rpcProtocol = rpcProtocol; 119 | } 120 | 121 | public String getSerialization() { 122 | return serialization; 123 | } 124 | 125 | public void setSerialization(String serialization) { 126 | this.serialization = serialization; 127 | } 128 | 129 | public int getPort() { 130 | return port; 131 | } 132 | 133 | public void setPort(int port) { 134 | this.port = port; 135 | } 136 | 137 | public boolean isDaemon() { 138 | return daemon; 139 | } 140 | 141 | public void setDaemon(boolean daemon) { 142 | this.daemon = daemon; 143 | } 144 | 145 | public String getGroupId() { 146 | return groupId; 147 | } 148 | 149 | public void setGroupId(String groupId) { 150 | this.groupId = groupId; 151 | } 152 | 153 | public String getAddress() { 154 | return address; 155 | } 156 | 157 | public void setAddress(String address) { 158 | this.address = address; 159 | } 160 | 161 | public String getName() { 162 | return name; 163 | } 164 | 165 | public void setName(String name) { 166 | this.name = name; 167 | } 168 | 169 | public String getPeerId() { 170 | return peerId; 171 | } 172 | 173 | public void setPeerId(String peerId) { 174 | this.peerId = peerId; 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/main/java/entity/ElectionTimeOutClosure.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | import core.NodeImpl; 4 | import service.ElectionService; 5 | 6 | /** 7 | * Created by 周思成 on 2020/4/1 16:10 8 | */ 9 | 10 | public class ElectionTimeOutClosure implements Closure,TimeOutClosure { 11 | @Override 12 | public void run(Status status) { 13 | ElectionService.checkToStartPreVote(); 14 | } 15 | 16 | 17 | @Override 18 | public long getBaseTime(){ 19 | return NodeImpl.getNodeImple().getLastReceiveHeartbeatTime().longValue(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/entity/Endpoint.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | /** 4 | * Created by 周思成 on 2020/3/10 15:18 5 | */ 6 | 7 | import utils.Utils; 8 | 9 | import java.io.Serializable; 10 | import java.net.InetSocketAddress; 11 | 12 | /** 13 | * A IP address with port. 14 | * 15 | * @author boyan (boyan@alibaba-inc.com) 16 | * 17 | * 2018-Mar-12 3:29:12 PM 18 | */ 19 | public class Endpoint implements Serializable { 20 | 21 | private static final long serialVersionUID = -7329681263115546100L; 22 | 23 | private String ip = Utils.IP_ANY; 24 | private int port; 25 | private String str; 26 | 27 | 28 | public Endpoint() { 29 | super(); 30 | } 31 | 32 | public Endpoint(String address, int port) { 33 | super(); 34 | this.ip = address; 35 | this.port = port; 36 | } 37 | 38 | public String getIp() { 39 | return this.ip; 40 | } 41 | 42 | public int getPort() { 43 | return this.port; 44 | } 45 | 46 | public InetSocketAddress toInetSocketAddress() { 47 | return new InetSocketAddress(this.ip, this.port); 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | if (str == null) { 53 | str = this.ip + ":" + this.port; 54 | } 55 | return str; 56 | } 57 | 58 | 59 | 60 | @Override 61 | public int hashCode() { 62 | final int prime = 31; 63 | int result = 1; 64 | result = prime * result + (this.ip == null ? 0 : this.ip.hashCode()); 65 | result = prime * result + this.port; 66 | return result; 67 | } 68 | 69 | @Override 70 | public boolean equals(Object obj) { 71 | if (this == obj) { 72 | return true; 73 | } 74 | if (obj == null) { 75 | return false; 76 | } 77 | if (getClass() != obj.getClass()) { 78 | return false; 79 | } 80 | final Endpoint other = (Endpoint) obj; 81 | if (this.ip == null) { 82 | if (other.ip != null) { 83 | return false; 84 | } 85 | } else if (!this.ip.equals(other.ip)) { 86 | return false; 87 | } 88 | return this.port == other.port; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/entity/FSMCallerOptions.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | import core.NodeImpl; 4 | import core.StateMachine; 5 | 6 | /** 7 | * Created by 周思成 on 2020/4/22 11:12 8 | */ 9 | 10 | public class FSMCallerOptions { 11 | 12 | private LogManager logManager; 13 | private StateMachine fsm; 14 | private Closure afterShutdown; 15 | private LogId bootstrapId; 16 | //private ClosureQueue closureQueue; 17 | private NodeImpl node; 18 | 19 | /** 20 | * disruptor buffer size. 21 | */ 22 | private int disruptorBufferSize = 1024; 23 | 24 | public LogManager getLogManager() { 25 | return logManager; 26 | } 27 | 28 | public void setLogManager(LogManager logManager) { 29 | this.logManager = logManager; 30 | } 31 | 32 | public StateMachine getFsm() { 33 | return fsm; 34 | } 35 | 36 | public void setFsm(StateMachine fsm) { 37 | this.fsm = fsm; 38 | } 39 | 40 | public Closure getAfterShutdown() { 41 | return afterShutdown; 42 | } 43 | 44 | public void setAfterShutdown(Closure afterShutdown) { 45 | this.afterShutdown = afterShutdown; 46 | } 47 | 48 | public LogId getBootstrapId() { 49 | return bootstrapId; 50 | } 51 | 52 | public void setBootstrapId(LogId bootstrapId) { 53 | this.bootstrapId = bootstrapId; 54 | } 55 | 56 | public NodeImpl getNode() { 57 | return node; 58 | } 59 | 60 | public void setNode(NodeImpl node) { 61 | this.node = node; 62 | } 63 | 64 | public int getDisruptorBufferSize() { 65 | return disruptorBufferSize; 66 | } 67 | 68 | public void setDisruptorBufferSize(int disruptorBufferSize) { 69 | this.disruptorBufferSize = disruptorBufferSize; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/entity/Heartbeat.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | import core.HeartbeatThreadFactory; 4 | import core.NodeImpl; 5 | import core.RaftGroupService; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.util.Queue; 10 | import java.util.concurrent.*; 11 | import java.util.concurrent.locks.Lock; 12 | import java.util.concurrent.locks.ReentrantLock; 13 | 14 | /** 15 | * Created by 周思成 on 2020/3/25 0:01 16 | */ 17 | 18 | public class Heartbeat { 19 | 20 | private static final Logger LOG = LoggerFactory.getLogger(RaftGroupService.class); 21 | private ThreadPoolExecutor threadPoolExecutor; 22 | 23 | private volatile TimeOutChecker checker; 24 | 25 | private Lock lock = new ReentrantLock(true); 26 | public Heartbeat(int corePoolSize, int maximumPoolSize 27 | , int keepAliveTime, TimeUnit timeUnit, BlockingQueue queue 28 | , ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler){ 29 | LOG.info("start to create heartbeat module with parameters:corePoolSize:{}" + 30 | ",maximumPoolSize:{},keepAliveTime:{},timeUnit:{}",corePoolSize 31 | ,maximumPoolSize,keepAliveTime,timeUnit); 32 | //线程池 负责复用计时器,来判断是否超时启动leader选举 33 | this.threadPoolExecutor = new ThreadPoolExecutor(corePoolSize 34 | ,maximumPoolSize,keepAliveTime,timeUnit,queue,threadFactory,rejectedExecutionHandler); 35 | 36 | NodeImpl.getNodeImple().setHeartbeat(this); 37 | 38 | } 39 | 40 | public TimeOutChecker getChecker() { 41 | 42 | return checker; 43 | } 44 | 45 | public void setChecker(TimeOutChecker checker) { 46 | lock.lock(); 47 | try { 48 | LOG.debug("Set checker"); 49 | 50 | this.checker = checker; 51 | this.getThreadPoolExecutor().execute(checker); 52 | LOG.debug("heartbeat thread pool {}",threadPoolExecutor.getActiveCount()); 53 | } catch (Exception e) { 54 | e.printStackTrace(); 55 | LOG.error("set Heartbeat checker error:"+e.getMessage()); 56 | }finally { 57 | lock.unlock(); 58 | } 59 | 60 | } 61 | 62 | public ThreadPoolExecutor getThreadPoolExecutor() { 63 | return threadPoolExecutor; 64 | } 65 | 66 | public Lock getLock() { 67 | return lock; 68 | } 69 | 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/entity/Iterator.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | /** 6 | * Created by 周思成 on 2020/3/11 15:47 7 | * @author Mike 8 | */ 9 | 10 | public interface Iterator extends java.util.Iterator{ 11 | 12 | 13 | 14 | String getOperation(); 15 | 16 | /** 17 | * Return the data whose content is the same as what was passed to 18 | * Node#apply(Task) in the leader node. 19 | */ 20 | ByteBuffer getData(); 21 | 22 | /** 23 | * Return a unique and monotonically increasing identifier of the current task: 24 | * - Uniqueness guarantees that committed tasks in different peers with 25 | * the same index are always the same and kept unchanged. 26 | * - Monotonicity guarantees that for any index pair i, j (i < j), task 27 | * at index |i| must be applied before task at index |j| in all the 28 | * peers from the group. 29 | */ 30 | long getIndex(); 31 | 32 | /** 33 | * Returns the term of the leader which to task was applied to. 34 | */ 35 | long getTerm(); 36 | 37 | 38 | /** 39 | * If done() is non-NULL, you must call done()->Run() after applying this 40 | * task no matter this operation succeeds or fails, otherwise the 41 | * corresponding resources would leak. 42 | * 43 | * If this task is proposed by this Node when it was the leader of this 44 | * group and the leadership has not changed before this point, done() is 45 | * exactly what was passed to Node#apply(Task) which may stand for some 46 | * continuation (such as respond to the client) after updating the 47 | * StateMachine with the given task. Otherwise done() must be NULL. 48 | * */ 49 | Closure done(); 50 | 51 | 52 | /** 53 | * Invoked when some critical error occurred. And we will consider the last 54 | * |ntail| tasks (starting from the last iterated one) as not applied. After 55 | * this point, no further changes on the StateMachine as well as the Node 56 | * would be allowed and you should try to repair this replica or just drop it. 57 | * 58 | * @param ntail the number of tasks (starting from the last iterated one) considered as not to be applied. 59 | * @param st Status to describe the detail of the error. 60 | */ 61 | void setErrorAndRollback(final long ntail, final Status st); 62 | 63 | int getSize(); 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/entity/KVEntity.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Created by 周思成 on 2020/4/25 12:48 7 | */ 8 | 9 | public class KVEntity implements Serializable { 10 | 11 | private static final long serialVersionUID = -7329681263115546100L; 12 | 13 | String value; 14 | String key; 15 | 16 | public KVEntity(String value, String key) { 17 | this.value = value; 18 | this.key = key; 19 | } 20 | 21 | public String getValue() { 22 | return value; 23 | } 24 | 25 | public void setValue(String value) { 26 | this.value = value; 27 | } 28 | 29 | public String getKey() { 30 | return key; 31 | } 32 | 33 | public void setKey(String key) { 34 | this.key = key; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/entity/LeaderChangeContext.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | /** 4 | * Created by 周思成 on 2020/3/12 13:38 5 | */ 6 | /** 7 | * The leader change context, contains: 8 | *
    9 | *
  • leaderId: the leader peer id.
  • 10 | *
  • term: the leader term.
  • 11 | *
  • Status: context status.
  • 12 | *
13 | * @author boyan (boyan@alibaba-inc.com) 14 | * 15 | * 2018-Mar-13 3:23:48 PM 16 | */ 17 | public class LeaderChangeContext { 18 | 19 | private PeerId leaderId; 20 | private long term; 21 | private Status status; 22 | 23 | public LeaderChangeContext(PeerId leaderId, long term, Status status) { 24 | super(); 25 | this.leaderId = leaderId; 26 | this.term = term; 27 | this.status = status; 28 | } 29 | 30 | public PeerId getLeaderId() { 31 | return this.leaderId; 32 | } 33 | 34 | public void setLeaderId(PeerId leaderId) { 35 | this.leaderId = leaderId; 36 | } 37 | 38 | public long getTerm() { 39 | return this.term; 40 | } 41 | 42 | public void setTerm(long term) { 43 | this.term = term; 44 | } 45 | 46 | public Status getStatus() { 47 | return this.status; 48 | } 49 | 50 | public void setStatus(Status status) { 51 | this.status = status; 52 | } 53 | 54 | @Override 55 | public int hashCode() { 56 | final int prime = 31; 57 | int result = 1; 58 | result = prime * result + (this.leaderId == null ? 0 : this.leaderId.hashCode()); 59 | result = prime * result + (this.status == null ? 0 : this.status.hashCode()); 60 | result = prime * result + (int) (this.term ^ this.term >>> 32); 61 | return result; 62 | } 63 | 64 | @Override 65 | public boolean equals(Object obj) { 66 | if (this == obj) { 67 | return true; 68 | } 69 | if (obj == null) { 70 | return false; 71 | } 72 | if (getClass() != obj.getClass()) { 73 | return false; 74 | } 75 | LeaderChangeContext other = (LeaderChangeContext) obj; 76 | if (this.leaderId == null) { 77 | if (other.leaderId != null) { 78 | return false; 79 | } 80 | } else if (!this.leaderId.equals(other.leaderId)) { 81 | return false; 82 | } 83 | if (this.status == null) { 84 | if (other.status != null) { 85 | return false; 86 | } 87 | } else if (!this.status.equals(other.status)) { 88 | return false; 89 | } 90 | return this.term == other.term; 91 | } 92 | 93 | @Override 94 | public String toString() { 95 | return "LeaderChangeContext [leaderId=" + this.leaderId + ", term=" + this.term + ", status=" + this.status 96 | + "]"; 97 | } 98 | 99 | } 100 | 101 | -------------------------------------------------------------------------------- /src/main/java/entity/LogEntry.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | import com.google.protobuf.ZeroByteStringHelper; 4 | import rpc.EnumOutter; 5 | import utils.CrcUtil; 6 | 7 | import java.nio.ByteBuffer; 8 | import java.util.Collection; 9 | import java.util.List; 10 | 11 | /** 12 | * Created by 周思成 on 2020/3/13 16:37 13 | */ 14 | 15 | public class LogEntry implements Checksum { 16 | /** entry type */ 17 | private EnumOutter.EntryType type; 18 | /** log id with index/term */ 19 | private LogId id = new LogId(0, 0); 20 | private PeerId leaderId; 21 | /** log entry current peers */ 22 | private List peers; 23 | /** log entry old peers */ 24 | private List oldPeers; 25 | /** log entry current learners */ 26 | private List learners; 27 | /** log entry old learners */ 28 | private List oldLearners; 29 | /** entry data */ 30 | private ByteBuffer data; 31 | private String dataString; 32 | /** checksum for log entry*/ 33 | private long checksum; 34 | /** true when the log has checksum **/ 35 | private boolean hasChecksum; 36 | 37 | public EnumOutter.EntryType getType() { 38 | return type; 39 | } 40 | 41 | public void setType(EnumOutter.EntryType type) { 42 | this.type = type; 43 | } 44 | 45 | public String getDataString() { 46 | return dataString; 47 | } 48 | 49 | public void setDataString(String dataString) { 50 | this.dataString = dataString; 51 | } 52 | 53 | public LogId getId() { 54 | return id; 55 | } 56 | 57 | public void setId(LogId id) { 58 | this.id = id; 59 | } 60 | 61 | public List getPeers() { 62 | return peers; 63 | } 64 | 65 | public void setPeers(List peers) { 66 | this.peers = peers; 67 | } 68 | 69 | public List getOldPeers() { 70 | return oldPeers; 71 | } 72 | 73 | public void setOldPeers(List oldPeers) { 74 | this.oldPeers = oldPeers; 75 | } 76 | 77 | public List getLearners() { 78 | return learners; 79 | } 80 | 81 | public void setLearners(List learners) { 82 | this.learners = learners; 83 | } 84 | 85 | public List getOldLearners() { 86 | return oldLearners; 87 | } 88 | 89 | public void setOldLearners(List oldLearners) { 90 | this.oldLearners = oldLearners; 91 | } 92 | 93 | public ByteBuffer getData() { 94 | return ByteBuffer.wrap(getDataString().getBytes()); 95 | 96 | } 97 | 98 | public void setData(ByteBuffer data) { 99 | 100 | setDataString( 101 | ZeroByteStringHelper.byteBufferToString(data)); 102 | } 103 | 104 | public long getChecksum() { 105 | return checksum; 106 | } 107 | 108 | public void setChecksum(long checksum) { 109 | this.checksum = checksum; 110 | } 111 | 112 | public boolean isHasChecksum() { 113 | return hasChecksum; 114 | } 115 | 116 | public void setHasChecksum(boolean hasChecksum) { 117 | this.hasChecksum = hasChecksum; 118 | } 119 | 120 | @Override 121 | public long checksum() { 122 | long c = checksum(this.type.getNumber(), this.id.checksum()); 123 | c = checksumPeers(this.peers, c); 124 | c = checksumPeers(this.oldPeers, c); 125 | c = checksumPeers(this.learners, c); 126 | c = checksumPeers(this.oldLearners, c); 127 | if (this.data != null && this.data.hasRemaining()) { 128 | c = checksum(c, CrcUtil.crc64(this.data)); 129 | } 130 | return c; 131 | } 132 | private long checksumPeers(final Collection peers, long c) { 133 | if (peers != null && !peers.isEmpty()) { 134 | for (final PeerId peer : peers) { 135 | c = checksum(c, peer.checksum()); 136 | } 137 | } 138 | return c; 139 | } 140 | 141 | public PeerId getLeaderId() { 142 | return leaderId; 143 | } 144 | 145 | public void setLeaderId(PeerId leaderId) { 146 | this.leaderId = leaderId; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/main/java/entity/LogEntryCodecFactory.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | /** 4 | * Created by 周思成 on 2020/3/13 16:54 5 | */ 6 | 7 | public interface LogEntryCodecFactory { 8 | /** 9 | * Returns a log entry encoder. 10 | * @return encoder instance 11 | */ 12 | LogEntryEncoder encoder(); 13 | 14 | /** 15 | * Returns a log entry decoder. 16 | * @return encoder instance 17 | */ 18 | LogEntryDecoder decoder(); 19 | } -------------------------------------------------------------------------------- /src/main/java/entity/LogEntryDecoder.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | /** 4 | * Created by 周思成 on 2020/3/13 16:55 5 | */ 6 | 7 | public interface LogEntryDecoder { 8 | /** 9 | * Decode a log entry from byte array, 10 | * return null when fail to decode. 11 | * @param bs 12 | * @return 13 | */ 14 | LogEntry decode(byte[] bs); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/entity/LogEntryEncoder.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | import exceptions.RaftException; 4 | 5 | /** 6 | * Created by 周思成 on 2020/3/13 16:54 7 | */ 8 | 9 | public interface LogEntryEncoder { 10 | 11 | /** 12 | * Encode a log entry into a byte array. 13 | * @param log log entry 14 | * @return encoded byte array 15 | */ 16 | byte[] encode(LogEntry log) throws RaftException; 17 | } -------------------------------------------------------------------------------- /src/main/java/entity/LogEntryEvent.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | /** 4 | * Created by 周思成 on 2020/4/9 20:40 5 | */ 6 | 7 | public class LogEntryEvent { 8 | 9 | public LogEntry entry; 10 | public long expectedTerm; 11 | public Closure done; 12 | 13 | public void reset(){ 14 | this.entry = null; 15 | this.expectedTerm = 0; 16 | this.done = null; 17 | } 18 | 19 | public LogEntryEvent(LogEntry entry, long expectedTerm, Closure done) { 20 | this.entry = entry; 21 | this.expectedTerm = expectedTerm; 22 | this.done = done; 23 | } 24 | public LogEntryEvent() { 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/entity/LogId.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | import utils.Bits; 4 | import utils.Copiable; 5 | import utils.CrcUtil; 6 | 7 | import java.io.Serializable; 8 | 9 | /** 10 | * Created by 周思成 on 2020/3/13 16:43 11 | */ 12 | 13 | public class LogId implements Comparable, Copiable, Serializable, Checksum { 14 | 15 | private static final long serialVersionUID = -6680425579347357313L; 16 | 17 | private long index; 18 | private long term; 19 | 20 | @Override 21 | public LogId copy() { 22 | return new LogId(this.index, this.term); 23 | } 24 | 25 | @Override 26 | public long checksum() { 27 | byte[] bs = new byte[16]; 28 | Bits.putLong(bs, 0, this.index); 29 | Bits.putLong(bs, 8, this.term); 30 | return CrcUtil.crc64(bs); 31 | } 32 | 33 | public LogId() { 34 | this(0, 0); 35 | } 36 | 37 | public LogId(final long index, final long term) { 38 | super(); 39 | setIndex(index); 40 | setTerm(term); 41 | } 42 | 43 | @Override 44 | public int hashCode() { 45 | final int prime = 31; 46 | int result = 1; 47 | result = prime * result + (int) (this.index ^ (this.index >>> 32)); 48 | result = prime * result + (int) (this.term ^ (this.term >>> 32)); 49 | return result; 50 | } 51 | 52 | @Override 53 | public boolean equals(final Object obj) { 54 | if (this == obj) { 55 | return true; 56 | } 57 | if (obj == null) { 58 | return false; 59 | } 60 | if (getClass() != obj.getClass()) { 61 | return false; 62 | } 63 | final LogId other = (LogId) obj; 64 | if (this.index != other.index) { 65 | return false; 66 | } 67 | // noinspection RedundantIfStatement 68 | if (this.term != other.term) { 69 | return false; 70 | } 71 | return true; 72 | } 73 | 74 | @Override 75 | public int compareTo(final LogId o) { 76 | // Compare term at first 77 | final int c = Long.compare(getTerm(), o.getTerm()); 78 | if (c == 0) { 79 | return Long.compare(getIndex(), o.getIndex()); 80 | } else { 81 | return c; 82 | } 83 | } 84 | 85 | public long getTerm() { 86 | return this.term; 87 | } 88 | 89 | public void setTerm(final long term) { 90 | this.term = term; 91 | } 92 | 93 | public long getIndex() { 94 | return this.index; 95 | } 96 | 97 | public void setIndex(final long index) { 98 | this.index = index; 99 | } 100 | 101 | @Override 102 | public String toString() { 103 | return "LogId [index=" + this.index + ", term=" + this.term + "]"; 104 | } 105 | 106 | } -------------------------------------------------------------------------------- /src/main/java/entity/LogManager.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | import config.LogManagerOptions; 4 | import core.LogManagerImpl; 5 | import exceptions.LogStorageException; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Created by 周思成 on 2020/4/5 16:43 11 | */ 12 | 13 | public interface LogManager { 14 | 15 | abstract class StableClosure implements Closure { 16 | 17 | protected long firstLogIndex = 0; 18 | protected long lastLogIndex = 0; 19 | protected List entries; 20 | protected int nEntries; 21 | 22 | public StableClosure() { 23 | // NO-OP 24 | } 25 | 26 | public long getFirstLogIndex() { 27 | return this.firstLogIndex; 28 | } 29 | 30 | public void setFirstLogIndex(final long firstLogIndex) { 31 | this.firstLogIndex = firstLogIndex; 32 | } 33 | 34 | public long getLastLogIndex() { 35 | return lastLogIndex; 36 | } 37 | 38 | public void setLastLogIndex(long lastLogIndex) { 39 | this.lastLogIndex = lastLogIndex; 40 | } 41 | 42 | public List getEntries() { 43 | return this.entries; 44 | } 45 | 46 | public void setEntries(final List entries) { 47 | this.entries = entries; 48 | if (entries != null) { 49 | this.nEntries = entries.size(); 50 | } else { 51 | this.nEntries = 0; 52 | } 53 | } 54 | 55 | public StableClosure(final List entries) { 56 | super(); 57 | setEntries(entries); 58 | } 59 | 60 | } 61 | 62 | 63 | /** 64 | * Wait the log manager to be shut down. 65 | * 66 | * @throws InterruptedException if the current thread is interrupted 67 | * while waiting 68 | */ 69 | void join() throws InterruptedException; 70 | 71 | /** 72 | * Append log entry vector and wait until it's stable (NOT COMMITTED!) 73 | * 74 | * @param entries log entries 75 | */ 76 | void appendEntries(final List entries,final StableClosure done); 77 | 78 | /** 79 | * Get the log entry at index. 80 | * 81 | * @param index the index of log entry 82 | * @return the log entry with {@code index} 83 | */ 84 | LogEntry getEntry(final long index); 85 | 86 | 87 | /** 88 | * Get the log term at index. 89 | * 90 | * @param index the index of log entry 91 | * @return the term of log entry 92 | */ 93 | long getTerm(final long index); 94 | 95 | /** 96 | * Get the last log index of log 97 | */ 98 | long getLastLogIndex(); 99 | 100 | /** 101 | * Get the last log index of log 102 | * 103 | * @param isFlush whether to flush from disk. 104 | */ 105 | long getLastLogIndex(final boolean isFlush); 106 | 107 | /** 108 | * Return the id the last log. 109 | * 110 | * @param isFlush whether to flush all pending task. 111 | */ 112 | LogId getLastLogId(final boolean isFlush); 113 | 114 | 115 | boolean init(final LogManagerOptions options) throws LogStorageException; 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/entity/Node.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | import core.ReplicatorStateListener; 4 | import rpc.RpcRequests; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * raft节点 10 | * Created by 周思成 on 2020/3/10 14:47 11 | */ 12 | 13 | 14 | public interface Node { 15 | 16 | /** 17 | * Get the leader peer id for redirect, null if absent. 18 | */ 19 | NodeId getLeaderId(); 20 | 21 | /** 22 | * Get current node id. 23 | */ 24 | NodeId getNodeId(); 25 | 26 | /** 27 | * Start To Perform As the Leader 28 | */ 29 | void startToPerformAsLeader(); 30 | 31 | // RpcRequests.AppendEntriesResponse nullAppendEntriesHandler(RpcRequests.AppendEntriesRequest appendEntriesRequest); 32 | 33 | List getReplicatorStatueListeners(); 34 | 35 | boolean transformLeader(RpcRequests.AppendEntriesRequest request); 36 | 37 | void apply(Task task); 38 | 39 | boolean init(); 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/entity/NodeId.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Created by 周思成 on 2020/3/11 14:20 7 | */ 8 | 9 | /** 10 | * A raft node identifier. 11 | * 12 | * @author boyan (boyan@alibaba-inc.com) 13 | * 14 | * 2018-Apr-03 4:08:14 PM 15 | */ 16 | public final class NodeId implements Serializable { 17 | 18 | private static final long serialVersionUID = 4428173460056804264L; 19 | 20 | /** Raft group id*/ 21 | private final String groupId; 22 | /** Node peer id*/ 23 | private final PeerId peerId; 24 | /** cached toString result*/ 25 | private String str; 26 | 27 | public NodeId(String groupId, PeerId peerId) { 28 | super(); 29 | this.groupId = groupId; 30 | this.peerId = peerId; 31 | } 32 | 33 | public String getGroupId() { 34 | return this.groupId; 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | if (str == null) { 40 | str = "<" + this.groupId + "/" + this.peerId + ">"; 41 | } 42 | return str; 43 | } 44 | 45 | public PeerId getPeerId() { 46 | return this.peerId; 47 | } 48 | 49 | 50 | 51 | @Override 52 | public int hashCode() { 53 | final int prime = 31; 54 | int result = 1; 55 | result = prime * result + (this.groupId == null ? 0 : this.groupId.hashCode()); 56 | result = prime * result + (this.peerId == null ? 0 : this.peerId.hashCode()); 57 | return result; 58 | } 59 | 60 | @Override 61 | public boolean equals(Object obj) { 62 | if (this == obj) { 63 | return true; 64 | } 65 | if (obj == null) { 66 | return false; 67 | } 68 | if (getClass() != obj.getClass()) { 69 | return false; 70 | } 71 | final NodeId other = (NodeId) obj; 72 | if (this.groupId == null) { 73 | if (other.groupId != null) { 74 | return false; 75 | } 76 | } else if (!this.groupId.equals(other.groupId)) { 77 | return false; 78 | } 79 | if (this.peerId == null) { 80 | return other.peerId == null; 81 | } else { 82 | return this.peerId.equals(other.peerId); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/entity/Options.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | import java.util.Arrays; 4 | import java.util.Map; 5 | 6 | /** 7 | * Created by 周思成 on 2020/3/30 21:07 8 | * yaml options entity 9 | */ 10 | 11 | public class Options { 12 | 13 | private CurrentNodeOptions currentNodeOptions; 14 | 15 | 16 | private OtherNodes[] otherNodes; 17 | 18 | 19 | public CurrentNodeOptions getCurrentNodeOptions() { 20 | return currentNodeOptions; 21 | } 22 | 23 | public void setCurrentNodeOptions(CurrentNodeOptions currentNodeOptions) { 24 | this.currentNodeOptions = currentNodeOptions; 25 | } 26 | 27 | public OtherNodes[] getOtherNodes() { 28 | return otherNodes; 29 | } 30 | 31 | public void setOtherNodes(OtherNodes[] otherNodes) { 32 | this.otherNodes = otherNodes; 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return "Options{" + 38 | "currentNodeOptions=" + currentNodeOptions + 39 | ", otherNodes=" + Arrays.toString(otherNodes) + 40 | '}'; 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /src/main/java/entity/OtherNodes.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | /** 4 | * Created by 周思成 on 2020/3/30 21:25 5 | */ 6 | 7 | public class OtherNodes{ 8 | private String peerId; 9 | private String name; 10 | private String address; 11 | private Integer port; 12 | private int taskPort; 13 | 14 | public int getTaskPort() { 15 | return taskPort; 16 | } 17 | 18 | public void setTaskPort(int taskPort) { 19 | this.taskPort = taskPort; 20 | } 21 | 22 | public String getName() { 23 | return name; 24 | } 25 | 26 | public void setName(String name) { 27 | this.name = name; 28 | } 29 | 30 | public String getAddress() { 31 | return address; 32 | } 33 | 34 | public void setAddress(String address) { 35 | this.address = address; 36 | } 37 | 38 | public Integer getPort() { 39 | return port; 40 | } 41 | 42 | public void setPort(Integer port) { 43 | this.port = port; 44 | } 45 | 46 | public String getPeerId() { 47 | return peerId; 48 | } 49 | 50 | public void setPeerId(String peerId) { 51 | this.peerId = peerId; 52 | } 53 | 54 | @Override 55 | public String toString() { 56 | return "OtherNodes{" + 57 | "peerId='" + peerId + '\'' + 58 | ", name='" + name + '\'' + 59 | ", address='" + address + '\'' + 60 | ", port='" + port + '\'' + 61 | '}'; 62 | } 63 | } -------------------------------------------------------------------------------- /src/main/java/entity/PeerId.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | import java.io.Serializable; 4 | import java.util.Objects; 5 | 6 | import com.alipay.remoting.util.StringUtils; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import utils.AsciiStringUtil; 10 | import utils.CrcUtil; 11 | import utils.Utils; 12 | 13 | 14 | 15 | 16 | /** 17 | * Represent a participant in a replicating group. 18 | * Created by 周思成 on 2020/3/10 14:51 19 | */ 20 | 21 | 22 | public class PeerId implements Serializable,Checksum { 23 | 24 | private static final Logger LOG = LoggerFactory.getLogger(PeerId.class); 25 | 26 | /** Peer address. */ 27 | private Endpoint endpoint; 28 | 29 | 30 | public static final PeerId ANY_PEER = new PeerId(); 31 | 32 | 33 | private long checksum; 34 | 35 | /** 36 | * peerName and id are equally the same 37 | */ 38 | private String peerName; 39 | private String id; 40 | 41 | private int taskPort; 42 | public PeerId() { 43 | 44 | } 45 | 46 | public int getTaskPort() { 47 | return taskPort; 48 | } 49 | 50 | public void setTaskPort(int taskPort) { 51 | this.taskPort = taskPort; 52 | } 53 | 54 | public String getId() { 55 | return id; 56 | } 57 | 58 | public void setId(String id) { 59 | this.id = id; 60 | } 61 | 62 | public PeerId(String id,String peerName,String address,Integer port,Integer taskPort) { 63 | this.endpoint = new Endpoint(address,port); 64 | this.id = id; 65 | this.peerName = peerName; 66 | this.taskPort = taskPort; 67 | } 68 | 69 | public PeerId copy() { 70 | return new PeerId(); 71 | } 72 | @Override 73 | public long checksum() { 74 | if (this.checksum == 0) { 75 | this.checksum = CrcUtil.crc64(AsciiStringUtil.unsafeEncode(toString())); 76 | } 77 | return this.checksum; 78 | } 79 | 80 | 81 | public String getPeerName() { 82 | return peerName; 83 | } 84 | 85 | public void setPeerName(String peerName) { 86 | this.peerName = peerName; 87 | } 88 | 89 | /** 90 | * Create an empty peer. 91 | * @return empty peer 92 | */ 93 | public static PeerId emptyPeer() { 94 | return new PeerId(); 95 | } 96 | 97 | 98 | public PeerId(Endpoint endpoint) { 99 | this.endpoint = endpoint; 100 | } 101 | 102 | public static Logger getLOG() { 103 | return LOG; 104 | } 105 | 106 | public Endpoint getEndpoint() { 107 | return endpoint; 108 | } 109 | 110 | public void setEndpoint(Endpoint endpoint) { 111 | this.endpoint = endpoint; 112 | } 113 | 114 | public static PeerId getAnyPeer() { 115 | return ANY_PEER; 116 | } 117 | 118 | public long getChecksum() { 119 | return checksum; 120 | } 121 | 122 | public void setChecksum(long checksum) { 123 | this.checksum = checksum; 124 | } 125 | 126 | 127 | @Override 128 | public String toString() { 129 | return "PeerId{" + 130 | "endpoint=" + endpoint + 131 | ", checksum=" + checksum + 132 | '}'; 133 | } 134 | 135 | /** 136 | * Parse peerId from string that generated by {@link #toString()} 137 | * This method can support parameter string values are below: 138 | * 139 | *
140 |      * PeerId.parse("a:b")          = new PeerId("a", "b", 0 , -1)
141 |      * PeerId.parse("a:b:c")        = new PeerId("a", "b", "c", -1)
142 |      * PeerId.parse("a:b::d")       = new PeerId("a", "b", 0, "d")
143 |      * PeerId.parse("a:b:c:d")      = new PeerId("a", "b", "c", "d")
144 |      * 
145 | * 146 | */ 147 | public boolean parse(final String s) { 148 | if (StringUtils.isEmpty(s)) { 149 | return false; 150 | } 151 | 152 | final String[] tmps = StringUtils.split(s, ':'); 153 | if (tmps.length < 2 ) { 154 | return false; 155 | } 156 | try { 157 | final int port = Integer.parseInt(tmps[1]); 158 | this.endpoint = new Endpoint(tmps[0], port); 159 | 160 | 161 | return true; 162 | } catch (final Exception e) { 163 | LOG.error("Parse peer from string failed: {}.", s, e); 164 | return false; 165 | } 166 | } 167 | 168 | @Override 169 | public boolean equals(Object o) { 170 | if (this == o) return true; 171 | if (o == null || getClass() != o.getClass()) return false; 172 | PeerId peerId = (PeerId) o; 173 | return taskPort == peerId.taskPort && 174 | endpoint.equals(peerId.endpoint) && 175 | Objects.equals(peerName, peerId.peerName) && 176 | Objects.equals(id, peerId.id); 177 | } 178 | 179 | @Override 180 | public int hashCode() { 181 | return Objects.hash(endpoint, peerName, id, taskPort); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/main/java/entity/RaftError.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | /** 4 | * Created by 周思成 on 2020/3/11 16:29 5 | */ 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | /** 11 | * Raft error code. 12 | * @author Mike 13 | */ 14 | public enum RaftError { 15 | 16 | /** 17 | * Unknown error 18 | */ 19 | UNKNOWN(-1), 20 | 21 | /** 22 | * Success, no error. 23 | */ 24 | SUCCESS(0), 25 | 26 | /** 27 | *
 28 |      * All Kinds of Timeout(Including Election_timeout, Timeout_now, Stepdown_timeout)
 29 |      * 
30 | *

31 | * ERAFTTIMEDOUT = 10001; 32 | */ 33 | ERAFTTIMEDOUT(10001), 34 | 35 | /** 36 | *

 37 |      * Bad User State Machine
 38 |      * 
39 | *

40 | * ESTATEMACHINE = 10002; 41 | */ 42 | ESTATEMACHINE(10002), 43 | 44 | /** 45 | *

 46 |      * Catchup Failed
 47 |      * 
48 | *

49 | * ECATCHUP = 10003; 50 | */ 51 | ECATCHUP(10003), 52 | 53 | /** 54 | *

 55 |      * Trigger step_down(Not All)
 56 |      * 
57 | *

58 | * ELEADERREMOVED = 10004; 59 | */ 60 | ELEADERREMOVED(10004), 61 | 62 | /** 63 | *

 64 |      * Leader Is Not In The New Configuration
 65 |      * 
66 | *

67 | * ESETPEER = 10005; 68 | */ 69 | ESETPEER(10005), 70 | 71 | /** 72 | *

 73 |      * Shut_down
 74 |      * 
75 | *

76 | * ENODESHUTDOWN = 10006; 77 | */ 78 | ENODESHUTDOWN(10006), 79 | 80 | /** 81 | *

 82 |      * Receive Higher Term Requests
 83 |      * 
84 | *

85 | * EHIGHERTERMREQUEST = 10007; 86 | */ 87 | EHIGHERTERMREQUEST(10007), 88 | 89 | /** 90 | *

 91 |      * Receive Higher Term Response
 92 |      * 
93 | *

94 | * EHIGHERTERMRESPONSE = 10008; 95 | */ 96 | EHIGHERTERMRESPONSE(10008), 97 | 98 | /** 99 | *

100 |      * Node Is In Error
101 |      * 
102 | *

103 | * EBADNODE = 10009; 104 | */ 105 | EBADNODE(10009), 106 | 107 | /** 108 | *

109 |      * Node Votes For Some Candidate
110 |      * 
111 | *

112 | * EVOTEFORCANDIDATE = 10010; 113 | */ 114 | EVOTEFORCANDIDATE(10010), 115 | 116 | /** 117 | *

118 |      * Follower(without leader) or Candidate Receives
119 |      * 
120 | *

121 | * ENEWLEADER = 10011; 122 | */ 123 | ENEWLEADER(10011), 124 | 125 | /** 126 | *

127 |      * Append_entries/Install_snapshot Request from a new leader
128 |      * 
129 | *

130 | * ELEADERCONFLICT = 10012; 131 | */ 132 | ELEADERCONFLICT(10012), 133 | 134 | /** 135 | *

136 |      * Trigger on_leader_stop
137 |      * 
138 | *

139 | * ETRANSFERLEADERSHIP = 10013; 140 | */ 141 | ETRANSFERLEADERSHIP(10013), 142 | 143 | /** 144 | *

145 |      * The log at the given index is deleted
146 |      * 
147 | *

148 | * ELOGDELETED = 10014; 149 | */ 150 | ELOGDELETED(10014), 151 | 152 | /** 153 | *

154 |      * No available user log to read
155 |      * 
156 | *

157 | * ENOMOREUSERLOG = 10015; 158 | */ 159 | ENOMOREUSERLOG(10015), 160 | 161 | /* other non-raft error codes 1000~10000 */ 162 | /** 163 | * Invalid rpc request 164 | */ 165 | EREQUEST(1000), 166 | 167 | /** 168 | * Task is stopped 169 | */ 170 | ESTOP(1001), 171 | 172 | /** 173 | * Retry again 174 | */ 175 | EAGAIN(1002), 176 | 177 | /** 178 | * Interrupted 179 | */ 180 | EINTR(1003), 181 | 182 | /** 183 | * Internal exception 184 | */ 185 | EINTERNAL(1004), 186 | 187 | /** 188 | * Task is canceled 189 | */ 190 | ECANCELED(1005), 191 | 192 | /** 193 | * Host is down 194 | */ 195 | EHOSTDOWN(1006), 196 | 197 | /** 198 | * Service is shutdown 199 | */ 200 | ESHUTDOWN(1007), 201 | 202 | /** 203 | * Permission issue 204 | */ 205 | EPERM(1008), 206 | 207 | /** 208 | * Server is in busy state 209 | */ 210 | EBUSY(1009), 211 | 212 | /** 213 | * Timed out 214 | */ 215 | ETIMEDOUT(1010), 216 | 217 | /** 218 | * Data is stale 219 | */ 220 | ESTALE(1011), 221 | 222 | /** 223 | * Something not found 224 | */ 225 | ENOENT(1012), 226 | 227 | /** 228 | * File/folder already exists 229 | */ 230 | EEXISTS(1013), 231 | 232 | /** 233 | * IO error 234 | */ 235 | EIO(1014), 236 | 237 | /** 238 | * Invalid value. 239 | */ 240 | EINVAL(1015), 241 | 242 | /** 243 | * Permission denied 244 | */ 245 | EACCES(1016); 246 | 247 | private static final Map RAFT_ERROR_MAP = new HashMap<>(); 248 | 249 | static { 250 | for (final RaftError error : RaftError.values()) { 251 | RAFT_ERROR_MAP.put(error.getNumber(), error); 252 | } 253 | } 254 | 255 | public final int getNumber() { 256 | return this.value; 257 | } 258 | 259 | public static RaftError forNumber(final int value) { 260 | return RAFT_ERROR_MAP.getOrDefault(value, UNKNOWN); 261 | } 262 | 263 | public static String describeCode(final int code) { 264 | RaftError e = forNumber(code); 265 | return e != null ? e.name() : ""; 266 | } 267 | 268 | private final int value; 269 | 270 | RaftError(final int value) { 271 | this.value = value; 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /src/main/java/entity/ReadEvent.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | /** 4 | * Created by 周思成 on 2020/4/9 20:40 5 | */ 6 | 7 | public class ReadEvent { 8 | 9 | public byte[] entry; 10 | public long expectedIndex; 11 | public Closure done; 12 | 13 | public void reset(){ 14 | this.entry = null; 15 | this.expectedIndex = 0; 16 | this.done = null; 17 | } 18 | 19 | public ReadEvent(byte[] entry, long expectedIndex, Closure done) { 20 | this.entry = entry; 21 | this.expectedIndex = expectedIndex; 22 | this.done = done; 23 | } 24 | public ReadEvent() { 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/entity/ReadTask.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Created by 周思成 on 2020/5/8 12:39 7 | */ 8 | 9 | public class ReadTask implements Serializable { 10 | 11 | private static final long serialVersionUID = -4427655712599189827L; 12 | 13 | 14 | private byte[] taskBytes; 15 | 16 | public byte[] getTaskBytes() { 17 | return taskBytes; 18 | } 19 | 20 | public void setTaskBytes(byte[] taskBytes) { 21 | this.taskBytes = taskBytes; 22 | } 23 | 24 | public ReadTask(byte[] taskBytes) { 25 | this.taskBytes = taskBytes; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/entity/ReadTaskResponse.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Created by 周思成 on 2020/5/8 12:39 7 | */ 8 | 9 | public class ReadTaskResponse implements Serializable { 10 | 11 | private static final long serialVersionUID = -4427655712599189824L; 12 | 13 | 14 | private boolean response; 15 | 16 | private String msg; 17 | 18 | public ReadTaskResponse(boolean response, String msg) { 19 | this.response = response; 20 | this.msg = msg; 21 | } 22 | public ReadTaskResponse(){ 23 | 24 | } 25 | 26 | public boolean isResponse() { 27 | return response; 28 | } 29 | 30 | public void setResponse(boolean response) { 31 | this.response = response; 32 | } 33 | 34 | public String getMsg() { 35 | return msg; 36 | } 37 | 38 | public void setMsg(String msg) { 39 | this.msg = msg; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/entity/ReadTaskSchedule.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Created by 周思成 on 2020/5/8 17:26 7 | */ 8 | 9 | /** 10 | * A scheduled task for read 11 | */ 12 | public class ReadTaskSchedule { 13 | 14 | private List readEventList; 15 | private long index; 16 | private boolean finished =false; 17 | 18 | public List getReadEventList() { 19 | return readEventList; 20 | } 21 | 22 | public void setReadEventList(List readEventList) { 23 | this.readEventList = readEventList; 24 | } 25 | 26 | public long getIndex() { 27 | return index; 28 | } 29 | 30 | public void setIndex(long index) { 31 | this.index = index; 32 | } 33 | 34 | public boolean isFinished() { 35 | return finished; 36 | } 37 | 38 | public void setFinished(boolean finished) { 39 | this.finished = finished; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/entity/ReadTaskWaiter.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | /** 4 | * Created by 周思成 on 2020/5/8 13:28 5 | */ 6 | 7 | public class ReadTaskWaiter { 8 | 9 | private long currentLogIndex; 10 | 11 | private String currentLeader; 12 | 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/entity/ReplicatorGroupOptions.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | import core.NodeImpl; 4 | import utils.TimerManager; 5 | 6 | /** 7 | * Created by 周思成 on 2020/4/14 15:33 8 | */ 9 | 10 | public class ReplicatorGroupOptions { 11 | 12 | private long heartbeatTimeoutMs; 13 | private long electionTimeoutMs; 14 | private LogManager logManager; 15 | //private BallotBox ballotBox; 16 | private NodeImpl node; 17 | 18 | public ReplicatorGroupOptions(long heartbeatTimeoutMs, 19 | long electionTimeoutMs, 20 | LogManager logManager 21 | ) { 22 | this.heartbeatTimeoutMs = heartbeatTimeoutMs; 23 | this.electionTimeoutMs = electionTimeoutMs; 24 | this.logManager = logManager; 25 | 26 | 27 | } 28 | 29 | private TimerManager timerManager; 30 | 31 | public TimerManager getTimerManager() { 32 | return this.timerManager; 33 | } 34 | 35 | public void setTimerManager(TimerManager timerManager) { 36 | this.timerManager = timerManager; 37 | } 38 | 39 | 40 | 41 | 42 | public void setHeartbeatTimeoutMs(int heartbeatTimeoutMs) { 43 | this.heartbeatTimeoutMs = heartbeatTimeoutMs; 44 | } 45 | 46 | 47 | public void setElectionTimeoutMs(int electionTimeoutMs) { 48 | this.electionTimeoutMs = electionTimeoutMs; 49 | } 50 | 51 | public LogManager getLogManager() { 52 | return this.logManager; 53 | } 54 | 55 | public void setLogManager(LogManager logManager) { 56 | this.logManager = logManager; 57 | } 58 | 59 | public long getHeartbeatTimeoutMs() { 60 | return heartbeatTimeoutMs; 61 | } 62 | 63 | public long getElectionTimeoutMs() { 64 | return electionTimeoutMs; 65 | } 66 | 67 | public NodeImpl getNode() { 68 | return this.node; 69 | } 70 | 71 | public void setNode(NodeImpl node) { 72 | this.node = node; 73 | } 74 | 75 | 76 | public void setHeartbeatTimeoutMs(long heartbeatTimeoutMs) { 77 | this.heartbeatTimeoutMs = heartbeatTimeoutMs; 78 | } 79 | 80 | @Override 81 | public String toString() { 82 | return "ReplicatorGroupOptions{" + 83 | "heartbeatTimeoutMs=" + heartbeatTimeoutMs + 84 | ", electionTimeoutMs=" + electionTimeoutMs + 85 | ", logManager=" + logManager + 86 | ", node=" + node + 87 | ", timerManager=" + timerManager + 88 | '}'; 89 | } 90 | 91 | public void setElectionTimeoutMs(long electionTimeoutMs) { 92 | this.electionTimeoutMs = electionTimeoutMs; 93 | } 94 | } -------------------------------------------------------------------------------- /src/main/java/entity/ReplicatorType.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | /** 4 | * Created by 周思成 on 2020/4/6 7:34 5 | */ 6 | 7 | public enum ReplicatorType { 8 | Follower, Learner; 9 | 10 | public final boolean isFollower() { 11 | return this == Follower; 12 | } 13 | 14 | public final boolean isLearner() { 15 | return this == Learner; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/entity/RocksBatch.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | /** 4 | * Created by 周思成 on 2020/4/17 12:39 5 | */ 6 | 7 | public class RocksBatch { 8 | 9 | 10 | private byte[] key; 11 | private byte[] value; 12 | 13 | public RocksBatch(byte[] key, byte[] value) { 14 | this.key = key; 15 | this.value = value; 16 | } 17 | 18 | public byte[] getKey() { 19 | return key; 20 | } 21 | 22 | public void setKey(byte[] key) { 23 | this.key = key; 24 | } 25 | 26 | public byte[] getValue() { 27 | return value; 28 | } 29 | 30 | public void setValue(byte[] value) { 31 | this.value = value; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/entity/RpcOptions.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | /** 4 | * Created by 周思成 on 2020/3/11 14:31 5 | */ 6 | 7 | public class RpcOptions { 8 | 9 | 10 | /** 11 | * Rpc connect timeout in milliseconds 12 | * Default: 1000(1s) 13 | */ 14 | private int rpcConnectTimeoutMs = 1000; 15 | 16 | /** 17 | * RPC.proto request default timeout in milliseconds 18 | * Default: 5000(5s) 19 | */ 20 | private int rpcDefaultTimeout = 5000; 21 | 22 | /** 23 | * Install snapshot RPC.proto request default timeout in milliseconds 24 | * Default: 5 * 60 * 1000(5min) 25 | */ 26 | private int rpcInstallSnapshotTimeout = 5 * 60 * 1000; 27 | 28 | /** 29 | * RPC.proto process thread pool size 30 | * Default: 80 31 | */ 32 | private int rpcProcessorThreadPoolSize = 80; 33 | 34 | /** 35 | * Whether to enable checksum for RPC.proto. 36 | * Default: false 37 | */ 38 | private boolean enableRpcChecksum = false; 39 | 40 | 41 | public int getRpcConnectTimeoutMs() { 42 | return rpcConnectTimeoutMs; 43 | } 44 | 45 | public void setRpcConnectTimeoutMs(int rpcConnectTimeoutMs) { 46 | this.rpcConnectTimeoutMs = rpcConnectTimeoutMs; 47 | } 48 | 49 | public int getRpcDefaultTimeout() { 50 | return rpcDefaultTimeout; 51 | } 52 | 53 | public void setRpcDefaultTimeout(int rpcDefaultTimeout) { 54 | this.rpcDefaultTimeout = rpcDefaultTimeout; 55 | } 56 | 57 | public int getRpcInstallSnapshotTimeout() { 58 | return rpcInstallSnapshotTimeout; 59 | } 60 | 61 | public void setRpcInstallSnapshotTimeout(int rpcInstallSnapshotTimeout) { 62 | this.rpcInstallSnapshotTimeout = rpcInstallSnapshotTimeout; 63 | } 64 | 65 | public int getRpcProcessorThreadPoolSize() { 66 | return rpcProcessorThreadPoolSize; 67 | } 68 | 69 | public void setRpcProcessorThreadPoolSize(int rpcProcessorThreadPoolSize) { 70 | this.rpcProcessorThreadPoolSize = rpcProcessorThreadPoolSize; 71 | } 72 | 73 | public boolean isEnableRpcChecksum() { 74 | return enableRpcChecksum; 75 | } 76 | 77 | public void setEnableRpcChecksum(boolean enableRpcChecksum) { 78 | this.enableRpcChecksum = enableRpcChecksum; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/entity/RpcResult.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | /** 4 | * Created by 周思成 on 2020/4/24 16:48 5 | */ 6 | 7 | public class RpcResult { 8 | 9 | boolean success = false; 10 | 11 | public boolean isSuccess() { 12 | return success; 13 | } 14 | 15 | public void setSuccess(boolean success) { 16 | this.success = success; 17 | } 18 | 19 | public static RpcResult newRpcResult() { 20 | return new RpcResult(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/entity/StateMachine.java: -------------------------------------------------------------------------------- 1 | //package entity; 2 | // 3 | // 4 | //import exceptions.RaftException; 5 | // 6 | ///** 7 | // * Created by 周思成 on 2020/3/11 14:44 8 | // * @author Mike 9 | // */ 10 | // 11 | //public interface StateMachine { 12 | // 13 | // /** 14 | // * Update the StateMachine with a batch a tasks that can be accessed 15 | // * through |iterator|. 16 | // * 17 | // * Invoked when one or more tasks that were passed to Node#apply(Task) have been 18 | // * committed to the raft group (quorum of the group peers have received 19 | // * those tasks and stored them on the backing storage). 20 | // * 21 | // * Once this function returns to the caller, we will regard all the iterated 22 | // * tasks through |iter| have been successfully applied. And if you didn't 23 | // * apply all the the given tasks, we would regard this as a critical error 24 | // * and report a error whose type is ERROR_TYPE_STATE_MACHINE. 25 | // * 26 | // * @param iter iterator of states 27 | // */ 28 | // void onApply(final Iterator iter); 29 | // 30 | // /** 31 | // * Invoked once when the raft node was shut down. 32 | // * Default do nothing 33 | // */ 34 | // void onShutdown(); 35 | // 36 | // /** 37 | // * Invoked when the belonging node becomes the leader of the group at |term| 38 | // * Default: Do nothing 39 | // * 40 | // * @param term new term num 41 | // */ 42 | // void onLeaderStart(final long term); 43 | // 44 | // 45 | // /** 46 | // * Invoked when this node steps down from the leader of the replication 47 | // * group and |status| describes detailed information 48 | // * 49 | // * @param status status info 50 | // */ 51 | // void onLeaderStop(final Status status); 52 | // 53 | // 54 | // /** 55 | // * This method is called when a critical error was encountered, after this 56 | // * point, no any further modification is allowed to applied to this node 57 | // * until the error is fixed and this node restarts. 58 | // * 59 | // * @param e raft error message 60 | // */ 61 | // void onError(final RaftException e); 62 | // 63 | // 64 | // /** 65 | // * This method is called when a follower stops following a leader and its leaderId becomes null, 66 | // * situations including: 67 | // * 1. handle election timeout and start preVote 68 | // * 2. receive requests with higher term such as VoteRequest from a candidate 69 | // * or appendEntries request from a new leader 70 | // * 3. receive timeoutNow request from current leader and start request vote. 71 | // * 72 | // * the parameter ctx gives the information(leaderId, term and status) about the 73 | // * very leader whom the follower followed before. 74 | // * User can reset the node's information as it stops following some leader. 75 | // * 76 | // * @param ctx context of leader change 77 | // */ 78 | // void onStopFollowing(final LeaderChangeContext ctx); 79 | // 80 | // 81 | // /** 82 | // * This method is called when a follower or candidate starts following a leader and its leaderId 83 | // * (should be NULL before the method is called) is set to the leader's id, 84 | // * situations including: 85 | // * 1. a candidate receives appendEntries request from a leader 86 | // * 2. a follower(without leader) receives appendEntries from a leader 87 | // * 88 | // * the parameter ctx gives the information(leaderId, term and status) about 89 | // * the very leader whom the follower starts to follow. 90 | // * User can reset the node's information as it starts to follow some leader. 91 | // * 92 | // * @param ctx context of leader change 93 | // */ 94 | // void onStartFollowing(final LeaderChangeContext ctx); 95 | // 96 | //} 97 | -------------------------------------------------------------------------------- /src/main/java/entity/Task.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | 4 | 5 | import java.io.Serializable; 6 | import java.nio.ByteBuffer; 7 | 8 | /** 9 | * Created by 周思成 on 2020/4/8 16:41 10 | */ 11 | 12 | public class Task implements Serializable { 13 | 14 | 15 | private static final long serialVersionUID = -4427655712599189826L; 16 | private ByteBuffer data; 17 | 18 | private Closure done; 19 | 20 | private long expectedTerm = -1; 21 | 22 | 23 | /** 24 | * Creates a task with data/done. 25 | */ 26 | public Task(ByteBuffer data, Closure done) { 27 | super(); 28 | this.data = data; 29 | this.done = done; 30 | } 31 | 32 | public Task() { 33 | 34 | } 35 | public Task(ByteBuffer data, Closure done, long expectedTerm) { 36 | super(); 37 | this.data = data; 38 | this.done = done; 39 | this.expectedTerm = expectedTerm; 40 | } 41 | 42 | public ByteBuffer getData() { 43 | return data; 44 | } 45 | 46 | public void setData(ByteBuffer data) { 47 | this.data = data; 48 | } 49 | 50 | public Closure getDone() { 51 | return done; 52 | } 53 | 54 | public void setDone(Closure done) { 55 | this.done = done; 56 | } 57 | 58 | public long getExpectedTerm() { 59 | return expectedTerm; 60 | } 61 | 62 | public void setExpectedTerm(long expectedTerm) { 63 | this.expectedTerm = expectedTerm; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/entity/TimeOutChecker.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | import core.NodeImpl; 4 | import core.RaftGroupService; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import service.ElectionService; 8 | import utils.Utils; 9 | 10 | import java.util.concurrent.TimeUnit; 11 | import java.util.concurrent.locks.Condition; 12 | 13 | /** 14 | * Created by 周思成 on 2020/3/25 12:36 15 | */ 16 | 17 | public class TimeOutChecker implements Runnable{ 18 | 19 | private static final Logger LOG = LoggerFactory.getLogger(RaftGroupService.class); 20 | 21 | /** 22 | * 超时时间 23 | */ 24 | private long timeOut = NodeImpl.getNodeImple().getOptions().getCurrentNodeOptions().getMaxHeartBeatTime(); 25 | //private long timeOut = 1000; 26 | 27 | /** 28 | * 进入线程池的时间 29 | */ 30 | private long enterQueueTime; 31 | 32 | /** 33 | * time out closure 34 | */ 35 | private TimeOutClosure timeOutClosure; 36 | private String uuid; 37 | 38 | public TimeOutChecker(long enterQueueTime,TimeOutClosure timeOutClosure,String uuid) { 39 | this.timeOutClosure = timeOutClosure; 40 | this.enterQueueTime = enterQueueTime; 41 | this.uuid = uuid; 42 | //this.timeOut = timeOut; 43 | } 44 | 45 | @Override 46 | public void run() { 47 | 48 | 49 | 50 | NodeImpl.getNodeImple().getHeartbeat().getLock().lock(); 51 | synchronized(NodeImpl.getNodeImple().getHeartbeat().getChecker()) { 52 | try { 53 | //检查开始运行的时间,是否已经超过timeout时间,如果超出则直接检查 54 | long currentTimeDifferent = Utils.monotonicMs() - enterQueueTime; 55 | LOG.debug("currentTimeDifferent:{} uuid {}",currentTimeDifferent,uuid); 56 | if (currentTimeDifferent > timeOut) { 57 | //在队列里等待的时候就已经超时了,检查node是否超时 58 | LOG.error("Timeout during waiting int queue"); 59 | checkTimeOut(); 60 | } else { 61 | //还未超时,等待检测超时 62 | try { 63 | LOG.debug("thread wait:"+(currentTimeDifferent)); 64 | long u = Utils.monotonicMs(); 65 | NodeImpl.getNodeImple().getHeartbeat() 66 | .getChecker().wait(timeOut - currentTimeDifferent); 67 | System.out.println("wait:"+(Utils.monotonicMs()-u)); 68 | } catch (InterruptedException e) { 69 | e.printStackTrace(); 70 | } 71 | LOG.debug("Thread wake up"); 72 | checkTimeOut(); 73 | 74 | } 75 | } catch (Exception e) { 76 | LOG.error("Heartbeat timeout checker exception: " + e.getMessage() + "uuid:"+uuid); 77 | e.printStackTrace(); 78 | } finally { 79 | NodeImpl.getNodeImple().getHeartbeat().getLock().unlock(); 80 | } 81 | 82 | 83 | } 84 | 85 | } 86 | 87 | public long getEnterQueueTime() { 88 | return enterQueueTime; 89 | } 90 | 91 | public void setEnterQueueTime(long enterQueueTime) { 92 | this.enterQueueTime = enterQueueTime; 93 | } 94 | // private void checkNodeStatus() { 95 | // 96 | // 97 | // } 98 | 99 | private void checkTimeOut() { 100 | LOG.debug("CkeckTimeout:{}",Utils.monotonicMs() 101 | - NodeImpl.getNodeImple().getLastReceiveHeartbeatTime().longValue()+ "uuid:"+uuid); 102 | LOG.debug("enterQueueTime:{}",enterQueueTime 103 | - Utils.monotonicMs()+ "uuid:"+uuid); 104 | if ((Utils.monotonicMs() 105 | - NodeImpl.getNodeImple().getLastReceiveHeartbeatTime().longValue()) >= timeOut) { 106 | //超时,执行超时逻辑 107 | LOG.error("Node timeout, start to launch TimeOut actions"); 108 | //timeOutClosure.run(null); 109 | ElectionService.checkToStartPreVote(); 110 | }else { 111 | //未超时 112 | 113 | 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/entity/TimeOutClosure.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | /** 4 | * Created by 周思成 on 2020/4/1 16:10 5 | */ 6 | 7 | public interface TimeOutClosure extends Closure { 8 | 9 | 10 | long getBaseTime(); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/exceptions/ElectionException.java: -------------------------------------------------------------------------------- 1 | package exceptions; 2 | 3 | /** 4 | * Created by 周思成 on 2020/3/24 16:19 5 | */ 6 | 7 | public class ElectionException extends Throwable { 8 | 9 | private String errMsg; 10 | 11 | public String getErrMsg() { 12 | return errMsg; 13 | } 14 | 15 | public void setErrMsg(String errMsg) { 16 | this.errMsg = errMsg; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/exceptions/LogExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package exceptions; 2 | 3 | import com.lmax.disruptor.ExceptionHandler; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | /** 8 | * Created by 周思成 on 2020/4/12 19:57 9 | */ 10 | 11 | public class LogExceptionHandler implements ExceptionHandler { 12 | private static final Logger LOG = LoggerFactory.getLogger(LogExceptionHandler.class); 13 | 14 | public interface OnEventException { 15 | 16 | void onException(T event, Throwable ex); 17 | } 18 | 19 | private final String name; 20 | private final OnEventException onEventException; 21 | 22 | public LogExceptionHandler(String name) { 23 | this(name, null); 24 | } 25 | 26 | public LogExceptionHandler(String name, OnEventException onEventException) { 27 | this.name = name; 28 | this.onEventException = onEventException; 29 | } 30 | 31 | @Override 32 | public void handleOnStartException(Throwable ex) { 33 | LOG.error("Fail to start {} disruptor", this.name, ex); 34 | } 35 | 36 | @Override 37 | public void handleOnShutdownException(Throwable ex) { 38 | LOG.error("Fail to shutdown {}r disruptor", this.name, ex); 39 | 40 | } 41 | 42 | @Override 43 | public void handleEventException(Throwable ex, long sequence, T event) { 44 | LOG.error("Handle {} disruptor event error, event is {}", this.name, event, ex); 45 | if (this.onEventException != null) { 46 | this.onEventException.onException(event, ex); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/exceptions/LogStorageException.java: -------------------------------------------------------------------------------- 1 | package exceptions; 2 | 3 | /** 4 | * Created by 周思成 on 2020/3/24 16:19 5 | */ 6 | 7 | public class LogStorageException extends Throwable { 8 | 9 | private String errMsg; 10 | 11 | public String getErrMsg() { 12 | return errMsg; 13 | } 14 | 15 | public void setErrMsg(String errMsg) { 16 | this.errMsg = errMsg; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/exceptions/RaftException.java: -------------------------------------------------------------------------------- 1 | package exceptions; 2 | 3 | import entity.Status; 4 | import rpc.EnumOutter; 5 | 6 | /** 7 | * Created by 周思成 on 2020/3/11 16:36 8 | */ 9 | 10 | public class RaftException extends Throwable{ 11 | 12 | private static final long serialVersionUID = -1533343555230409704L; 13 | /** Error status*/ 14 | private Status status = Status.OK(); 15 | /** 16 | * Error type 17 | */ 18 | private EnumOutter.ErrorType type; 19 | public RaftException(Status status) { 20 | this.status = status; 21 | } 22 | public RaftException(EnumOutter.ErrorType type) { 23 | super(type.name()); 24 | this.type = type; 25 | } 26 | 27 | public RaftException() { 28 | 29 | } 30 | 31 | public Status getStatus() { 32 | return status; 33 | } 34 | 35 | public void setStatus(Status status) { 36 | this.status = status; 37 | } 38 | 39 | @Override 40 | public String toString() { 41 | return "RaftException{" + 42 | "status=" + status + 43 | '}'; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/rpc/ClientRpcService.java: -------------------------------------------------------------------------------- 1 | package rpc; 2 | 3 | import entity.ReadTask; 4 | import entity.Status; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Created by 周思成 on 2020/5/8 21:09 10 | */ 11 | 12 | public interface ClientRpcService { 13 | 14 | void applied(Status status); 15 | 16 | void appliedBatches(List statusList); 17 | 18 | void readResult(ReadTask readTask); 19 | 20 | void readResults(List readTaskList); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/rpc/ClientRpcServiceImpl.java: -------------------------------------------------------------------------------- 1 | package rpc; 2 | 3 | import entity.ReadTask; 4 | import entity.Status; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import java.nio.charset.StandardCharsets; 9 | import java.util.List; 10 | import java.util.concurrent.atomic.AtomicInteger; 11 | 12 | /** 13 | * Created by 周思成 on 2020/5/8 21:12 14 | */ 15 | 16 | public class ClientRpcServiceImpl implements ClientRpcService { 17 | 18 | public static final Logger LOG = LoggerFactory.getLogger(ClientRpcServiceImpl.class); 19 | public static AtomicInteger atomicInteger = new AtomicInteger(0); 20 | @Override 21 | public void applied(Status status) { 22 | LOG.info("Receive status {}",status); 23 | } 24 | 25 | @Override 26 | public void appliedBatches(List statusList) { 27 | LOG.info("Receive statusList {}",statusList); 28 | } 29 | 30 | @Override 31 | public void readResult(ReadTask readTask) { 32 | try { 33 | LOG.info("Receive ReadTask {}", readTask); 34 | String result = new String(readTask.getTaskBytes(), StandardCharsets.UTF_8); 35 | LOG.info("Read result:" + result); 36 | atomicInteger.addAndGet(1); 37 | } catch (Exception e) { 38 | e.printStackTrace(); 39 | LOG.error("readResult error:{}",e.getMessage()); 40 | } 41 | 42 | } 43 | 44 | @Override 45 | public void readResults(List readTaskList) { 46 | LOG.info("Receive readTaskList {}",readTaskList); 47 | for (ReadTask r : 48 | readTaskList) { 49 | readResult(r); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/rpc/LogEntryCodecFactory.java: -------------------------------------------------------------------------------- 1 | package rpc; 2 | 3 | import entity.LogEntryDecoder; 4 | 5 | /** 6 | * Created by 周思成 on 2020/5/7 12:25 7 | */ 8 | 9 | public interface LogEntryCodecFactory { 10 | 11 | 12 | public LogEntryEncoder encoder(); 13 | 14 | 15 | public LogEntryDecoder decoder(); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/rpc/LogEntryEncoder.java: -------------------------------------------------------------------------------- 1 | package rpc; 2 | 3 | import com.google.protobuf.ByteString; 4 | import com.google.protobuf.CodedOutputStream; 5 | import entity.LogEntry; 6 | import entity.LogId; 7 | import entity.Status; 8 | import exceptions.RaftException; 9 | import utils.Requires; 10 | 11 | import java.io.IOException; 12 | import java.nio.ByteBuffer; 13 | 14 | /** 15 | * Created by 周思成 on 2020/5/7 11:54 16 | */ 17 | 18 | @Deprecated 19 | public class LogEntryEncoder implements entity.LogEntryEncoder { 20 | 21 | public static final LogEntryEncoder INSTANCE = new LogEntryEncoder(); 22 | 23 | 24 | @Override 25 | public byte[] encode(LogEntry log) throws RaftException { 26 | Requires.requireNonNull(log,"The log entry is null"); 27 | String data = new String(getByteArrayFromByteBuffer(log.getData())); 28 | 29 | return data.getBytes(); 30 | } 31 | 32 | // @Override 33 | // public byte[] encode(LogEntry log) throws RaftException { 34 | // Requires.requireNonNull(log,"The log entry is null"); 35 | // 36 | // final LogId logId = log.getId(); 37 | // final LogOuter.LogEntry.Builder builder = LogOuter.LogEntry.newBuilder(); 38 | // builder.setIndex(logId.getIndex()); 39 | // builder.setTerm(logId.getTerm()); 40 | // builder.setData( ByteString.copyFrom(getByteArrayFromByteBuffer(log.getData()))); 41 | // final LogOuter.LogEntry logEntry = builder.build(); 42 | // final int bodyLen = logEntry.getSerializedSize(); 43 | // final byte[] ret = new byte[LogEntryV2CodecFactory.HEADER_SIZE + bodyLen]; 44 | // int i = 0; 45 | // for (; i >> 24); 46 | bytes[1] = (byte)(num >>> 16); 47 | bytes[2] = (byte)(num >>> 8); 48 | bytes[3] = (byte)num; 49 | return bytes; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/rpc/RaftRpcServerFactory.java: -------------------------------------------------------------------------------- 1 | package rpc; 2 | 3 | import com.alipay.remoting.rpc.RpcServer; 4 | import entity.Endpoint; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import java.util.concurrent.Executor; 9 | 10 | /** 11 | * Created by 周思成 on 2020/3/18 13:03 12 | */ 13 | 14 | public class RaftRpcServerFactory { 15 | 16 | public static final Logger LOG = LoggerFactory.getLogger(RaftRpcServerFactory.class); 17 | 18 | 19 | /** 20 | * Creates a raft RPC server with executors to handle requests. 21 | * 22 | * @param endpoint server address to bind 23 | * @param raftExecutor executor to handle RAFT requests. 24 | * @param cliExecutor executor to handle CLI service requests. 25 | * @return a rpc server instance 26 | */ 27 | public static RpcServer createRaftRpcServer(final Endpoint endpoint, final Executor raftExecutor 28 | ,final Executor cliExecutor){ 29 | final RpcServer rpcServer = new RpcServer(endpoint.getPort(),true,true); 30 | addRaftRequestProcessors(rpcServer, raftExecutor, cliExecutor); 31 | return rpcServer; 32 | } 33 | 34 | public static void addRaftRequestProcessors(RpcServer rpcServer, Executor raftExecutor, Executor cliExecutor) { 35 | 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/rpc/RpcRequestClosure.java: -------------------------------------------------------------------------------- 1 | package rpc; 2 | 3 | import com.google.protobuf.Message; 4 | import entity.Closure; 5 | import entity.Status; 6 | import com.alipay.remoting.AsyncContext; 7 | import com.alipay.remoting.BizContext; 8 | 9 | import com.google.protobuf.Message; 10 | 11 | /** 12 | * Created by 周思成 on 2020/3/13 11:45 13 | */ 14 | 15 | /** 16 | * RPC request Closure encapsulates the RPC contexts. 17 | * 18 | * @author boyan (boyan@alibaba-inc.com) 19 | * 20 | * 2018-Mar-28 4:55:24 PM 21 | */ 22 | public class RpcRequestClosure implements Closure { 23 | 24 | private final BizContext bizContext; 25 | private final AsyncContext asyncContext; 26 | private boolean respond; 27 | 28 | public RpcRequestClosure(BizContext bizContext, AsyncContext asyncContext) { 29 | super(); 30 | this.bizContext = bizContext; 31 | this.asyncContext = asyncContext; 32 | this.respond = false; 33 | } 34 | 35 | public BizContext getBizContext() { 36 | return this.bizContext; 37 | } 38 | 39 | public AsyncContext getAsyncContext() { 40 | return this.asyncContext; 41 | } 42 | 43 | public synchronized void sendResponse(Message msg) { 44 | if (this.respond) { 45 | return; 46 | } 47 | this.asyncContext.sendResponse(msg); 48 | this.respond = true; 49 | } 50 | 51 | @Override 52 | public void run(Status status) { 53 | sendResponse(RpcResponseFactory.newResponse(status)); 54 | } 55 | } 56 | 57 | -------------------------------------------------------------------------------- /src/main/java/rpc/RpcResponseClosure.java: -------------------------------------------------------------------------------- 1 | package rpc; 2 | 3 | /** 4 | * Created by 周思成 on 2020/3/13 11:51 5 | */ 6 | 7 | import com.alipay.sofa.rpc.core.exception.SofaRpcException; 8 | import com.alipay.sofa.rpc.core.invoke.SofaResponseCallback; 9 | import com.alipay.sofa.rpc.core.request.RequestBase; 10 | import com.google.protobuf.Message; 11 | import core.NodeImpl; 12 | import entity.Closure; 13 | import entity.Node; 14 | import entity.TimeOutChecker; 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | import service.ElectionService; 18 | import service.ElectionServiceImpl; 19 | import utils.Utils; 20 | 21 | import java.util.List; 22 | 23 | 24 | /** 25 | * @author Mike 26 | */ 27 | public class RpcResponseClosure implements SofaResponseCallback { 28 | 29 | public static final Logger LOG = LoggerFactory.getLogger(RpcResponseClosure.class); 30 | ElectionService electionService = new ElectionServiceImpl(); 31 | @Override 32 | public void onAppResponse(Object o, String s, RequestBase requestBase) { 33 | 34 | LOG.debug("Receive response:requestBase: {} requestString: {}",requestBase.toString(),s); 35 | 36 | if(requestBase.getMethodName()==null){ 37 | LOG.error("Receive empty methodName:{}",requestBase.toString()); 38 | return; 39 | } 40 | 41 | switch (requestBase.getMethodName()) { 42 | case "handleAppendEntriesRequest": 43 | RpcRequests.AppendEntriesResponse appendEntriesResponse = 44 | (RpcRequests.AppendEntriesResponse)o; 45 | NodeImpl.getNodeImple().handleAppendEntriesResponse(appendEntriesResponse); 46 | break; 47 | case "handlePreVoteRequest": 48 | RpcRequests.RequestPreVoteResponse requestPreVoteResponse = 49 | (RpcRequests.RequestPreVoteResponse)o; 50 | electionService.handlePrevoteResponse(requestPreVoteResponse); 51 | break; 52 | case "handleVoteRequest": 53 | RpcRequests.RequestVoteResponse requestVoteResponse = 54 | (RpcRequests.RequestVoteResponse)o; 55 | electionService.handleElectionResponse(requestVoteResponse); 56 | break; 57 | case "handleToApplyRequest": 58 | RpcRequests.NotifyFollowerToApplyResponse response = 59 | (RpcRequests.NotifyFollowerToApplyResponse)o; 60 | if(!response.getSuccess()){ 61 | LOG.error("***critical error*** FSM StateMachine apply failed follower{}" 62 | ,response.getFollowerId()); 63 | }else { 64 | NodeImpl.getNodeImple().handleToApplyResponse(response); 65 | } 66 | break; 67 | case "handleReadHeartbeatrequest": 68 | RpcRequests.AppendEntriesResponse appendEntriesResponse1 69 | = (RpcRequests.AppendEntriesResponse)o; 70 | NodeImpl.getNodeImple().handleReadHeartbeatRequestClosure(appendEntriesResponse1); 71 | break; 72 | case "handleApendEntriesRequests": 73 | RpcRequests.AppendEntriesResponses appendEntriesResponses = 74 | (RpcRequests.AppendEntriesResponses)o; 75 | 76 | for (RpcRequests.AppendEntriesResponse a: 77 | appendEntriesResponses.getArgsList() ) { 78 | NodeImpl.getNodeImple().handleAppendEntriesResponse(a); 79 | } 80 | break; 81 | case "handleFollowerStableRequest": 82 | LOG.info("Follower invoke follower stable request success lastIndex:{}" 83 | ,((RpcRequests.NotifyFollowerStableResponse)o).getLastIndex()); 84 | break; 85 | default: 86 | LOG.error("RPC Request closure mismatched, requestBase: {} requestString: {}" 87 | ,requestBase.toString(),s); 88 | } 89 | } 90 | 91 | @Override 92 | public void onAppException(Throwable throwable, String s, RequestBase requestBase) { 93 | 94 | } 95 | 96 | @Override 97 | public void onSofaException(SofaRpcException e, String s, RequestBase requestBase) { 98 | 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/rpc/RpcResponseFactory.java: -------------------------------------------------------------------------------- 1 | package rpc; 2 | 3 | /** 4 | * Created by 周思成 on 2020/3/13 11:49 5 | */ 6 | 7 | import entity.RaftError; 8 | import entity.Status; 9 | 10 | /** 11 | * Helper to create error response. 12 | * 13 | * @author boyan (boyan@alibaba-inc.com) 14 | * 15 | * 2018-Mar-28 4:33:50 PM 16 | */ 17 | public class RpcResponseFactory { 18 | 19 | /** 20 | * Creates a RPC response from status,return OK response 21 | * when status is null. 22 | * 23 | * @param st status with response 24 | * @return a response instance 25 | */ 26 | public static RpcRequests.ErrorResponse newResponse(Status st) { 27 | if (st == null) { 28 | return newResponse(0, "OK"); 29 | } 30 | return newResponse(st.getCode(), st.getErrorMsg()); 31 | } 32 | 33 | /** 34 | * Creates an error response with parameters. 35 | * 36 | * @param error error with raft info 37 | * @param fmt message with format string 38 | * @param args arguments referenced by the format specifiers in the format 39 | * string 40 | * @return a response instance 41 | */ 42 | public static RpcRequests.ErrorResponse newResponse(RaftError error, String fmt, Object... args) { 43 | return newResponse(error.getNumber(), fmt, args); 44 | } 45 | 46 | /** 47 | * Creates an error response with parameters. 48 | * 49 | * @param code error code with raft info 50 | * @param fmt message with format string 51 | * @param args arguments referenced by the format specifiers in the format 52 | * string 53 | * @return a response instance 54 | */ 55 | public static RpcRequests.ErrorResponse newResponse(int code, String fmt, Object... args) { 56 | RpcRequests.ErrorResponse.Builder builder = RpcRequests.ErrorResponse.newBuilder(); 57 | builder.setErrorCode(code); 58 | if (fmt != null) { 59 | builder.setErrorMsg(String.format(fmt, args)); 60 | } 61 | return builder.build(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/rpc/RpcServices.java: -------------------------------------------------------------------------------- 1 | package rpc; 2 | 3 | 4 | import entity.RpcResult; 5 | 6 | /** 7 | * Created by 周思成 on 2020/3/24 12:47 8 | */ 9 | 10 | public interface RpcServices { 11 | 12 | 13 | public RpcRequests sendRpcRequest(RpcRequests rpcRequests); 14 | 15 | public RpcRequests.RequestPreVoteResponse handlePreVoteRequest( 16 | RpcRequests.RequestPreVoteRequest preVoteRequest); 17 | 18 | public RpcRequests.RequestVoteResponse handleVoteRequest( 19 | RpcRequests.RequestVoteRequest requestVoteRequest); 20 | 21 | public RpcRequests.AppendEntriesResponse handleAppendEntriesRequest( 22 | RpcRequests.AppendEntriesRequest appendEntriesRequest); 23 | 24 | public RpcRequests.AppendEntriesResponses handleApendEntriesRequests( 25 | RpcRequests.AppendEntriesRequests appendEntriesRequests); 26 | 27 | public RpcRequests.NotifyFollowerStableResponse handleFollowerStableRequest( 28 | RpcRequests.NotifyFollowerStableRequest notifyFollowerStableRequest 29 | ); 30 | 31 | public RpcRequests.NotifyFollowerToApplyResponse handleToApplyRequest( 32 | RpcRequests.NotifyFollowerToApplyRequest request 33 | ); 34 | 35 | public RpcRequests.AppendEntriesResponse handleReadHeartbeatrequest( 36 | RpcRequests.AppendEntriesRequest appendEntriesRequest 37 | ); 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/rpc/TaskRpcResponseClosure.java: -------------------------------------------------------------------------------- 1 | package rpc; 2 | 3 | /** 4 | * Created by 周思成 on 2020/3/13 11:51 5 | */ 6 | 7 | import com.alipay.sofa.rpc.core.exception.SofaRpcException; 8 | import com.alipay.sofa.rpc.core.invoke.SofaResponseCallback; 9 | import com.alipay.sofa.rpc.core.request.RequestBase; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | 14 | /** 15 | * @author Mike 16 | */ 17 | public class TaskRpcResponseClosure implements SofaResponseCallback { 18 | 19 | public static final Logger LOG = LoggerFactory.getLogger(TaskRpcResponseClosure.class); 20 | 21 | @Override 22 | public void onAppResponse(Object o, String s, RequestBase requestBase) { 23 | 24 | LOG.info("Receive taskRpcResponse response:requestBase: {} requestString: {}",requestBase.toString(),s); 25 | switch (requestBase.getMethodName()) { 26 | case "": 27 | 28 | } 29 | } 30 | 31 | @Override 32 | public void onAppException(Throwable throwable, String s, RequestBase requestBase) { 33 | 34 | } 35 | 36 | @Override 37 | public void onSofaException(SofaRpcException e, String s, RequestBase requestBase) { 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/rpc/TaskRpcServices.java: -------------------------------------------------------------------------------- 1 | package rpc; 2 | 3 | import entity.ReadTask; 4 | import entity.ReadTaskResponse; 5 | import entity.Task; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Created by 周思成 on 2020/4/8 17:12 11 | */ 12 | 13 | public interface TaskRpcServices { 14 | 15 | void init(String protocol,String ip,String serialization,int port); 16 | 17 | void apply(final Task task); 18 | 19 | void apply(final Task[] tasks); 20 | 21 | ReadTaskResponse handleReadIndexRequest(ReadTask readTask); 22 | List handleReadIndexRequests(List readTask); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/rpc/TaskServicesImpl.java: -------------------------------------------------------------------------------- 1 | package rpc; 2 | 3 | import com.alipay.sofa.rpc.config.ConsumerConfig; 4 | import com.alipay.sofa.rpc.config.ServerConfig; 5 | import com.sun.org.apache.xml.internal.utils.StringVector; 6 | import core.NodeImpl; 7 | import core.RaftGroupService; 8 | import entity.ReadTask; 9 | import entity.ReadTaskResponse; 10 | import entity.Task; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | import java.nio.charset.StandardCharsets; 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | /** 19 | * Created by 周思成 on 2020/4/8 17:13 20 | */ 21 | 22 | public class TaskServicesImpl implements TaskRpcServices { 23 | private static final Logger LOG = LoggerFactory.getLogger(TaskServicesImpl.class); 24 | 25 | @Override 26 | public void init(String protocol,String ip, String serialization,int port) { 27 | 28 | } 29 | 30 | @Override 31 | public void apply(Task task) { 32 | try { 33 | LOG.debug("Receive task:{}", task.getData()); 34 | NodeImpl.getNodeImple().apply(task); 35 | 36 | } catch (Exception e) { 37 | e.printStackTrace(); 38 | } 39 | } 40 | 41 | @Override 42 | public void apply(Task[] tasks) { 43 | LOG.info("Apply tasks:{} currentThread:{}",tasks.length,Thread.currentThread().getName()); 44 | for (Task task : tasks) { 45 | LOG.debug("for :{}",Thread.currentThread().getName()); 46 | NodeImpl.getNodeImple().apply(task); 47 | } 48 | } 49 | 50 | @Override 51 | public ReadTaskResponse handleReadIndexRequest(ReadTask readTask) { 52 | LOG.debug("Receive readTask"); 53 | return NodeImpl.getNodeImple().addReadTaskEvent(readTask); 54 | } 55 | @Override 56 | public List handleReadIndexRequests(List readTask) { 57 | List readTaskResponses = new ArrayList<>(readTask.size()); 58 | for (ReadTask r : 59 | readTask) { 60 | readTaskResponses.add(handleReadIndexRequest(r)); 61 | } 62 | return readTaskResponses; 63 | } 64 | 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/rpc/processor/AppendEntriesRequestProcessor.java: -------------------------------------------------------------------------------- 1 | package rpc.processor; 2 | 3 | import com.alipay.remoting.Connection; 4 | import com.alipay.remoting.ConnectionEventProcessor; 5 | 6 | /** 7 | * Created by 周思成 on 2020/3/18 13:43 8 | */ 9 | 10 | public class AppendEntriesRequestProcessor implements ConnectionEventProcessor { 11 | @Override 12 | public void onEvent(String s, Connection connection) { 13 | 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/service/ElectionService.java: -------------------------------------------------------------------------------- 1 | package service; 2 | 3 | import core.NodeImpl; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import rpc.RpcRequests; 7 | import rpc.RpcServicesImpl; 8 | 9 | /** 10 | * Created by 周思成 on 2020/3/24 16:02 11 | */ 12 | 13 | public interface ElectionService { 14 | 15 | public static final Logger LOG = LoggerFactory.getLogger(ElectionService.class); 16 | 17 | 18 | /** 19 | * 检查是否启动election 20 | */ 21 | public static void checkToStartPreVote(){ 22 | LOG.debug("checkToStartPreVote:"+!NodeImpl.getNodeImple().checkNodeStatePreCandidate()); 23 | if ( ! NodeImpl.getNodeImple().checkNodeStatePreCandidate()) { 24 | ElectionService electionService = new ElectionServiceImpl(); 25 | electionService.startPrevote(); 26 | } 27 | } 28 | 29 | public static void checkToStartElection() { 30 | LOG.debug("checkToStartElection:{}",NodeImpl.getNodeImple().checkIfCurrentNodeCanStartElection()); 31 | if (NodeImpl.getNodeImple().checkIfCurrentNodeCanStartElection()) { 32 | //start election 33 | ElectionService electionService = new ElectionServiceImpl(); 34 | electionService.election(); 35 | } 36 | } 37 | 38 | public void startPrevote(); 39 | 40 | public void election(); 41 | 42 | public void handlePrevoteResponse(RpcRequests.RequestPreVoteResponse requestPreVoteResponse); 43 | 44 | public void handleElectionResponse(RpcRequests.RequestVoteResponse requestVoteResponse); 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/service/RaftServerService.java: -------------------------------------------------------------------------------- 1 | package service; 2 | 3 | import com.google.protobuf.Message; 4 | import rpc.RpcRequestClosure; 5 | import rpc.RpcRequests; 6 | import rpc.RpcRequests; 7 | import rpc.RpcResponseClosure; 8 | 9 | /** 10 | * Created by 周思成 on 2020/3/12 14:33 11 | * @author Mike 12 | */ 13 | 14 | public interface RaftServerService { 15 | 16 | 17 | /** 18 | * Handle pre-vote request. 19 | * 20 | * @param request data of the pre vote 21 | * @return the response message 22 | */ 23 | Message handlePreVoteRequest(RpcRequests.RequestVoteRequest request); 24 | 25 | /** 26 | * Handle request-vote request. 27 | * 28 | * @param request data of the vote 29 | * @return the response message 30 | */ 31 | Message handleRequestVoteRequest(RpcRequests.RequestVoteRequest request); 32 | 33 | /** 34 | * Handle append-entries request, return response message or 35 | * called done.run() with response. 36 | * 37 | * @param request data of the entries to append 38 | * @param done callback 39 | * @return the response message 40 | */ 41 | Message handleAppendEntriesRequest(RpcRequests.AppendEntriesRequest request, RpcRequestClosure done); 42 | 43 | 44 | 45 | /** 46 | * Handle time-out-now request, return response message or 47 | * called done.run() with response. 48 | * 49 | * @param request data of the timeout now request 50 | * @param done callback 51 | * @return the response message 52 | */ 53 | Message handleTimeoutNowRequest(RpcRequests.TimeoutNowRequest request, RpcRequestClosure done); 54 | 55 | /** 56 | * Handle read-index request, call the RPC.proto closure with response. 57 | * 58 | * @param request data of the readIndex read 59 | * @param done callback 60 | */ 61 | void handleReadIndexRequest(RpcRequests.ReadIndexRequest request, RpcResponseClosure done); 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/service/RaftServiceFactory.java: -------------------------------------------------------------------------------- 1 | package service; 2 | 3 | import config.RaftOptionsLoader; 4 | import entity.LogEntryCodecFactory; 5 | import storage.LogStorage; 6 | 7 | /** 8 | * Created by 周思成 on 2020/3/13 11:57 9 | */ 10 | 11 | public interface RaftServiceFactory { 12 | 13 | /** 14 | * Creates a raft log storage. 15 | * @param uri The log storage uri from {@link NodeOptions} 16 | * @param raftOptions the raft options. 17 | * @return storage to store raft log entires. 18 | */ 19 | LogStorage createLogStorage(final String uri, final RaftOptionsLoader raftOptions); 20 | 21 | 22 | /** 23 | * Creates a log entry codec factory. 24 | * @return a codec factory to create encoder/decoder for raft log entry. 25 | */ 26 | LogEntryCodecFactory createLogEntryCodecFactory(); 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/service/RaftServiceFactoryImpl.java: -------------------------------------------------------------------------------- 1 | package service; 2 | 3 | import config.RaftOptionsLoader; 4 | import entity.LogEntryCodecFactory; 5 | import storage.LogStorage; 6 | 7 | /** 8 | * Created by 周思成 on 2020/3/13 14:27 9 | */ 10 | 11 | public class RaftServiceFactoryImpl implements RaftServiceFactory { 12 | @Override 13 | public LogStorage createLogStorage(String uri, RaftOptionsLoader raftOptions) { 14 | return null; 15 | } 16 | 17 | @Override 18 | public LogEntryCodecFactory createLogEntryCodecFactory() { 19 | return null; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/storage/LogStorage.java: -------------------------------------------------------------------------------- 1 | package storage; 2 | 3 | /** 4 | * Created by 周思成 on 2020/3/13 15:52 5 | */ 6 | 7 | import config.LogStorageOptions; 8 | import core.Lifecycle; 9 | import entity.LogEntry; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * Log entry storage service. 15 | * 16 | * @author boyan (boyan@alibaba-inc.com) 17 | * 18 | * 2018-Mar-12 3:43:54 PM 19 | */ 20 | public interface LogStorage { 21 | 22 | /** 23 | * Returns first log index in log. 24 | */ 25 | long getFirstLogIndex(); 26 | 27 | /** 28 | * Returns last log index in log. 29 | */ 30 | long getLastLogIndex(); 31 | 32 | /** 33 | * Get logEntry by index. 34 | */ 35 | LogEntry getEntry(final long index); 36 | 37 | /** 38 | * Get logEntry's term by index. This method is deprecated, you should use {@link #getEntry(long)} to get the log id's term. 39 | * @deprecated 40 | */ 41 | @Deprecated 42 | long getTerm(final long index); 43 | 44 | /** 45 | * Append entries to log. 46 | */ 47 | boolean appendEntry(final LogEntry entry); 48 | 49 | /** 50 | * Append entries to log, return append success number. 51 | */ 52 | int appendEntries(final List entries); 53 | 54 | /** 55 | * Delete logs from storage's head, [first_log_index, first_index_kept) will 56 | * be discarded. 57 | */ 58 | boolean truncatePrefix(final long firstIndexKept); 59 | 60 | /** 61 | * Delete uncommitted logs from storage's tail, (last_index_kept, last_log_index] 62 | * will be discarded. 63 | */ 64 | boolean truncateSuffix(final long lastIndexKept); 65 | 66 | /** 67 | * Drop all the existing logs and reset next log index to |next_log_index|. 68 | * This function is called after installing snapshot from leader. 69 | */ 70 | boolean reset(final long nextLogIndex); 71 | 72 | 73 | boolean init(); 74 | } -------------------------------------------------------------------------------- /src/main/java/utils/ArrayDeque.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * Extend array list to add peek/poll first/last element. 8 | * 9 | * @author boyan (boyan@alibaba-inc.com) 10 | * 11 | * 2018-Apr-11 11:14:38 AM 12 | * @param 13 | */ 14 | public class ArrayDeque extends java.util.ArrayList { 15 | 16 | private static final long serialVersionUID = -4929318149975955629L; 17 | 18 | /** 19 | * Get the first element of list. 20 | */ 21 | public static E peekFirst(List list) { 22 | return list.get(0); 23 | } 24 | 25 | /** 26 | * Remove the first element from list and return it. 27 | */ 28 | public static E pollFirst(List list) { 29 | return list.remove(0); 30 | } 31 | 32 | /** 33 | * Get the last element of list. 34 | */ 35 | public static E peekLast(List list) { 36 | return list.get(list.size() - 1); 37 | } 38 | 39 | /** 40 | * Remove the last element from list and return it. 41 | */ 42 | public static E pollLast(List list) { 43 | return list.remove(list.size() - 1); 44 | } 45 | 46 | /** 47 | * Get the first element of list. 48 | */ 49 | public E peekFirst() { 50 | return peekFirst(this); 51 | } 52 | 53 | /** 54 | * Get the last element of list. 55 | */ 56 | public E peekLast() { 57 | return peekLast(this); 58 | } 59 | 60 | /** 61 | * Remove the first element from list and return it. 62 | */ 63 | public E pollFirst() { 64 | return pollFirst(this); 65 | } 66 | 67 | /** 68 | * Remove the last element from list and return it. 69 | */ 70 | public E pollLast() { 71 | return pollLast(this); 72 | } 73 | 74 | /** 75 | * Expose this methods so we not need to create a new subList just to 76 | * remove a range of elements. 77 | * 78 | * Removes from this deque all of the elements whose index is between 79 | * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive. 80 | * Shifts any succeeding elements to the left (reduces their index). 81 | * This call shortens the deque by {@code (toIndex - fromIndex)} elements. 82 | * (If {@code toIndex==fromIndex}, this operation has no effect.) 83 | * 84 | * @throws IndexOutOfBoundsException if {@code fromIndex} or 85 | * {@code toIndex} is out of range 86 | * ({@code fromIndex < 0 || 87 | * fromIndex >= size() || 88 | * toIndex > size() || 89 | * toIndex < fromIndex}) 90 | */ 91 | @Override 92 | public void removeRange(int fromIndex, int toIndex) { 93 | super.removeRange(fromIndex, toIndex); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/utils/AsciiStringUtil.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | /** 4 | * Created by 周思成 on 2020/3/10 15:43 5 | */ 6 | 7 | 8 | 9 | /** 10 | * @author jiachun.fjc 11 | */ 12 | public final class AsciiStringUtil { 13 | 14 | public static byte[] unsafeEncode(final CharSequence in) { 15 | final int len = in.length(); 16 | final byte[] out = new byte[len]; 17 | for (int i = 0; i < len; i++) { 18 | out[i] = (byte) in.charAt(i); 19 | } 20 | return out; 21 | } 22 | 23 | // public static String unsafeDecode(final byte[] in) { 24 | // final int len = in.length; 25 | // final char[] out = new char[len]; 26 | // for (int i = 0; i < len; i++) { 27 | // out[i] = (char) (in[i] & 0xFF); 28 | // } 29 | // return UnsafeUtil.moveToString(out); 30 | // } 31 | 32 | // public static String unsafeDecode(final ByteString in) { 33 | // final int len = in.size(); 34 | // final char[] out = new char[len]; 35 | // for (int i = 0; i < len; i++) { 36 | // out[i] = (char) (in.byteAt(i) & 0xFF); 37 | // } 38 | // return UnsafeUtil.moveToString(out); 39 | // } 40 | 41 | private AsciiStringUtil() { 42 | } 43 | } -------------------------------------------------------------------------------- /src/main/java/utils/Bits.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | /** 4 | * Created by 周思成 on 2020/3/13 16:49 5 | */ 6 | 7 | /** 8 | * Moved from java.io.Bits 9 | * 10 | * @author boyan (boyan@alibaba-inc.com) 11 | */ 12 | public class Bits { 13 | public static int getInt(byte[] b, int off) { 14 | return (b[off + 3] & 0xFF) + ((b[off + 2] & 0xFF) << 8) + ((b[off + 1] & 0xFF) << 16) + (b[off] << 24); 15 | } 16 | 17 | public static float getFloat(byte[] b, int off) { 18 | return Float.intBitsToFloat(getInt(b, off)); 19 | } 20 | 21 | public static long getLong(byte[] b, int off) { 22 | return (b[off + 7] & 0xFFL) + ((b[off + 6] & 0xFFL) << 8) + ((b[off + 5] & 0xFFL) << 16) 23 | + ((b[off + 4] & 0xFFL) << 24) + ((b[off + 3] & 0xFFL) << 32) + ((b[off + 2] & 0xFFL) << 40) 24 | + ((b[off + 1] & 0xFFL) << 48) + ((long) b[off] << 56); 25 | } 26 | 27 | public static double getDouble(byte[] b, int off) { 28 | return Double.longBitsToDouble(getLong(b, off)); 29 | } 30 | 31 | public static void putInt(byte[] b, int off, int val) { 32 | b[off + 3] = (byte) val; 33 | b[off + 2] = (byte) (val >>> 8); 34 | b[off + 1] = (byte) (val >>> 16); 35 | b[off] = (byte) (val >>> 24); 36 | } 37 | 38 | public static void putShort(byte[] b, int off, short val) { 39 | b[off + 1] = (byte) val; 40 | b[off] = (byte) (val >>> 8); 41 | } 42 | 43 | public static short getShort(byte[] b, int off) { 44 | return (short) ((b[off + 1] & 0xFF) + (b[off] << 8)); 45 | } 46 | 47 | public static void putFloat(byte[] b, int off, float val) { 48 | putInt(b, off, Float.floatToIntBits(val)); 49 | } 50 | 51 | public static void putLong(byte[] b, int off, long val) { 52 | b[off + 7] = (byte) val; 53 | b[off + 6] = (byte) (val >>> 8); 54 | b[off + 5] = (byte) (val >>> 16); 55 | b[off + 4] = (byte) (val >>> 24); 56 | b[off + 3] = (byte) (val >>> 32); 57 | b[off + 2] = (byte) (val >>> 40); 58 | b[off + 1] = (byte) (val >>> 48); 59 | b[off] = (byte) (val >>> 56); 60 | } 61 | 62 | public static void putDouble(byte[] b, int off, double val) { 63 | putLong(b, off, Double.doubleToLongBits(val)); 64 | } 65 | } 66 | 67 | -------------------------------------------------------------------------------- /src/main/java/utils/BootYaml.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | import entity.Options; 4 | import org.yaml.snakeyaml.Yaml; 5 | 6 | import java.io.File; 7 | import java.io.FileInputStream; 8 | import java.io.FileNotFoundException; 9 | import java.io.InputStream; 10 | import java.util.Map; 11 | 12 | /** 13 | * Created by 周思成 on 2020/3/27 11:18 14 | */ 15 | 16 | public class BootYaml { 17 | 18 | 19 | public static Options getYaml(String yamlName) throws FileNotFoundException { 20 | Yaml yaml = new Yaml();// 这个需要的jar为:org.yaml.snakeyaml 21 | 22 | File file = new File(yamlName); 23 | //MailConfig 这个是这个主函数所在的类的类名 24 | // InputStream resourceAsStream = BootYaml.class.getClassLoader() 25 | // .getResourceAsStream(yamlName); 26 | 27 | InputStream resourceAsStream = new FileInputStream(file); 28 | //加载流,获取yaml文件中的配置数据,然后转换为Map, 29 | // Map obj = (Map) yaml.load(resourceAsStream); 30 | // return obj; 31 | //Options obj = (Options) yaml.load(resourceAsStream); 32 | return yaml.loadAs(resourceAsStream,Options.class); 33 | 34 | } 35 | 36 | public static void main(String[] args) { 37 | Yaml yaml = new Yaml();// 这个需要的jar为:org.yaml.snakeyaml 38 | 39 | //MailConfig 这个是这个主函数所在的类的类名 40 | InputStream resourceAsStream = BootYaml.class.getClassLoader() 41 | .getResourceAsStream("properties.yml"); 42 | 43 | //加载流,获取yaml文件中的配置数据,然后转换为Map, 44 | //Options obj = (Options) yaml.load(resourceAsStream); 45 | Options obj = yaml.loadAs(resourceAsStream,Options.class); 46 | System.out.println(obj.getCurrentNodeOptions().getClientAddress()); 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/utils/CatchUpClosure.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | /** 4 | * Created by 周思成 on 2020/4/14 16:29 5 | */ 6 | 7 | import entity.Closure; 8 | import entity.Status; 9 | 10 | import java.util.concurrent.ScheduledFuture; 11 | 12 | /** 13 | * A catchup closure for peer to catch up. 14 | * 15 | * @author boyan (boyan@alibaba-inc.com) 16 | * 17 | * 2018-Apr-04 2:15:05 PM 18 | */ 19 | public abstract class CatchUpClosure implements Closure { 20 | 21 | private long maxMargin; 22 | private ScheduledFuture timer; 23 | private boolean hasTimer; 24 | private boolean errorWasSet; 25 | 26 | private final Status status = Status.OK(); 27 | 28 | public Status getStatus() { 29 | return this.status; 30 | } 31 | 32 | public long getMaxMargin() { 33 | return this.maxMargin; 34 | } 35 | 36 | public void setMaxMargin(long maxMargin) { 37 | this.maxMargin = maxMargin; 38 | } 39 | 40 | public ScheduledFuture getTimer() { 41 | return this.timer; 42 | } 43 | 44 | public void setTimer(ScheduledFuture timer) { 45 | this.timer = timer; 46 | this.hasTimer = true; 47 | } 48 | 49 | public boolean hasTimer() { 50 | return this.hasTimer; 51 | } 52 | 53 | public boolean isErrorWasSet() { 54 | return this.errorWasSet; 55 | } 56 | 57 | public void setErrorWasSet(boolean errorWasSet) { 58 | this.errorWasSet = errorWasSet; 59 | } 60 | } -------------------------------------------------------------------------------- /src/main/java/utils/Copiable.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | /** 4 | * Created by 周思成 on 2020/3/13 16:45 5 | */ 6 | 7 | public interface Copiable { 8 | 9 | /** 10 | * Copy current object(deep-clone). 11 | */ 12 | T copy(); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/utils/CrcUtil.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | /** 4 | * Created by 周思成 on 2020/3/10 15:32 5 | */ 6 | 7 | import java.nio.ByteBuffer; 8 | 9 | /** 10 | * CRC utilities to compute CRC64 checksum. 11 | * 12 | * @author boyan(boyan@antfin.com) 13 | */ 14 | public final class CrcUtil { 15 | 16 | private static final ThreadLocal CRC_64_THREAD_LOCAL = ThreadLocal.withInitial(CRC64::new); 17 | 18 | /** 19 | * Compute CRC64 checksum for byte[]. 20 | * 21 | * @param array source array 22 | * @return checksum value 23 | */ 24 | public static long crc64(final byte[] array) { 25 | if (array == null) { 26 | return 0; 27 | } 28 | return crc64(array, 0, array.length); 29 | } 30 | 31 | /** 32 | * Compute CRC64 checksum for byte[]. 33 | * 34 | * @param array source array 35 | * @param offset starting position in the source array 36 | * @param length the number of array elements to be computed 37 | * @return checksum value 38 | */ 39 | public static long crc64(final byte[] array, final int offset, final int length) { 40 | final CRC64 crc64 = CRC_64_THREAD_LOCAL.get(); 41 | crc64.update(array, offset, length); 42 | final long ret = crc64.getValue(); 43 | crc64.reset(); 44 | return ret; 45 | } 46 | 47 | /** 48 | * Compute CRC64 checksum for {@code ByteBuffer}. 49 | * 50 | * @param buf source {@code ByteBuffer} 51 | * @return checksum value 52 | */ 53 | public static long crc64(final ByteBuffer buf) { 54 | final int pos = buf.position(); 55 | final int rem = buf.remaining(); 56 | if (rem <= 0) { 57 | return 0; 58 | } 59 | // Currently we have not used DirectByteBuffer yet. 60 | if (buf.hasArray()) { 61 | return crc64(buf.array(), pos + buf.arrayOffset(), rem); 62 | } 63 | final byte[] b = new byte[rem]; 64 | buf.mark(); 65 | buf.get(b); 66 | buf.reset(); 67 | return crc64(b); 68 | } 69 | 70 | private CrcUtil() { 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/utils/DisruptorBuilder.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | import com.alipay.remoting.NamedThreadFactory; 4 | import com.lmax.disruptor.BlockingWaitStrategy; 5 | import com.lmax.disruptor.EventFactory; 6 | import com.lmax.disruptor.WaitStrategy; 7 | import com.lmax.disruptor.dsl.Disruptor; 8 | import com.lmax.disruptor.dsl.ProducerType; 9 | 10 | import java.util.concurrent.ThreadFactory; 11 | 12 | /** 13 | * Created by 周思成 on 2020/4/9 21:11 14 | */ 15 | 16 | public class DisruptorBuilder { 17 | private EventFactory eventFactory; 18 | private Integer ringBufferSize; 19 | private ThreadFactory threadFactory = new NamedThreadFactory("Disruptor",true); 20 | private ProducerType producerType = ProducerType.MULTI; 21 | private WaitStrategy waitStrategy = new BlockingWaitStrategy(); 22 | 23 | private DisruptorBuilder() { 24 | } 25 | 26 | public static DisruptorBuilder newInstance() { 27 | return new DisruptorBuilder<>(); 28 | } 29 | 30 | public EventFactory getEventFactory() { 31 | return this.eventFactory; 32 | } 33 | 34 | public DisruptorBuilder setEventFactory(final EventFactory eventFactory) { 35 | this.eventFactory = eventFactory; 36 | return this; 37 | } 38 | 39 | public int getRingBufferSize() { 40 | return this.ringBufferSize; 41 | } 42 | 43 | public DisruptorBuilder setRingBufferSize(final int ringBufferSize) { 44 | this.ringBufferSize = ringBufferSize; 45 | return this; 46 | } 47 | 48 | public ThreadFactory getThreadFactory() { 49 | return this.threadFactory; 50 | } 51 | 52 | public DisruptorBuilder setThreadFactory(final ThreadFactory threadFactory) { 53 | this.threadFactory = threadFactory; 54 | return this; 55 | } 56 | 57 | public ProducerType getProducerType() { 58 | return this.producerType; 59 | } 60 | 61 | public DisruptorBuilder setProducerType(final ProducerType producerType) { 62 | this.producerType = producerType; 63 | return this; 64 | } 65 | 66 | public WaitStrategy getWaitStrategy() { 67 | return this.waitStrategy; 68 | } 69 | 70 | public DisruptorBuilder setWaitStrategy(final WaitStrategy waitStrategy) { 71 | this.waitStrategy = waitStrategy; 72 | return this; 73 | } 74 | 75 | public Disruptor build() { 76 | Requires.requireNonNull(this.ringBufferSize, " Ring buffer size not set"); 77 | Requires.requireNonNull(this.eventFactory, "Event factory not set"); 78 | return new Disruptor<>(this.eventFactory, this.ringBufferSize, this.threadFactory, this.producerType, 79 | this.waitStrategy); 80 | } 81 | 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/utils/Ints.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | /** 4 | * Created by 周思成 on 2020/3/13 22:58 5 | */ 6 | 7 | public class Ints { 8 | 9 | /** 10 | * Fast method of finding the next power of 2 greater than or equal to the supplied value. 11 | * 12 | * If the value is {@code <= 0} then 1 will be returned. 13 | * This method is not suitable for {@link Integer#MIN_VALUE} or numbers greater than 2^30. 14 | * 15 | * @param value from which to search for next power of 2 16 | * @return The next power of 2 or the value itself if it is a power of 2 17 | */ 18 | public static int findNextPositivePowerOfTwo(final int value) { 19 | return value <= 0 ? 1 : value >= 0x40000000 ? 0x40000000 : 1 << (32 - Integer.numberOfLeadingZeros(value - 1)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/utils/LogThreadPoolExecutor.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.util.concurrent.*; 7 | 8 | /** 9 | * Created by 周思成 on 2020/3/13 22:15 10 | */ 11 | 12 | public class LogThreadPoolExecutor extends ThreadPoolExecutor { 13 | 14 | private static final Logger LOG = LoggerFactory.getLogger(LogThreadPoolExecutor.class); 15 | 16 | private final String name; 17 | 18 | public LogThreadPoolExecutor(int coreThreads, int maximumThreads 19 | , long keepAliveSeconds, TimeUnit unit, BlockingQueue workQueue 20 | , ThreadFactory threadFactory, RejectedExecutionHandler handler, String poolName) { 21 | 22 | super(coreThreads,maximumThreads,keepAliveSeconds,unit,workQueue,threadFactory,handler); 23 | this.name = poolName; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/utils/NonReentrantLock.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | /** 4 | * Created by 周思成 on 2020/4/14 16:21 5 | */ 6 | 7 | import java.util.concurrent.TimeUnit; 8 | import java.util.concurrent.locks.AbstractQueuedSynchronizer; 9 | import java.util.concurrent.locks.Condition; 10 | import java.util.concurrent.locks.Lock; 11 | 12 | /** 13 | * Non reentrant lock, copied from netty project. 14 | */ 15 | public final class NonReentrantLock extends AbstractQueuedSynchronizer implements Lock { 16 | 17 | private static final long serialVersionUID = -833780837233068610L; 18 | 19 | private Thread owner; 20 | 21 | @Override 22 | public void lock() { 23 | acquire(1); 24 | } 25 | 26 | @Override 27 | public void lockInterruptibly() throws InterruptedException { 28 | acquireInterruptibly(1); 29 | } 30 | 31 | @Override 32 | public boolean tryLock() { 33 | return tryAcquire(1); 34 | } 35 | 36 | @Override 37 | public boolean tryLock(final long time, final TimeUnit unit) throws InterruptedException { 38 | return tryAcquireNanos(1, unit.toNanos(time)); 39 | } 40 | 41 | @Override 42 | public void unlock() { 43 | release(1); 44 | } 45 | 46 | public boolean isHeldByCurrentThread() { 47 | return isHeldExclusively(); 48 | } 49 | 50 | public Thread getOwner() { 51 | return this.owner; 52 | } 53 | 54 | @Override 55 | public Condition newCondition() { 56 | return new ConditionObject(); 57 | } 58 | 59 | @Override 60 | protected boolean tryAcquire(final int acquires) { 61 | if (compareAndSetState(0, 1)) { 62 | this.owner = Thread.currentThread(); 63 | return true; 64 | } 65 | return false; 66 | } 67 | 68 | @Override 69 | protected boolean tryRelease(final int releases) { 70 | if (Thread.currentThread() != this.owner) { 71 | throw new IllegalMonitorStateException("Owner is " + this.owner); 72 | } 73 | this.owner = null; 74 | setState(0); 75 | return true; 76 | } 77 | 78 | @Override 79 | protected boolean isHeldExclusively() { 80 | return getState() != 0 && this.owner == Thread.currentThread(); 81 | } 82 | } 83 | 84 | -------------------------------------------------------------------------------- /src/main/java/utils/RandomTimeUtil.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | /** 4 | * Created by 周思成 on 2020/3/24 14:36 5 | */ 6 | 7 | public class RandomTimeUtil { 8 | 9 | 10 | public static long newRandomTime(long begin,long end){ 11 | long rtn = begin + (long)(Math.random() * (end - begin)); 12 | // 如果返回的是开始时间和结束时间,则递归调用本函数查找随机值 13 | if(rtn == begin || rtn == end){ 14 | return newRandomTime(begin,end); 15 | } 16 | return rtn; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/utils/Recyclable.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | /** 4 | * Created by 周思成 on 2020/4/12 15:18 5 | */ 6 | 7 | public interface Recyclable { 8 | /** 9 | * Recycle this instance. 10 | */ 11 | boolean recycle(); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/utils/RecycleUtil.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | /** 4 | * Recycle tool for {@link Recyclable}. 5 | * 6 | * @author jiachun.fjc 7 | */ 8 | public final class RecycleUtil { 9 | 10 | /** 11 | * Recycle designated instance. 12 | */ 13 | public static boolean recycle(final Object obj) { 14 | return obj instanceof Recyclable && ((Recyclable) obj).recycle(); 15 | } 16 | 17 | private RecycleUtil() { 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/utils/Requires.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | /** 4 | * Created by 周思成 on 2020/3/13 15:27 5 | */ 6 | /** 7 | * Simple static methods to be called at the start of your own methods to verify 8 | * correct arguments and state. 9 | * 10 | * @author jiachun.fjc 11 | */ 12 | public final class Requires { 13 | 14 | /** 15 | * Checks that the specified object reference is not {@code null}. 16 | * 17 | * @param obj the object reference to check for nullity 18 | * @param the type of the reference 19 | * @return {@code obj} if not {@code null} 20 | * @throws NullPointerException if {@code obj} is {@code null} 21 | */ 22 | public static T requireNonNull(T obj) { 23 | if (obj == null) { 24 | throw new NullPointerException(); 25 | } 26 | return obj; 27 | } 28 | 29 | /** 30 | * Checks that the specified object reference is not {@code null} and 31 | * throws a customized {@link NullPointerException} if it is. 32 | * 33 | * @param obj the object reference to check for nullity 34 | * @param message detail message to be used in the event that a {@code 35 | * NullPointerException} is thrown 36 | * @param the type of the reference 37 | * @return {@code obj} if not {@code null} 38 | * @throws NullPointerException if {@code obj} is {@code null} 39 | */ 40 | public static T requireNonNull(T obj, String message) { 41 | if (obj == null) { 42 | throw new NullPointerException(message); 43 | } 44 | return obj; 45 | } 46 | 47 | /** 48 | * Ensures the truth of an expression involving one or more parameters 49 | * to the calling method. 50 | * 51 | * @param expression a boolean expression 52 | * @throws IllegalArgumentException if {@code expression} is false 53 | */ 54 | public static void requireTrue(boolean expression) { 55 | if (!expression) { 56 | throw new IllegalArgumentException(); 57 | } 58 | } 59 | 60 | /** 61 | * Ensures the truth of an expression involving one or more parameters 62 | * to the calling method. 63 | * 64 | * @param expression a boolean expression 65 | * @param message the exception message to use if the check fails; 66 | * will be converted to a string using 67 | * {@link String#valueOf(Object)} 68 | * @throws IllegalArgumentException if {@code expression} is false 69 | */ 70 | public static void requireTrue(boolean expression, Object message) { 71 | if (!expression) { 72 | throw new IllegalArgumentException(String.valueOf(message)); 73 | } 74 | } 75 | 76 | /** 77 | * Ensures the truth of an expression involving one or more parameters 78 | * to the calling method. 79 | * 80 | * @param expression a boolean expression 81 | * @param fmt the exception message with format string 82 | * @param args arguments referenced by the format specifiers in the format 83 | * string 84 | * @throws IllegalArgumentException if {@code expression} is false 85 | */ 86 | public static void requireTrue(boolean expression, String fmt, Object... args) { 87 | if (!expression) { 88 | throw new IllegalArgumentException(String.format(fmt, args)); 89 | } 90 | } 91 | 92 | private Requires() { 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/utils/ThreadId.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | /** 4 | * Created by 周思成 on 2020/4/14 15:34 5 | */ 6 | 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Collections; 12 | import java.util.List; 13 | import java.util.concurrent.TimeUnit; 14 | 15 | /** 16 | * Replicator id with lock. 17 | * 18 | * @author boyan (boyan@alibaba-inc.com) 19 | * 20 | * 2018-Mar-29 10:59:47 AM 21 | */ 22 | public class ThreadId { 23 | 24 | private static final Logger LOG = LoggerFactory.getLogger(ThreadId.class); 25 | 26 | private static final int TRY_LOCK_TIMEOUT_MS = 10; 27 | 28 | private final Object data; 29 | private final NonReentrantLock lock = new NonReentrantLock(); 30 | private final List pendingErrors = Collections.synchronizedList(new ArrayList<>()); 31 | private final OnError onError; 32 | private volatile boolean destroyed; 33 | 34 | /** 35 | * @author boyan (boyan@alibaba-inc.com) 36 | * 37 | * 2018-Mar-29 11:01:54 AM 38 | */ 39 | public interface OnError { 40 | 41 | /** 42 | * Error callback, it will be called in lock, but should take care of unlocking it. 43 | * 44 | * @param id the thread id 45 | * @param data the data 46 | * @param errorCode the error code 47 | */ 48 | void onError(final ThreadId id, final Object data, final int errorCode); 49 | } 50 | 51 | public ThreadId(final Object data, final OnError onError) { 52 | super(); 53 | this.data = data; 54 | this.onError = onError; 55 | this.destroyed = false; 56 | } 57 | 58 | public Object getData() { 59 | return this.data; 60 | } 61 | 62 | public Object lock() { 63 | if (this.destroyed) { 64 | return null; 65 | } 66 | try { 67 | while (!this.lock.tryLock(TRY_LOCK_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 68 | if (this.destroyed) { 69 | return null; 70 | } 71 | } 72 | } catch (final InterruptedException e) { 73 | Thread.currentThread().interrupt(); // reset 74 | return null; 75 | } 76 | // Got the lock, double checking state. 77 | if (this.destroyed) { 78 | // should release lock 79 | this.lock.unlock(); 80 | return null; 81 | } 82 | return this.data; 83 | } 84 | 85 | public void unlock() { 86 | if (!this.lock.isHeldByCurrentThread()) { 87 | LOG.warn("Fail to unlock with {}, the lock is held by {} and current thread is {}.", this.data, 88 | this.lock.getOwner(), Thread.currentThread()); 89 | return; 90 | } 91 | // calls all pending errors before unlock 92 | boolean doUnlock = true; 93 | try { 94 | final List errors; 95 | synchronized (this.pendingErrors) { 96 | errors = new ArrayList<>(this.pendingErrors); 97 | this.pendingErrors.clear(); 98 | } 99 | for (final Integer code : errors) { 100 | // The lock will be unlocked in onError. 101 | doUnlock = false; 102 | if (this.onError != null) { 103 | this.onError.onError(this, this.data, code); 104 | } 105 | } 106 | } finally { 107 | if (doUnlock) { 108 | this.lock.unlock(); 109 | } 110 | } 111 | } 112 | 113 | public void join() { 114 | while (!this.destroyed) { 115 | Thread.yield(); 116 | } 117 | } 118 | 119 | @Override 120 | public String toString() { 121 | return this.data.toString(); 122 | } 123 | 124 | public void unlockAndDestroy() { 125 | if (this.destroyed) { 126 | return; 127 | } 128 | this.destroyed = true; 129 | if (!this.lock.isHeldByCurrentThread()) { 130 | LOG.warn("Fail to unlockAndDestroy with {}, the lock is held by {} and current thread is {}.", this.data, 131 | this.lock.getOwner(), Thread.currentThread()); 132 | return; 133 | } 134 | this.lock.unlock(); 135 | } 136 | 137 | /** 138 | * Set error code, if it tryLock success, run the onError callback 139 | * with code immediately, else add it into pending errors and will 140 | * be called before unlock. 141 | * 142 | * @param errorCode error code 143 | */ 144 | public void setError(final int errorCode) { 145 | if (this.destroyed) { 146 | return; 147 | } 148 | if (this.lock.tryLock()) { 149 | if (this.destroyed) { 150 | this.lock.unlock(); 151 | return; 152 | } 153 | if (this.onError != null) { 154 | // The lock will be unlocked in onError. 155 | this.onError.onError(this, this.data, errorCode); 156 | } 157 | } else { 158 | this.pendingErrors.add(errorCode); 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/main/java/utils/ThreadPoolUtil.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | import java.util.concurrent.*; 4 | 5 | /** 6 | * Created by 周思成 on 2020/3/13 22:08 7 | */ 8 | 9 | public class ThreadPoolUtil { 10 | 11 | /** 12 | * The default rejected execution handler 13 | */ 14 | private static final RejectedExecutionHandler defaultHandler = new ThreadPoolExecutor.AbortPolicy(); 15 | 16 | public static PoolBuilder newBuilder() { 17 | return new PoolBuilder(); 18 | } 19 | 20 | 21 | /** 22 | * Creates a new {@code MetricThreadPoolExecutor} or {@code LogThreadPoolExecutor} 23 | * with the given initial parameters. 24 | * 25 | * @param poolName the name of the thread pool 26 | * @param coreThreads the number of threads to keep in the pool, even if they are 27 | * idle, unless {@code allowCoreThreadTimeOut} is set. 28 | * @param maximumThreads the maximum number of threads to allow in the pool 29 | * @param keepAliveSeconds when the number of threads is greater than the core, this 30 | * is the maximum time (seconds) that excess idle threads will 31 | * wait for new tasks before terminating. 32 | * @param workQueue the queue to use for holding tasks before they are executed. 33 | * This queue will hold only the {@code Runnable} tasks submitted 34 | * by the {@code execute} method. 35 | * @param threadFactory the factory to use when the executor creates a new thread 36 | * @param handler the handler to use when execution is blocked because the 37 | * thread bounds and queue capacities are reached 38 | * @throws IllegalArgumentException if one of the following holds:
39 | * {@code corePoolSize < 0}
40 | * {@code keepAliveSeconds < 0}
41 | * {@code maximumPoolSize <= 0}
42 | * {@code maximumPoolSize < corePoolSize} 43 | * @throws NullPointerException if {@code workQueue} 44 | * or {@code threadFactory} or {@code handler} is null 45 | */ 46 | public static ThreadPoolExecutor newThreadPool(final String poolName, 47 | final int coreThreads, final int maximumThreads, 48 | final long keepAliveSeconds, 49 | final BlockingQueue workQueue, 50 | final ThreadFactory threadFactory, 51 | final RejectedExecutionHandler handler) { 52 | final TimeUnit unit = TimeUnit.SECONDS; 53 | 54 | return new LogThreadPoolExecutor(coreThreads, maximumThreads, keepAliveSeconds, unit, workQueue, 55 | threadFactory, handler, poolName); 56 | 57 | } 58 | 59 | 60 | private ThreadPoolUtil() { 61 | } 62 | 63 | 64 | public static class PoolBuilder { 65 | private String poolName; 66 | 67 | private Integer coreThreads; 68 | private Integer maximumThreads; 69 | private Long keepAliveSeconds; 70 | private BlockingQueue workQueue; 71 | private ThreadFactory threadFactory; 72 | private RejectedExecutionHandler handler = ThreadPoolUtil.defaultHandler; 73 | 74 | public PoolBuilder poolName(final String poolName) { 75 | this.poolName = poolName; 76 | return this; 77 | } 78 | 79 | 80 | 81 | public PoolBuilder coreThreads(final Integer coreThreads) { 82 | this.coreThreads = coreThreads; 83 | return this; 84 | } 85 | 86 | public PoolBuilder maximumThreads(final Integer maximumThreads) { 87 | this.maximumThreads = maximumThreads; 88 | return this; 89 | } 90 | 91 | public PoolBuilder keepAliveSeconds(final Long keepAliveSeconds) { 92 | this.keepAliveSeconds = keepAliveSeconds; 93 | return this; 94 | } 95 | 96 | public PoolBuilder workQueue(final BlockingQueue workQueue) { 97 | this.workQueue = workQueue; 98 | return this; 99 | } 100 | 101 | public PoolBuilder threadFactory(final ThreadFactory threadFactory) { 102 | this.threadFactory = threadFactory; 103 | return this; 104 | } 105 | 106 | public PoolBuilder rejectedHandler(final RejectedExecutionHandler handler) { 107 | this.handler = handler; 108 | return this; 109 | } 110 | 111 | public ThreadPoolExecutor build() { 112 | Requires.requireNonNull(this.poolName, "poolName"); 113 | 114 | Requires.requireNonNull(this.coreThreads, "coreThreads"); 115 | Requires.requireNonNull(this.maximumThreads, "maximumThreads"); 116 | Requires.requireNonNull(this.keepAliveSeconds, "keepAliveSeconds"); 117 | Requires.requireNonNull(this.workQueue, "workQueue"); 118 | Requires.requireNonNull(this.threadFactory, "threadFactory"); 119 | Requires.requireNonNull(this.handler, "handler"); 120 | 121 | return ThreadPoolUtil.newThreadPool(this.poolName, this.coreThreads, 122 | this.maximumThreads, this.keepAliveSeconds, this.workQueue, this.threadFactory, this.handler); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/utils/TimeHelper.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | /** 4 | * Created by 周思成 on 2020/3/12 13:49 5 | */ 6 | import org.joda.time.DateTime; 7 | /** 8 | * 全局时间工具 9 | * 单例模式 10 | */ 11 | public class TimeHelper { 12 | 13 | private static DateTime dateTime = new DateTime(); 14 | 15 | private TimeHelper(){} 16 | 17 | public static DateTime getDateTime(){ 18 | return dateTime; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/utils/TimerManager.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | /** 4 | * Created by 周思成 on 2020/4/5 23:49 5 | */ 6 | 7 | import com.alipay.remoting.NamedThreadFactory; 8 | import core.Lifecycle; 9 | 10 | import java.util.concurrent.Executors; 11 | import java.util.concurrent.ScheduledExecutorService; 12 | import java.util.concurrent.ScheduledFuture; 13 | import java.util.concurrent.TimeUnit; 14 | 15 | /** 16 | * The global timer manager. 17 | * 18 | * @author boyan (boyan@alibaba-inc.com) 19 | * 20 | * 2018-Mar-30 3:24:34 PM 21 | */ 22 | public class TimerManager implements Lifecycle { 23 | 24 | private ScheduledExecutorService executor; 25 | 26 | @Override 27 | public boolean init(Integer coreSize) { 28 | this.executor = Executors.newScheduledThreadPool(coreSize, new NamedThreadFactory( 29 | "JRaft-Node-ScheduleThreadPool-", true)); 30 | return true; 31 | } 32 | 33 | @Override 34 | public void shutdown() { 35 | if (this.executor != null) { 36 | this.executor.shutdownNow(); 37 | this.executor = null; 38 | } 39 | } 40 | 41 | private void checkStarted() { 42 | if (this.executor == null) { 43 | throw new IllegalStateException("Please init timer manager."); 44 | } 45 | } 46 | 47 | public ScheduledFuture schedule(final Runnable command, final long delay, final TimeUnit unit) { 48 | checkStarted(); 49 | return this.executor.schedule(command, delay, unit); 50 | } 51 | 52 | public ScheduledFuture scheduleAtFixedRate(final Runnable command, final long initialDelay, final long period, 53 | final TimeUnit unit) { 54 | checkStarted(); 55 | return this.executor.scheduleAtFixedRate(command, initialDelay, period, unit); 56 | } 57 | 58 | public ScheduledFuture scheduleWithFixedDelay(final Runnable command, final long initialDelay, final long delay, 59 | final TimeUnit unit) { 60 | checkStarted(); 61 | return this.executor.scheduleWithFixedDelay(command, initialDelay, delay, unit); 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /src/main/java/utils/Utils.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | //import com.alipay.remoting.NamedThreadFactory; 4 | import com.alipay.sofa.rpc.common.struct.NamedThreadFactory; 5 | import entity.Closure; 6 | import entity.Status; 7 | import io.netty.util.internal.SystemPropertyUtil; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.util.concurrent.Future; 12 | import java.util.concurrent.SynchronousQueue; 13 | import java.util.concurrent.ThreadPoolExecutor; 14 | import java.util.concurrent.TimeUnit; 15 | import java.util.regex.Pattern; 16 | 17 | /** 18 | * Created by 周思成 on 2020/3/10 15:18 19 | * @author Mike 20 | */ 21 | 22 | public class Utils { 23 | 24 | 25 | /** 26 | * ANY IP address 0.0.0.0 27 | */ 28 | public static final String IP_ANY = "0.0.0.0"; 29 | 30 | 31 | private static final Logger LOG = LoggerFactory.getLogger(Utils.class); 32 | 33 | /** 34 | * The configured number of available processors. The default is {@link Runtime#availableProcessors()}. 35 | * This can be overridden by setting the system property "jraft.available_processors". 36 | */ 37 | private static final int CPUS = SystemPropertyUtil.getInt( 38 | "jraft.available_processors", Runtime 39 | .getRuntime().availableProcessors()); 40 | 41 | /** 42 | * Default jraft closure executor pool minimum size, CPUs by default. 43 | */ 44 | public static final int MIN_CLOSURE_EXECUTOR_POOL_SIZE = SystemPropertyUtil.getInt( 45 | "jraft.closure.threadpool.size.min", 46 | cpus()); 47 | 48 | /** 49 | * Default jraft closure executor pool maximum size. 50 | */ 51 | public static final int MAX_CLOSURE_EXECUTOR_POOL_SIZE = SystemPropertyUtil.getInt( 52 | "jraft.closure.threadpool.size.max", 53 | Math.max(100, cpus() * 5)); 54 | 55 | /** 56 | * Default jraft append-entries executor(send) pool size. 57 | */ 58 | public static final int APPEND_ENTRIES_THREADS_SEND = SystemPropertyUtil 59 | .getInt( 60 | "jraft.append.entries.threads.send", 61 | Math.max( 62 | 16, 63 | Ints.findNextPositivePowerOfTwo(cpus() * 2))); 64 | 65 | /** 66 | * Default jraft max pending tasks of append-entries per thread, 65536 by default. 67 | */ 68 | public static final int MAX_APPEND_ENTRIES_TASKS_PER_THREAD = SystemPropertyUtil 69 | .getInt( 70 | "jraft.max.append.entries.tasks.per.thread", 71 | 32768); 72 | 73 | /** 74 | * Whether use {@link com.alipay.sofa.jraft.util.concurrent.MpscSingleThreadExecutor}, true by default. 75 | */ 76 | public static final boolean USE_MPSC_SINGLE_THREAD_EXECUTOR = SystemPropertyUtil.getBoolean( 77 | "jraft.use.mpsc.single.thread.executor", 78 | true); 79 | 80 | /** 81 | * Global thread pool to run closure. 82 | */ 83 | private static ThreadPoolExecutor CLOSURE_EXECUTOR = ThreadPoolUtil 84 | .newBuilder() 85 | .poolName("JRAFT_CLOSURE_EXECUTOR") 86 | 87 | .coreThreads( 88 | MIN_CLOSURE_EXECUTOR_POOL_SIZE) 89 | .maximumThreads( 90 | MAX_CLOSURE_EXECUTOR_POOL_SIZE) 91 | .keepAliveSeconds(60L) 92 | .workQueue(new SynchronousQueue<>()) 93 | .threadFactory( 94 | new NamedThreadFactory( 95 | "JRaft-Closure-Executor-", true)) 96 | .build(); 97 | 98 | private static final Pattern GROUP_ID_PATTER = Pattern 99 | .compile("^[a-zA-Z][a-zA-Z0-9\\-_]*$"); 100 | 101 | /** 102 | * Get system CPUs count. 103 | */ 104 | public static int cpus() { 105 | return CPUS; 106 | } 107 | public static long monotonicMs() { 108 | return TimeUnit.NANOSECONDS.toMillis(System.nanoTime()); 109 | } 110 | 111 | /** 112 | * Run a task in thread pool,returns the future object. 113 | */ 114 | public static Future runInThread(final Runnable runnable) { 115 | return CLOSURE_EXECUTOR.submit(runnable); 116 | } 117 | 118 | public static Future runInThreadPool(final Runnable runnable) { 119 | return CLOSURE_EXECUTOR.submit(runnable); 120 | } 121 | 122 | public static Future runClosureInThread(final Closure done, final Status status) { 123 | if (done == null) { 124 | return null; 125 | } 126 | return runInThread(() -> { 127 | try { 128 | done.run(status); 129 | } catch (final Throwable t) { 130 | LOG.error("Fail to run done closure", t); 131 | } 132 | }); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/utils/ZeroByteStringHelper.java: -------------------------------------------------------------------------------- 1 | //package com.google.protobuf; 2 | // 3 | //import com.google.protobuf.ByteString; 4 | //import com.google.protobuf.RopeByteString; 5 | // 6 | //import java.io.IOException; 7 | //import java.nio.ByteBuffer; 8 | //import java.util.List; 9 | // 10 | ///** 11 | // * Created by 周思成 on 2020/4/17 15:26 12 | // */ 13 | //public class ZeroByteStringHelper { 14 | // 15 | // /** 16 | // * Wrap a byte array into a ByteString. 17 | // */ 18 | // public static ByteString wrap(final byte[] bs) { 19 | // return ByteString.wrap(bs); 20 | // } 21 | // 22 | // /** 23 | // * Wrap a byte array into a ByteString. 24 | // * @param bs the byte array 25 | // * @param offset read start offset in array 26 | // * @param len read data length 27 | // *@return the result byte string. 28 | // */ 29 | // public static ByteString wrap(final byte[] bs, final int offset, final int len) { 30 | // return ByteString.wrap(bs, offset, len); 31 | // } 32 | // 33 | // /** 34 | // * Wrap a byte buffer into a ByteString. 35 | // */ 36 | // public static ByteString wrap(final ByteBuffer buf) { 37 | // return ByteString.wrap(buf); 38 | // } 39 | // 40 | // /** 41 | // * Carry the byte[] from {@link ByteString}, if failed, 42 | // * then call {@link ByteString#toByteArray()}. 43 | // * 44 | // * @param byteString the byteString source data 45 | // * @return carried bytes 46 | // */ 47 | // public static byte[] getByteArray(final ByteString byteString) { 48 | // final BytesCarrier carrier = new BytesCarrier(); 49 | // try { 50 | // byteString.writeTo(carrier); 51 | // if (carrier.isValid()) { 52 | // return carrier.getValue(); 53 | // } 54 | // } catch (final IOException ignored) { 55 | // // ignored 56 | // } 57 | // return byteString.toByteArray(); 58 | // } 59 | // 60 | // /** 61 | // * Concatenate the given strings while performing various optimizations to 62 | // * slow the growth rate of tree depth and tree node count. The result is 63 | // * either a {@link com.google.protobuf.ByteString.LeafByteString} or a 64 | // * {@link RopeByteString} depending on which optimizations, if any, were 65 | // * applied. 66 | // * 67 | // *

Small pieces of length less than {@link 68 | // * ByteString#CONCATENATE_BY_COPY_SIZE} may be copied by value here, as in 69 | // * BAP95. Large pieces are referenced without copy. 70 | // * 71 | // *

Most of the operation here is inspired by the now-famous paper 73 | // * 74 | // * @param left string on the left 75 | // * @param right string on the right 76 | // * @return concatenation representing the same sequence as the given strings 77 | // */ 78 | // public static ByteString concatenate(final ByteString left, final ByteString right) { 79 | // return RopeByteString.concatenate(left, right); 80 | // } 81 | // 82 | // /** 83 | // * @see #concatenate(ByteString, ByteString) 84 | // */ 85 | // public static ByteString concatenate(final List byteBuffers) { 86 | // final int size = byteBuffers.size(); 87 | // if (size == 0) { 88 | // return null; 89 | // } 90 | // if (size == 1) { 91 | // return wrap(byteBuffers.get(0)); 92 | // } 93 | // 94 | // ByteString left = null; 95 | // for (final ByteBuffer buf : byteBuffers) { 96 | // if (buf.remaining() > 0) { 97 | // if (left == null) { 98 | // left = wrap(buf); 99 | // } else { 100 | // left = concatenate(left, wrap(buf)); 101 | // } 102 | // } 103 | // } 104 | // return left; 105 | // } 106 | //} 107 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/service.RaftServiceFactory: -------------------------------------------------------------------------------- 1 | service.RaftServiceFactoryImpl -------------------------------------------------------------------------------- /src/main/resources/log4j.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoderiGenius/RocksRaft/e1365c67cceb5549d31d12f9452584bb15e786e2/src/main/resources/log4j.properties -------------------------------------------------------------------------------- /src/main/resources/properties.yml: -------------------------------------------------------------------------------- 1 | currentNodeOptions: 2 | electionTimeOut: 1000 3 | maxHeartBeatTime: 1000 4 | rpcProtocol: bolt 5 | serialization: protobuf 6 | port: 12200 7 | taskPort: 12201 8 | taskExecuteMethod: callback 9 | daemon: false 10 | groupId: raft 11 | address: localhost 12 | name: raft-1 13 | peerId: raft-1 14 | logStoragePath: src/main/resources/Rocksdb1 15 | logStorageName: log 16 | clientAddress: localhost 17 | clientPort: 12299 18 | otherNodes: 19 | - {peerId: raft-2, 20 | name: raft-2, 21 | address: localhost, 22 | taskPort: 12221, 23 | port: 12220} 24 | - {peerId: raft-3, 25 | name: raft-3, 26 | address: localhost, 27 | taskPort: 12231, 28 | port: 12230} 29 | # - {peerId: raft-4, 30 | # name: raft-4, 31 | # address: 192.168.1.10, 32 | # taskPort: 12201, 33 | # port: 12000} 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/main/resources/properties2.yml: -------------------------------------------------------------------------------- 1 | currentNodeOptions: 2 | electionTimeOut: 3000 3 | maxHeartBeatTime: 1000 4 | rpcProtocol: bolt 5 | serialization: protobuf 6 | port: 12220 7 | taskPort: 12221 8 | taskExecuteMethod: callback 9 | daemon: false 10 | groupId: raft 11 | address: localhost 12 | name: raft-2 13 | peerId: raft-2 14 | logStoragePath: src/main/resources/Rocksdb1 15 | logStorageName: log2 16 | clientAddress: localhost 17 | clientPort: 12299 18 | otherNodes: 19 | - {peerId: raft-1, 20 | name: raft-1, 21 | address: localhost, 22 | taskPort: 12201, 23 | port: 12200} 24 | - {peerId: raft-3, 25 | name: raft-3, 26 | address: localhost, 27 | taskPort: 12231, 28 | port: 12230} 29 | # - {peerId: raft-4, 30 | # name: raft-4, 31 | # address: 192.168.1.10, 32 | # taskPort: 12201, 33 | # port: 12000} 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/main/resources/properties3.yml: -------------------------------------------------------------------------------- 1 | currentNodeOptions: 2 | electionTimeOut: 3000 3 | maxHeartBeatTime: 1000 4 | rpcProtocol: bolt 5 | serialization: protobuf 6 | port: 12230 7 | taskPort: 12231 8 | taskExecuteMethod: callback 9 | daemon: false 10 | groupId: raft 11 | address: localhost 12 | name: raft-3 13 | peerId: raft-3 14 | logStoragePath: src/main/resources/Rocksdb1 15 | logStorageName: log3 16 | clientAddress: localhost 17 | clientPort: 12299 18 | otherNodes: 19 | - {peerId: raft-2, 20 | name: raft-2, 21 | address: localhost, 22 | taskPort: 12221, 23 | port: 12220} 24 | - {peerId: raft-1, 25 | name: raft-1, 26 | address: localhost, 27 | taskPort: 12201, 28 | port: 12200} 29 | # - {peerId: raft-4, 30 | # name: raft-4, 31 | # address: 192.168.1.10, 32 | # taskPort: 12201, 33 | # port: 12000} 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/main/resources/protobuf/RPC.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option java_package = "rpc"; 3 | package protobuf; 4 | option java_outer_classname = "RpcRequests"; 5 | import "raft.proto"; 6 | 7 | message PingRequest { 8 | int64 send_timestamp = 1; 9 | } 10 | enum AppendEntriesStatus{ 11 | APPROVED = 0; 12 | FAILED = 1; 13 | } 14 | message AppendEntriesRequests{ 15 | repeated AppendEntriesRequest args = 1; 16 | 17 | 18 | } 19 | 20 | message AppendEntriesResponses{ 21 | repeated AppendEntriesResponse args = 1; 22 | AppendEntriesStatus appendEntriesStatus = 2; 23 | } 24 | 25 | message ErrorResponse{ 26 | int64 errorCode = 1; 27 | string errorMsg = 2; 28 | } 29 | 30 | message TimeoutNowRequest{ 31 | string server_id = 1; 32 | string peer_id = 2; 33 | int64 term = 3; 34 | } 35 | message TimeoutNowResponse{ 36 | int64 term = 1; 37 | bool success = 2; 38 | } 39 | 40 | message RequestVoteRequest { 41 | string server_id = 1; 42 | string peer_id = 2; 43 | int64 term = 3; 44 | int64 committed_log_term = 4; 45 | int64 committed_log_index = 5; 46 | bool pre_vote = 6; 47 | }; 48 | message RequestVoteResponse { 49 | int64 term = 1; 50 | bool granted = 2; 51 | string peer_name = 3; 52 | }; 53 | 54 | 55 | message RequestPreVoteRequest { 56 | string server_id = 1; 57 | string peer_id = 2; 58 | int64 pre_term = 3; 59 | int64 committed_log_index = 4; 60 | int64 committed_log_term = 5; 61 | bool pre_vote = 6; 62 | string peer_name = 7; 63 | }; 64 | message RequestPreVoteResponse { 65 | int64 term = 1; 66 | bool granted = 2; 67 | string peer_name = 3; 68 | }; 69 | 70 | message AppendEntriesRequestHeader { 71 | 72 | string server_id = 1; 73 | string peer_id = 2; 74 | }; 75 | 76 | 77 | message AppendEntriesRequest { 78 | string group_id = 1; 79 | string server_id = 2; 80 | string peer_id = 3; 81 | int64 term = 4; 82 | int64 prev_log_term = 5; 83 | int64 prev_log_index = 6; 84 | EntryMeta entries = 7; 85 | int64 committed_index = 8; 86 | bytes data = 9; 87 | string address = 10; 88 | int32 port = 11; 89 | int32 taskPort = 12; 90 | int64 readIndex=13; 91 | string dataString = 14; 92 | }; 93 | 94 | message AppendEntriesResponse { 95 | int64 term = 1; 96 | bool success = 2; 97 | int64 last_log_index = 3; 98 | string address = 4; 99 | int32 port = 5; 100 | string peerId = 6; 101 | int64 readIndex = 7; 102 | string reason = 8; 103 | }; 104 | message ReadIndexRequest { 105 | string group_id = 1; 106 | string server_id = 2; 107 | bytes entries = 3; 108 | string peer_id = 4; 109 | }; 110 | 111 | message ReadIndexResponse { 112 | int64 index = 1; 113 | bool success = 2; 114 | }; 115 | 116 | message NotifyFollowerStableRequest{ 117 | int64 lastIndex = 1; 118 | int64 firstIndex = 2; 119 | string peerId = 3; 120 | bool success = 4; 121 | 122 | } 123 | message NotifyFollowerStableResponse{ 124 | int64 lastIndex = 1; 125 | int64 firstIndex = 2; 126 | int64 term = 3; 127 | bool success = 4; 128 | } 129 | 130 | 131 | message NotifyFollowerToApplyRequest{ 132 | int64 lastIndex = 1; 133 | int64 firstIndex = 2; 134 | string peerId = 3; 135 | bool success = 4; 136 | 137 | } 138 | message NotifyFollowerToApplyResponse{ 139 | int64 lastIndex = 1; 140 | int64 firstIndex = 2; 141 | int64 term = 3; 142 | string followerId = 4; 143 | bool success = 5; 144 | } -------------------------------------------------------------------------------- /src/main/resources/protobuf/enum.proto: -------------------------------------------------------------------------------- 1 | syntax="proto3"; 2 | package protobuf; 3 | 4 | option java_package="rpc"; 5 | option java_outer_classname = "EnumOutter"; 6 | 7 | enum EntryType { 8 | ENTRY_TYPE_UNKNOWN = 0; 9 | ENTRY_TYPE_NO_OP = 1; 10 | ENTRY_TYPE_DATA = 2; 11 | ENTRY_TYPE_CONFIGURATION= 3; 12 | }; 13 | 14 | enum ErrorType { 15 | ERROR_TYPE_NONE = 0; 16 | ERROR_TYPE_LOG = 1; 17 | ERROR_TYPE_STABLE = 2; 18 | ERROR_TYPE_SNAPSHOT = 3; 19 | ERROR_TYPE_STATE_MACHINE = 4; 20 | ERROR_TYPE_META = 5; 21 | }; 22 | -------------------------------------------------------------------------------- /src/main/resources/protobuf/logOuter.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package protobuf; 3 | 4 | option java_package = "rpc"; 5 | option java_outer_classname = "LogOuter"; 6 | 7 | message LogEntry{ 8 | int64 term=1; 9 | int64 index=2; 10 | bytes data=3; 11 | 12 | } -------------------------------------------------------------------------------- /src/main/resources/protobuf/raft.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package protobuf; 3 | import "enum.proto"; 4 | option java_package = "rpc"; 5 | option java_outer_classname = "RaftOutter"; 6 | 7 | message EntryMeta{ 8 | int64 term = 1; 9 | EntryType type = 2; 10 | string peers = 3; 11 | int64 data_len = 4; 12 | string old_peers = 5; 13 | 14 | int64 checksum = 6; 15 | string learners = 7; 16 | string old_learners = 8; 17 | } -------------------------------------------------------------------------------- /src/main/resources/protobuf/rpc.bat: -------------------------------------------------------------------------------- 1 | protoc --java_out=./ ./RPC.proto -------------------------------------------------------------------------------- /src/test/java/TestHeartbeatChecker.java: -------------------------------------------------------------------------------- 1 | //import com.alipay.sofa.rpc.common.annotation.JustForTest; 2 | // 3 | ///** 4 | // * Created by 周思成 on 2020/4/23 0:45 5 | // */ 6 | //import core.HeartbeatThreadFactory; 7 | //import core.NodeImpl; 8 | //import entity.ElectionTimeOutClosure; 9 | //import entity.Heartbeat; 10 | //import entity.TimeOutChecker; 11 | //import org.junit.Test; 12 | //import org.slf4j.Logger; 13 | //import org.slf4j.LoggerFactory; 14 | //import utils.Utils; 15 | // 16 | //import java.util.concurrent.LinkedBlockingDeque; 17 | //import java.util.concurrent.ThreadPoolExecutor; 18 | //import java.util.concurrent.TimeUnit; 19 | //import java.util.concurrent.atomic.AtomicLong; 20 | // 21 | //public class TestHeartbeatChecker { 22 | // public static final Logger LOG = LoggerFactory.getLogger(NodeImpl.class); 23 | // 24 | // @Test 25 | // public void testHeartbeat() { 26 | //// Heartbeat heartbeat = new Heartbeat(1, 2 27 | //// , 0, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>() 28 | //// , new HeartbeatThreadFactory(), new ThreadPoolExecutor.DiscardPolicy()); 29 | //// 30 | //// heartbeat.setChecker( 31 | //// new TimeOutChecker( Utils.monotonicMs(), new ElectionTimeOutClosure(),"")); 32 | // 33 | // LOG.debug("123"); 34 | // LOG.info("123123"); 35 | // LOG.error("123123123"); 36 | // LOG.warn("12312312312312"); 37 | // AtomicLong atomicLong = new AtomicLong(3/2+1); 38 | // LOG.info(atomicLong.get()+""); 39 | // } 40 | //} 41 | -------------------------------------------------------------------------------- /src/test/java/TestTimeManager.java: -------------------------------------------------------------------------------- 1 | //import com.google.protobuf.ZeroByteStringHelper; 2 | //import org.junit.Test; 3 | //import rpc.RpcRequests; 4 | //import utils.TimerManager; 5 | // 6 | //import java.nio.ByteBuffer; 7 | //import java.util.concurrent.TimeUnit; 8 | // 9 | ///** 10 | // * Created by 周思成 on 2020/4/23 18:41 11 | // */ 12 | // 13 | //public class TestTimeManager { 14 | // 15 | // TimerManager timerManager = new TimerManager(); 16 | // @Test 17 | // public void Test() throws InterruptedException { 18 | //// Runnable runnable = () -> { 19 | //// System.out.println(123); 20 | //// try { 21 | //// Test(); 22 | //// } catch (InterruptedException e) { 23 | //// e.printStackTrace(); 24 | //// } 25 | //// //timerManager.schedule(runnable,1000, TimeUnit.MILLISECONDS); 26 | //// } 27 | //// ; 28 | // RpcRequests.AppendEntriesRequest.Builder builder = RpcRequests.AppendEntriesRequest.newBuilder(); 29 | // ByteBuffer byteBuffer = ByteBuffer.wrap("123".getBytes()); 30 | // ByteBuffer byteBuffer1 = null; 31 | // builder.setData(ZeroByteStringHelper.wrap(byteBuffer1)); 32 | // RpcRequests.AppendEntriesRequest appendEntriesRequest = builder.build(); 33 | // String t = new String(ZeroByteStringHelper.getByteArray(appendEntriesRequest.getData())); 34 | // System.out.println(t); 35 | // 36 | //// timerManager.init(100); 37 | //// timerManager.schedule(runnable,1000, TimeUnit.MILLISECONDS); 38 | //// Thread.currentThread().join(); 39 | // } 40 | //} 41 | -------------------------------------------------------------------------------- /论文github开源版.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoderiGenius/RocksRaft/e1365c67cceb5549d31d12f9452584bb15e786e2/论文github开源版.pdf --------------------------------------------------------------------------------