Copycat server之间的configure是如何,何时被同步的?

 

大家可以看到,只有leader可以同步配置

 

1. 显式的调用LeaderState.configure

LeaderState.configure

/**
* Commits the given configuration.
*/
protected CompletableFuture<Long> configure(Collection<Member> members) {
final long index;
try (ConfigurationEntry entry = context.getLog().create(ConfigurationEntry.class)) {
entry.setTerm(context.getTerm())
.setTimestamp(System.currentTimeMillis())
.setMembers(members);
index = context.getLog().append(entry); //先把configuration写入local log
LOGGER.debug("{} - Appended {}", context.getCluster().member().address(), entry); // Store the index of the configuration entry in order to prevent other configurations from
// being logged and committed concurrently. This is an important safety property of Raft.
configuring = index; //configuring用于互斥
context.getClusterState().configure(new Configuration(entry.getIndex(), entry.getTerm(), entry.getTimestamp(), entry.getMembers())); //更新ClusterState
} return appender.appendEntries(index).whenComplete((commitIndex, commitError) -> { //调用appendEntries同步configuration
context.checkThread();
if (isOpen()) {
// Reset the configuration index to allow new configuration changes to be committed.
configuring = 0; //configuring完成
}
});
}

何处显式调用?

private void appendInitialEntries() {
  // Append a configuration entry to propagate the leader's cluster configuration.
configure(context.getCluster().members());

 

public CompletableFuture<JoinResponse> join(final JoinRequest request) {
  configure(members).whenComplete((index, error) -> {

 

@Override
public CompletableFuture<ReconfigureResponse> reconfigure(final ReconfigureRequest request) {
  configure(members).whenComplete((index, error) -> {

 

@Override
public CompletableFuture<LeaveResponse> leave(final LeaveRequest request) {
  configure(members).whenComplete((index, error) -> {

 

2. 在发送hb,和appendEntries时,也会自动发生同步

因为上面调用configure也是会调用到

LeaderAppender.appendEntries(MemberState member)

和普通append不同的是,会走到这个逻辑,

    // If the member term is less than the current term or the member's configuration index is less
// than the local configuration index, send a configuration update to the member.
// Ensure that only one configuration attempt per member is attempted at any given time by storing the
// member state in a set of configuring members.
// Once the configuration is complete sendAppendRequest will be called recursively.
else if (member.getConfigTerm() < context.getTerm() || member.getConfigIndex() < context.getClusterState().getConfiguration().index()) {
if (member.canConfigure()) {
sendConfigureRequest(member, buildConfigureRequest(member));
}
}

注意虽然是在appendEntries里面,

这里发出的是sendConfigureRequest,而不是appendRequest

因为leader的configuration发生变化,所以member.getConfigIndex一定是小的,所以要更新

AbstractAppender

/**
* Connects to the member and sends a configure request.
*/
protected void sendConfigureRequest(MemberState member, ConfigureRequest request) {
// Start the configure to the member.
member.startConfigure(); context.getConnections().getConnection(member.getMember().serverAddress()).whenComplete((connection, error) -> {
context.checkThread(); if (open) {
if (error == null) {
sendConfigureRequest(connection, member, request);
} else {
// Complete the configure to the member.
member.completeConfigure(); // 将configuring设置成false,表示configuring结束 // Trigger reactions to the request failure.
handleConfigureRequestFailure(member, request, error);
}
}
});
} /**
* Sends a configuration message.
*/
protected void sendConfigureRequest(Connection connection, MemberState member, ConfigureRequest request) {
LOGGER.debug("{} - Sent {} to {}", context.getCluster().member().address(), request, member.getMember().serverAddress());
connection.<ConfigureRequest, ConfigureResponse>send(request).whenComplete((response, error) -> {
context.checkThread(); // Complete the configure to the member.
member.completeConfigure(); if (open) {
if (error == null) {
LOGGER.debug("{} - Received {} from {}", context.getCluster().member().address(), response, member.getMember().serverAddress());
handleConfigureResponse(member, request, response);
} else {
LOGGER.warn("{} - Failed to configure {}", context.getCluster().member().address(), member.getMember().serverAddress());
handleConfigureResponseFailure(member, request, error);
}
}
});
}

 

LeaderAppender

@Override
protected void handleConfigureResponse(MemberState member, ConfigureRequest request, ConfigureResponse response) {
// Trigger commit futures if necessary.
updateHeartbeatTime(member, null); // 判断是否大部分member都已经完成configuration同步 super.handleConfigureResponse(member, request, response);
}

 

AbstractAppender

 

/**
* Handles a configuration response.
*/
protected void handleConfigureResponse(MemberState member, ConfigureRequest request, ConfigureResponse response) {
if (response.status() == Response.Status.OK) {
handleConfigureResponseOk(member, request, response);
} else {
handleConfigureResponseError(member, request, response);
}
}

 

/**
* Handles an OK configuration response.
*/
@SuppressWarnings("unused")
protected void handleConfigureResponseOk(MemberState member, ConfigureRequest request, ConfigureResponse response) {
// Reset the member failure count and update the member's status if necessary.
succeedAttempt(member); // Update the member's current configuration term and index according to the installed configuration.
member.setConfigTerm(request.term()).setConfigIndex(request.index()); // Recursively append entries to the member.
appendEntries(member);
}

 

在server上收到ConfigureRequest

ServerContext

public void connectServer(Connection connection) {
  connection.handler(ConfigureRequest.class, request -> state.configure(request)); 
在connectServer中,说明configuration只能在server间调用,client是不能调用的
 

configure,只在两个state中有实现

InactiveState
@Override
public CompletableFuture<ConfigureResponse> configure(ConfigureRequest request) {
context.checkThread();
logRequest(request);
updateTermAndLeader(request.term(), request.leader()); // 更新leader term Configuration configuration = new Configuration(request.index(), request.term(), request.timestamp(), request.members()); // 根据request创建Configuration // Configure the cluster membership. This will cause this server to transition to the
// appropriate state if its type has changed.
context.getClusterState().configure(configuration); // 调用ClusterState.configure // If the configuration is already committed, commit it to disk.
// Check against the actual cluster Configuration rather than the received configuration in
// case the received configuration was an older configuration that was not applied.
if (context.getCommitIndex() >= context.getClusterState().getConfiguration().index()) {
context.getClusterState().commit(); // 调用commit
} return CompletableFuture.completedFuture(logResponse(ConfigureResponse.builder()
.withStatus(Response.Status.OK) // 返回
.build()));
}

 

FollowerState
public CompletableFuture<ConfigureResponse> configure(ConfigureRequest request) {
CompletableFuture<ConfigureResponse> future = super.configure(request);
resetHeartbeatTimeout();
return future;
}

可以看到Follower里面只是多了resetHB

 

继续,

可以看到首先是ClusterState.configure

ClusterState中保存了当前server,所知道cluster的所有信息,其中包含Configuration对象

final class ClusterState implements Cluster, AutoCloseable {
private final ServerContext context;
private final ServerMember member;
private volatile Configuration configuration;

configure

/**
* Configures the cluster state.
*
* @param configuration The cluster configuration.
* @return The cluster state.
*/
ClusterState configure(Configuration configuration) {
// If the configuration index is less than the currently configured index, ignore it.
// Configurations can be persisted and applying old configurations can revert newer configurations.
if (this.configuration != null && configuration.index() <= this.configuration.index()) //如果是老的configuration,丢弃
return this; Instant time = Instant.ofEpochMilli(configuration.time()); // Iterate through members in the new configuration, add any missing members, and update existing members.
boolean transition = false;
for (Member member : configuration.members()) {
if (member.equals(this.member)) { //如果有我的配置变更
transition = this.member.type().ordinal() < member.type().ordinal(); //看下type是否promote,只有promote才要transition,不让demote
this.member.update(member.type(), time).update(member.clientAddress(), time);
members.add(this.member);
} else { //如果是更新其他member的配置
// If the member state doesn't already exist, create it.
MemberState state = membersMap.get(member.id());
if (state == null) { //如果是新member,初始化
state = new MemberState(new ServerMember(member.type(), member.serverAddress(), member.clientAddress(), time), this);
state.resetState(context.getLog());
this.members.add(state.getMember());
this.remoteMembers.add(state);
membersMap.put(member.id(), state);
addressMap.put(member.address(), state);
joinListeners.accept(state.getMember());
} // If the member type has changed, update the member type and reset its state.
state.getMember().update(member.clientAddress(), time);
if (state.getMember().type() != member.type()) { //如果member的type发生了变化,更新数据
state.getMember().update(member.type(), time);
state.resetState(context.getLog());
} // If the member status has changed, update the local member status.
if (state.getMember().status() != member.status()) { //如果status发送变化,更新
state.getMember().update(member.status(), time);
} // Update the optimized member collections according to the member type.
for (List<MemberState> memberType : memberTypes.values()) {
memberType.remove(state);
} List<MemberState> memberType = memberTypes.get(member.type());
if (memberType == null) {
memberType = new CopyOnWriteArrayList<>();
memberTypes.put(member.type(), memberType);
}
memberType.add(state);
}
} // Transition the local member only if the member is being promoted and not demoted.
// Configuration changes that demote the local member are only applied to the local server
// upon commitment. This ensures that e.g. a leader that's removing itself from the quorum
// can commit the configuration change prior to shutting down.
if (transition) { //做state transition
context.transition(this.member.type());
} // Iterate through configured members and remove any that no longer exist in the configuration.
int i = 0;
while (i < this.remoteMembers.size()) {
MemberState member = this.remoteMembers.get(i);
if (!configuration.members().contains(member.getMember())) {
this.members.remove(member.getMember());
this.remoteMembers.remove(i);
for (List<MemberState> memberType : memberTypes.values()) {
memberType.remove(member);
}
membersMap.remove(member.getMember().id());
addressMap.remove(member.getMember().address());
leaveListeners.accept(member.getMember());
} else {
i++;
}
} // If the local member was removed from the cluster, remove it from the members list.
if (!configuration.members().contains(member)) {
members.remove(member);
} this.configuration = configuration; //更新configuration对象 // Store the configuration if it's already committed.
if (context.getCommitIndex() >= configuration.index()) { //如果这个configuration已经被commit,store到存储上
context.getMetaStore().storeConfiguration(configuration);
} // Reassign members based on availability.
reassign(); //更新passive member的assignment,因为member变了,所以follower所对应的passive可能需要重新分配 return this;
}

 

ClusterState.commit

/**
* Commit the current configuration to disk.
*
* @return The cluster state.
*/
ClusterState commit() {
// Apply the configuration to the local server state.
context.transition(member.type());
if (!configuration.members().contains(member) && leaveFuture != null) {
leaveFuture.complete(null);
} // If the local stored configuration is older than the committed configuration, overwrite it.
if (context.getMetaStore().loadConfiguration().index() < configuration.index()) {
context.getMetaStore().storeConfiguration(configuration);
}
return this;
}

逻辑,就是把configuration存入本地盘

 

 

reconfigure

在什么地方被调用,

 

ServerMember

@Override
public CompletableFuture<Void> promote() {
return configure(Type.values()[type.ordinal() + 1]);
}

可以用reconfigure来更新配置,比如promote,demote

 

/**
* Demotes the server to the given type.
*/
CompletableFuture<Void> configure(Member.Type type) {
CompletableFuture<Void> future = new CompletableFuture<>();
cluster.getContext().getThreadContext().executor().execute(() -> configure(type, future));
return future;
}

 

/**
* Recursively reconfigures the cluster.
*/
private void configure(Member.Type type, CompletableFuture<Void> future) {
// Set a timer to retry the attempt to leave the cluster.
configureTimeout = cluster.getContext().getThreadContext().schedule(cluster.getContext().getElectionTimeout(), () -> {
configure(type, future); //设定schedule,反复重试
}); // Attempt to leave the cluster by submitting a LeaveRequest directly to the server state.
// Non-leader states should forward the request to the leader if there is one. Leader states
// will log, replicate, and commit the reconfiguration.
cluster.getContext().getServerState().reconfigure(ReconfigureRequest.builder()
.withIndex(cluster.getConfiguration().index())
.withTerm(cluster.getConfiguration().term())
.withMember(new ServerMember(type, serverAddress(), clientAddress(), updated))
.build()).whenComplete((response, error) -> {
if (error == null) {
if (response.status() == Response.Status.OK) {
cancelConfigureTimer(); //如果成功就cancel掉schedule
cluster.configure(new Configuration(response.index(), response.term(), response.timestamp(), response.members())); //更新clusterState中的configuration配置
future.complete(null);
} else if (response.error() == null || response.error() == CopycatError.Type.NO_LEADER_ERROR) {
cancelConfigureTimer();
configureTimeout = cluster.getContext().getThreadContext().schedule(cluster.getContext().getElectionTimeout().multipliedBy(2), () -> configure(type, future));
} else {
cancelConfigureTimer();
future.completeExceptionally(response.error().createException());
}
}
});
}

 

处理ReconfigureRequest

public void connectServer(Connection connection) {
  connection.handler(ReconfigureRequest.class, request -> state.reconfigure(request));

 

ReserveState
@Override
public CompletableFuture<ReconfigureResponse> reconfigure(ReconfigureRequest request) {
context.checkThread();
logRequest(request); if (context.getLeader() == null) {
return CompletableFuture.completedFuture(logResponse(ReconfigureResponse.builder()
.withStatus(Response.Status.ERROR)
.withError(CopycatError.Type.NO_LEADER_ERROR)
.build()));
} else {
return this.<ReconfigureRequest, ReconfigureResponse>forward(request)
.exceptionally(error -> ReconfigureResponse.builder()
.withStatus(Response.Status.ERROR)
.withError(CopycatError.Type.NO_LEADER_ERROR)
.build())
.thenApply(this::logResponse);
}
}

只是forward到leader

 

LeaderState

@Override
public CompletableFuture<ReconfigureResponse> reconfigure(final ReconfigureRequest request) {
// If another configuration change is already under way, reject the configuration.
// If the leader index is 0 or is greater than the commitIndex, reject the promote requests.
// Configuration changes should not be allowed until the leader has committed a no-op entry.
// See https://groups.google.com/forum/#!topic/raft-dev/t4xj6dJTP6E
if (configuring() || initializing()) { //如果正在configure或leader初始化,不能做配置变更
return CompletableFuture.completedFuture(logResponse(ReconfigureResponse.builder()
.withStatus(Response.Status.ERROR)
.build()));
} // If the configuration request index is less than the last known configuration index for
// the leader, fail the request to ensure servers can't reconfigure an old configuration.
if (request.index() > 0 && request.index() < context.getClusterState().getConfiguration().index() || request.term() != context.getClusterState().getConfiguration().term()
&& (existingMember.type() != request.member().type() || existingMember.status() != request.member().status())) {
return CompletableFuture.completedFuture(logResponse(ReconfigureResponse.builder()
.withStatus(Response.Status.ERROR)
.withError(CopycatError.Type.CONFIGURATION_ERROR)
.build()));
} Member member = request.member(); // If the client address is being set or has changed, update the configuration.
if (member.clientAddress() != null && (existingMember.clientAddress() == null || !existingMember.clientAddress().equals(member.clientAddress()))) {
existingMember.update(member.clientAddress(), Instant.now());
} // Update the member type.
existingMember.update(request.member().type(), Instant.now()); Collection<Member> members = context.getCluster().members(); CompletableFuture<ReconfigureResponse> future = new CompletableFuture<>();
configure(members).whenComplete((index, error) -> { //调用configure
context.checkThread();
if (isOpen()) {
if (error == null) { //如果成功,更新local配置
future.complete(logResponse(ReconfigureResponse.builder()
.withStatus(Response.Status.OK)
.withIndex(index)
.withTerm(context.getClusterState().getConfiguration().term())
.withTime(context.getClusterState().getConfiguration().time())
.withMembers(members)
.build()));
} else {
future.complete(logResponse(ReconfigureResponse.builder()
.withStatus(Response.Status.ERROR)
.withError(CopycatError.Type.INTERNAL_ERROR)
.build()));
}
}
});
return future;
}

Copycat - configure的更多相关文章

  1. 国产深度学习框架mindspore-1.3.0 gpu版本无法进行源码编译

    官网地址: https://www.mindspore.cn/install 所有依赖环境 进行sudo make install 安装,最终报错: 错误记录信息: cat     /tmp/mind ...

  2. Copycat - StateMachine

    看下用户注册StateMachine的过程, CopycatServer.Builder builder = CopycatServer.builder(address); builder.withS ...

  3. Copycat - MemberShip

    https://github.com/atomix/copycat   http://atomix.io/copycat/docs/membership/   为了便于实现,Copycat把membe ...

  4. Configure a VLAN on top of a team with NetworkManager (nmcli) in RHEL7

    SOLUTION VERIFIED September 13 2016 KB1248793 Environment Red Hat Enterprise Linux 7 NetworkManager ...

  5. Configure a bridge interface over a VLAN tagged bonded interface

    SOLUTION VERIFIED February 5 2014 KB340153 Environment Red Hat Enterprise Linux 6 (All Versions) Red ...

  6. Configure a bridged network interface for KVM using RHEL 5.4 or later?

    environment Red Hat Enterprise Linux 5.4 or later Red Hat Enterprise Linux 6.0 or later KVM virtual ...

  7. [转]Linux中configure/makefile

    本文教你如何使用autoconf.automake等来制作一个以源代码形式(.tar.gz)发布的软件.并可在执行configure时使用自定义参数. 一.概述和基础知识 在Linux下得到一个以源代 ...

  8. Install and Configure SharePoint 2013 Workflow

    这篇文章主要briefly introduce the Install and configure SharePoint 2013 Workflow. Microsoft 推出了新的Workflow ...

  9. SharePoint 2013 configure and publish infopth

    This article will simply descript how to configure and publish a InfoPath step by step. Note: To con ...

随机推荐

  1. 使用python抓取58手机维修信息

    之前在ququ的博客上看到说 python 中的BeautifulSoup 挺好玩的,今天下午果断下载下来,看了下api,挺好用的,完了2把,不错. 晚上写了一个使用python抓取58手机维修信息的 ...

  2. 【九天教您南方cass 9.1】 12 道路断面土方计算

    同学们大家好,欢迎收看由老王测量上班记出品的cass9.1视频课程 我是本节课主讲老师九天. 我们讲课的教程附件也是共享的,请注意索取 在测量空间中. [点击索取cass教程]5元立得 (给客服说暗号 ...

  3. WebService学习总结——调用第三方提供的webService服务

    互联网上面有很多的免费webService服务,我们可以调用这些免费的WebService服务,将一些其他网站的内容信息集成到我们的Web应用中显示,下面就以获取天气预报数据. 气象中心的管理系统将收 ...

  4. [数据]matplotlib总结

    这里权当一个matplotlib的用法小结,主要用于记录,以防忘记. 需要安装一下Anaconda,这里推荐清华大学的镜像:https://mirrors.tuna.tsinghua.edu.cn/h ...

  5. 什么是位、字节、字、KB、MB (转)

    回顾一下按位操作符和移位操作符的知识,顺便复习一下位相关的基础知识. 位:"位(bit)"是电子计算机中最小的数据单位.每一位的状态只能是0或1. 字节:8个二进制位构成1个&qu ...

  6. 【webssh】网页上的SSH终端

    [webssh] ——记两天来比较痛苦的历程 广义上来说,webssh泛指一种技术可以在网页上实现一个SSH终端.从而无需Xshell之类的模拟终端工具进行SSH连接,将SSH这一比较低层的操作也从C ...

  7. Java知多少(98)Graphics类的绘图方法

    Graphics类提供基本绘图方法,Graphics2D类提供更强大的绘图能力.本节讲解Graphics类,下节讲解Graphics2D. Graphics类提供基本的几何图形绘制方法,主要有:画线段 ...

  8. pca , nmds , pcoa 图添加分组的椭圆

    对于pca , nmds, pcoa 这些排序分析来说,我们可以从图中看出样本的排列规则,比如分成了几组. 为例样本分组更加的直观,我们可以根据实验设计时的样本分组情况,对属于同一个group的样本添 ...

  9. 使用grep查找字符串

    如下: grep -r 'target string' --exclude='pattern' dir/ 例子: grep -r Debug --exclude='*.js' ./ 查找本目录下除了j ...

  10. 详解 Redis 应用场景及应用实例

    redis(二)高级用法 详解 Redis 应用场景及应用实例 Redis 它是什么?它用来做什么?它的优势与短板如何? 告诉你Redis是一个牛逼货