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. 【转】Centos7安装nodejs

    下载及安装步骤: cd /usr/local wget https://nodejs.org/dist/v8.11.2/node-v8.11.2-linux-x64.tar.xz tar xvf no ...

  2. 【九天教您南方cass 9.1】 13 等高线法计算土方量

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

  3. Java知多少(12)运算符

    Java中的运算符和C/C++相差无几. 数学运算符 数学运算,结果为一个数值.见下表: 运算符 说明 举例 + 加法 1 + 2 - 减法 4 - 3.4 * 乘法 7 * 1.5 / 除法 3.5 ...

  4. 史上最详细的JavaScript事件使用指南

    事件流 事件流描述的是从页面中接收事件的顺序,IE和Netscape提出来差不多完全相反的事件流的概念,IE事件流是事件冒泡流,Netscape事件流是事件捕获流. 事件冒泡 IE的事件流叫做事件冒泡 ...

  5. PyCharm设置改变字体大小的快捷键

    File->Settings 在搜索框搜索increase 点击Increase Font Size(增大字体)右键选择Add Mouse Shortcut 然后按Ctrl并且鼠标滚轮往上滚. ...

  6. AngularJS中$timeout和$interval的用法详解

    1. 先将$interval,$timeout,作为参数注入到controller中,例如rds.controller('controllerCtrl', ['app', '$scope','$htt ...

  7. 网络编程 -- RPC实现原理 -- RPC -- 迭代版本V2 -- 本地方法调用 整合 Spring

    网络编程 -- RPC实现原理 -- 目录 啦啦啦 V2——RPC -- 本地方法调用 + Spring 1. 配置applicationContext.xml文件 注入 bean 及 管理 bean ...

  8. java.security.ProviderException: java.security.KeyException

    本机部署没问题,部署到linux服务器报错: javax.net.ssl.SSLException: java.security.ProviderException: java.security.Ke ...

  9. 关于Kafka high watermark的讨论2

    之前写过一篇关于Kafka High watermark的文章,引起的讨论不少:有赞扬之声,但更多的是针对文中的内容被challenge,于是下定决心找个晚上熬夜再看了一遍,昨晚挑灯通读了一遍确实发现 ...

  10. Kotlin入门第一课:从对比Java开始

    1. 介绍 今年初,甲骨文再次对谷歌所谓的安卓侵权使用Java提起诉讼,要求后者赔偿高达90亿美元.随后便传出谷歌因此计划将主力语言切换到苹果主导的Swift,不过这事后来没了跟进. 但谷歌在这两天的 ...