介绍
StateMachine 是实现这种一致性的关键部分之一,它的作用是:
-
保持一致性: StateMachine 负责执行分布式系统中的命令和操作,确保在不同节点上的数据状态保持一致。它通过在各个节点上应用相同的操作序列来达到这一目的。
-
处理命令: 分布式系统中的节点可以接收来自客户端或其他节点的命令和操作请求。StateMachine 会根据这些命令执行相应的操作,然后将结果应用到本地状态中,从而确保系统在不同节点上的状态保持一致。
-
容错和恢复: 在分布式系统中,节点可能会发生故障,因此需要能够进行故障检测和恢复。StateMachine 能够在节点故障后重新同步状态,确保数据的一致性和完整性。
-
顺序性保证: 分布式系统中,命令的执行可能会出现并发情况,需要保证命令的执行顺序。StateMachine 负责按照正确的顺序执行命令,以确保最终的状态是一致的。
看代码
StateMachine的实现要依赖于具体的应用中实现,Ratis中提供的BaseStateMachine供大家继承使用,其中实现了诸多接口,下面对各个接口的功能简要介绍。
DataApi,关于DataApi在Ratis官方给出的例子FileStoreStateMachine里面有用到,适用于如文件写数据同步的操作,按照raftLog的同步逻辑,如果要实现同步则需要Leader将数据commit到raftLog中,然后updater去日志中读取到内存中并最后写入到文件中,这其中涉及到了多次的读写数据操作,比较浪费IO资源,且效率低。
DataApi主要就是为了解决上述多次拷贝问题,在commit的时候直接将数据写入到文件中,然后提交空的Log,statemachine负责保证数据的一致性。
interface DataApi {
/** A noop implementation of {@link DataApi}. */
DataApi DEFAULT = new DataApi() {};
default CompletableFuture<ByteString> read(LogEntryProto entry) {
throw new UnsupportedOperationException("This method is NOT supported.");
}
default CompletableFuture<?> write(LogEntryProto entry) {
return CompletableFuture.completedFuture(null);
}
default CompletableFuture<DataStream> stream(RaftClientRequest request) {
return CompletableFuture.completedFuture(null);
}
default CompletableFuture<?> link(DataStream stream, LogEntryProto entry) {
return CompletableFuture.completedFuture(null);
}
default CompletableFuture<Void> flush(long logIndex) {
return CompletableFuture.completedFuture(null);
}
default CompletableFuture<Void> truncate(long logIndex) {
return CompletableFuture.completedFuture(null);
}
}
EventApi, 用于Raft出现变化时,通知上层stateMachine状态变化的函数
interface EventApi {
/** A noop implementation of {@link EventApi}. */
EventApi DEFAULT = new EventApi() {};
default void notifyLeaderChanged(RaftGroupMemberId groupMemberId, RaftPeerId newLeaderId) {}
default void notifyTermIndexUpdated(long term, long index) {}
default void notifyConfigurationChanged(long term, long index, RaftConfigurationProto newRaftConfiguration) {}
default void notifyGroupRemove() {}
default void notifyLogFailed(Throwable cause, LogEntryProto failedEntry) {}
default void notifySnapshotInstalled(InstallSnapshotResult result, long snapshotIndex, RaftPeer peer) {}
default void notifyServerShutdown(RoleInfoProto roleInfo) {}
}
LeaderEventApi, 类似于上边的eventApi,是当peer的状态是leader的时候,用于同时statemachine的函数。
interface LeaderEventApi {
/** A noop implementation of {@link LeaderEventApi}. */
LeaderEventApi DEFAULT = new LeaderEventApi() {};
default void notifyFollowerSlowness(RoleInfoProto leaderInfo, RaftPeer slowFollower) {}
@Deprecated
default void notifyFollowerSlowness(RoleInfoProto leaderInfo) {}
default void notifyNotLeader(Collection<TransactionContext> pendingEntries) throws IOException {}
}
FollowerEventApi, 与上述类似当前peer是follower状态,同时上层stateMachine安装snapshot的函数
interface FollowerEventApi {
/** A noop implementation of {@link FollowerEventApi}. */
FollowerEventApi DEFAULT = new FollowerEventApi() {};
default void notifyExtendedNoLeader(RoleInfoProto roleInfoProto) {}
default CompletableFuture<TermIndex> notifyInstallSnapshotFromLeader(
RoleInfoProto roleInfoProto, TermIndex firstTermIndexInLog) {
return CompletableFuture.completedFuture(null);
}
}
stateMachine中还有一些常用的函数,下面做简单的解释,参考自:Java Raft: Apache Ratis源码阅读 - 知乎
生命周期接口
void initialize(RaftServer raftServer, RaftGroupId raftGroupId, RaftStorage storage);
LifeCycle.State getLifeCycleState();
void pause();
void reinitialize() throws IOException;
snapshot接口
SnapshotInfo getLatestSnapshot();
void cleanupOldSnapshots(SnapshotRetentionPolicy snapshotRetentionPolicy) throws IOException;
long takeSnapshot() throws IOException;
查询状态机接口
CompletableFuture<Message> query(Message request);
CompletableFuture<Message> queryStale(Message request, long minIndex);
更改状态机接口
// 将用户的请求转化为TransactionContext
TransactionContext startTransaction(RaftClientRequest request) throws IOException;
// 提交日志前可以做的额外逻辑
TransactionContext preAppendTransaction(TransactionContext trx) throws IOException;
// 告知用户Transaction失败
TransactionContext cancelTransaction(TransactionContext trx) throws IOException;
TransactionContext applyTransactionSerial(TransactionContext trx) throws InvalidProtocolBufferException;
// 按顺序提交Transaction。 SM决定对trx的操作顺序
CompletableFuture<Message> applyTransaction(TransactionContext trx);