Server被拉起有两种方式,

Address address = new Address("123.456.789.0", 5000);
CopycatServer.Builder builder = CopycatServer.builder(address);
builder.withStateMachine(MapStateMachine::new);

自己拉起一个cluster,

CompletableFuture<CopycatServer> future = server.bootstrap();
future.join();

 

join到一个现有的cluster,

Collection<Address> cluster = Collections.singleton(new Address("127.0.0.1", 8700))
server.join(cluster).join();

 

CopycatServer.builder.build

/**
* @throws ConfigurationException if a state machine, members or transport are not configured
*/
@Override
public CopycatServer build() {
if (stateMachineFactory == null)
throw new ConfigurationException("state machine not configured"); // If the transport is not configured, attempt to use the default Netty transport.
if (serverTransport == null) {
try {
serverTransport = (Transport) Class.forName("io.atomix.catalyst.transport.netty.NettyTransport").newInstance(); //默认netty
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
throw new ConfigurationException("transport not configured");
}
} // If the client transport is not configured, default it to the server transport.
if (clientTransport == null) {
clientTransport = serverTransport;
} // If no serializer instance was provided, create one.
if (serializer == null) {
serializer = new Serializer(new PooledHeapAllocator());
} // Resolve serializable request/response and other types.
serializer.resolve(new ClientRequestTypeResolver());
serializer.resolve(new ClientResponseTypeResolver());
serializer.resolve(new ProtocolSerialization());
serializer.resolve(new ServerSerialization());
serializer.resolve(new StorageSerialization()); // If the storage is not configured, create a new Storage instance with the configured serializer.
if (storage == null) {
storage = new Storage(); //storage
} ConnectionManager connections = new ConnectionManager(serverTransport.client());
ThreadContext threadContext = new SingleThreadContext(String.format("copycat-server-%s-%s", serverAddress, name), serializer); //单线程的TreadContext,对thread的简单封装,用于执行对statemachine的操作 ServerContext context = new ServerContext(name, type, serverAddress, clientAddress, storage, serializer, stateMachineFactory, connections, threadContext); //封装成ServerContext
context.setElectionTimeout(electionTimeout)
.setHeartbeatInterval(heartbeatInterval)
.setSessionTimeout(sessionTimeout)
.setGlobalSuspendTimeout(globalSuspendTimeout); return new CopycatServer(name, clientTransport, serverTransport, context);
}

 

CopycatServer.bootstrap

public CompletableFuture<CopycatServer> bootstrap() {
return bootstrap(Collections.EMPTY_LIST); //仅仅拉起自己,所以参数是empty list
}

 

public CompletableFuture<CopycatServer> bootstrap(Collection<Address> cluster) {
return start(() -> cluster().bootstrap(cluster));
}
 
调用start,
/**
* Starts the server.
*/
private CompletableFuture<CopycatServer> start(Supplier<CompletableFuture<Void>> joiner) {
if (started)
return CompletableFuture.completedFuture(this); if (openFuture == null) {
synchronized (this) {
if (openFuture == null) {
Function<Void, CompletionStage<CopycatServer>> completionFunction = state -> {
CompletableFuture<CopycatServer> future = new CompletableFuture<>();
openFuture = null;
joiner.get().whenComplete((result, error) -> { //处理joiner
if (error == null) {
if (cluster().leader() != null) {
started = true;
future.complete(this);
} else {
electionListener = cluster().onLeaderElection(leader -> {
if (electionListener != null) {
started = true;
future.complete(this);
electionListener.close();
electionListener = null;
}
});
}
} else {
future.completeExceptionally(error);
}
});
return future;
}; if (closeFuture == null) {
openFuture = listen().thenCompose(completionFunction); //listen
} else {
openFuture = closeFuture.thenCompose(c -> listen().thenCompose(completionFunction));
}
}
}
}

start主要做两件事,执行joiner和listen

joiner这里是cluster().bootstrap(cluster)

@Override
public CompletableFuture<Void> bootstrap(Collection<Address> cluster) {
if (joinFuture != null)
return joinFuture; if (configuration == null) {
if (member.type() != Member.Type.ACTIVE) {
return Futures.exceptionalFuture(new IllegalStateException("only ACTIVE members can bootstrap the cluster"));
} else {
// Create a set of active members.
Set<Member> activeMembers = cluster.stream()
.filter(m -> !m.equals(member.serverAddress()))
.map(m -> new ServerMember(Member.Type.ACTIVE, m, null, member.updated()))
.collect(Collectors.toSet()); // Add the local member to the set of active members.
activeMembers.add(member); // Create a new configuration and store it on disk to ensure the cluster can fall back to the configuration.
configure(new Configuration(0, 0, member.updated().toEpochMilli(), activeMembers));
}
}
return join();
}

 

listen

/**
* Starts listening the server.
*/
private CompletableFuture<Void> listen() {
CompletableFuture<Void> future = new CompletableFuture<>();
context.getThreadContext().executor().execute(() -> {
internalServer.listen(cluster().member().serverAddress(), context::connectServer).whenComplete((internalResult, internalError) -> { //internalServer可能是local或是netty
if (internalError == null) {
// If the client address is different than the server address, start a separate client server.
if (clientServer != null) {
clientServer.listen(cluster().member().clientAddress(), context::connectClient).whenComplete((clientResult, clientError) -> { //和client沟通可能是不同的地址
started = true;
future.complete(null);
});
} else {
started = true;
future.complete(null);
}
} else {
future.completeExceptionally(internalError);
}
});
}); return future;
}

ServerContext

/**
* Handles a connection from a client.
*/
public void connectClient(Connection connection) {
threadContext.checkThread(); // Note we do not use method references here because the "state" variable changes over time.
// We have to use lambdas to ensure the request handler points to the current state.
connection.handler(RegisterRequest.class, request -> state.register(request));
connection.handler(ConnectRequest.class, request -> state.connect(request, connection));
connection.handler(KeepAliveRequest.class, request -> state.keepAlive(request));
connection.handler(UnregisterRequest.class, request -> state.unregister(request));
connection.handler(CommandRequest.class, request -> state.command(request));
connection.handler(QueryRequest.class, request -> state.query(request)); connection.closeListener(stateMachine.executor().context().sessions()::unregisterConnection);
} /**
* Handles a connection from another server.
*/
public void connectServer(Connection connection) {
threadContext.checkThread(); // Handlers for all request types are registered since requests can be proxied between servers.
// Note we do not use method references here because the "state" variable changes over time.
// We have to use lambdas to ensure the request handler points to the current state.
connection.handler(RegisterRequest.class, request -> state.register(request));
connection.handler(ConnectRequest.class, request -> state.connect(request, connection));
connection.handler(KeepAliveRequest.class, request -> state.keepAlive(request));
connection.handler(UnregisterRequest.class, request -> state.unregister(request));
connection.handler(PublishRequest.class, request -> state.publish(request));
connection.handler(ConfigureRequest.class, request -> state.configure(request));
connection.handler(InstallRequest.class, request -> state.install(request));
connection.handler(JoinRequest.class, request -> state.join(request));
connection.handler(ReconfigureRequest.class, request -> state.reconfigure(request));
connection.handler(LeaveRequest.class, request -> state.leave(request));
connection.handler(AppendRequest.class, request -> state.append(request));
connection.handler(PollRequest.class, request -> state.poll(request));
connection.handler(VoteRequest.class, request -> state.vote(request));
connection.handler(CommandRequest.class, request -> state.command(request));
connection.handler(QueryRequest.class, request -> state.query(request)); connection.closeListener(stateMachine.executor().context().sessions()::unregisterConnection);
}

 

加入一个cluster

public CompletableFuture<CopycatServer> join(Collection<Address> cluster) {
return start(() -> cluster().join(cluster));
}

ClusterState.join,这里的逻辑和bootstrap类似

@Override
public synchronized CompletableFuture<Void> join(Collection<Address> cluster) { // If no configuration was loaded from disk, create a new configuration.
if (configuration == null) { //当不存在configuration
// Create a set of cluster members, excluding the local member which is joining a cluster.
Set<Member> activeMembers = cluster.stream()
.filter(m -> !m.equals(member.serverAddress())) //过滤掉自己
.map(m -> new ServerMember(Member.Type.ACTIVE, m, null, member.updated())) //创建ServerMember对象
.collect(Collectors.toSet()); // If the set of members in the cluster is empty when the local member is excluded,
// fail the join.
if (activeMembers.isEmpty()) { //如果cluster为空
return Futures.exceptionalFuture(new IllegalStateException("cannot join empty cluster"));
} // Create a new configuration and configure the cluster. Once the cluster is configured, the configuration
// will be stored on disk to ensure the cluster can fall back to the provided configuration if necessary.
configure(new Configuration(0, 0, member.updated().toEpochMilli(), activeMembers)); //让configuration生效
}
return join();
}

只是需要初始化configuration

然后调用join

/**
* Starts the join to the cluster.
*/
private synchronized CompletableFuture<Void> join() {
joinFuture = new CompletableFuture<>(); context.getThreadContext().executor().execute(() -> {
// Transition the server to the appropriate state for the local member type.
context.transition(member.type()); //将当前member transitioin到指定type // Attempt to join the cluster. If the local member is ACTIVE then failing to join the cluster
// will result in the member attempting to get elected. This allows initial clusters to form.
List<MemberState> activeMembers = getActiveMemberStates();
if (!activeMembers.isEmpty()) {
join(getActiveMemberStates().iterator()); //join 其他active members
} else {
joinFuture.complete(null);
}
}); return joinFuture.whenComplete((result, error) -> joinFuture = null);
}

 

/**
* Recursively attempts to join the cluster.
*/
private void join(Iterator<MemberState> iterator) {
if (iterator.hasNext()) {
cancelJoinTimer();
joinTimeout = context.getThreadContext().schedule(context.getElectionTimeout().multipliedBy(2), () -> {
join(iterator); //只要不成功,就会一直递归schedule join
}); MemberState member = iterator.next();
LOGGER.debug("{} - Attempting to join via {}", member().address(), member.getMember().serverAddress()); context.getConnections().getConnection(member.getMember().serverAddress()).thenCompose(connection -> {
JoinRequest request = JoinRequest.builder()
.withMember(new ServerMember(member().type(), member().serverAddress(), member().clientAddress(), member().updated()))
.build();
return connection.<JoinRequest, JoinResponse>send(request); //发送join request
}).whenComplete((response, error) -> {
// Cancel the join timer.
cancelJoinTimer(); //先cancel join timer if (error == null) { //join 成功
if (response.status() == Response.Status.OK) {
LOGGER.info("{} - Successfully joined via {}", member().address(), member.getMember().serverAddress()); Configuration configuration = new Configuration(response.index(), response.term(), response.timestamp(), response.members()); // Configure the cluster with the join response.
// Commit the configuration as we know it was committed via the successful join response.
configure(configuration).commit(); //更新配置 } else if (response.error() == null || response.error() == CopycatError.Type.CONFIGURATION_ERROR) {
// If the response error is null, that indicates that no error occurred but the leader was
// in a state that was incapable of handling the join request. Attempt to join the leader
// again after an election timeout.
LOGGER.debug("{} - Failed to join {}", member().address(), member.getMember().address());
resetJoinTimer();
} else {
// If the response error was non-null, attempt to join via the next server in the members list.
LOGGER.debug("{} - Failed to join {}", member().address(), member.getMember().address());
join(iterator);
}
} else {
LOGGER.debug("{} - Failed to join {}", member().address(), member.getMember().address());
join(iterator);
}
});
}
// If join attempts remain, schedule another attempt after two election timeouts. This allows enough time
// for servers to potentially timeout and elect a leader.
else {
LOGGER.debug("{} - Failed to join cluster, retrying...", member.address());
resetJoinTimer(); //如果遍历完还不成功,reset
}
}

对任何一个member,join成功,即可,因为join request无论发给谁,都会forward给leader

Copycat - CopycatServer的更多相关文章

  1. Copycat - StateMachine

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

  2. Copycat - Overview

    Copycat’s primary role is as a framework for building highly consistent, fault-tolerant replicated s ...

  3. Copycat - MemberShip

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

  4. loj#3 -Copycat

    原题链接:https://loj.ac/problem/3 题目描述: --- Copycat 内存限制:256 MiB 时间限制:1000 ms 输入文件: copycat.in 输出文件: cop ...

  5. Copycat - configure

    Copycat server之间的configure是如何,何时被同步的?   大家可以看到,只有leader可以同步配置   1. 显式的调用LeaderState.configure Leader ...

  6. Copycat - AppendRequest

    对于Command,Configuration都要通过appendEntries的方式,把Entries同步给follower LeaderState.configure /** * Commits ...

  7. Copycat - 状态

    Member.Status status的变迁是源于heartbeat heartbeat,append空的entries /** * Triggers a heartbeat to a majori ...

  8. Copycat - command

    client.submit(new PutCommand("foo", "Hello world!")); ServerContext connection.h ...

  9. Java资源大全中文版(Awesome最新版)

    Awesome系列的Java资源整理.awesome-java 就是akullpp发起维护的Java资源列表,内容包括:构建工具.数据库.框架.模板.安全.代码分析.日志.第三方库.书籍.Java 站 ...

随机推荐

  1. 菜鸟教程之工具使用(五)——JRebel与Windows服务的Tomcat集成

    之前写过一篇Tomcat借助JRebel支持热部署的文章——<借助JRebel使Tomcat支持热部署>.介绍的是在开发.测试环境中的配置,但是正式的部署环境,我们不会通过命令行来启动To ...

  2. pandas DataFrame(2)-行列索引及值的获取

    pandas DataFrame是二维的,所以,它既有列索引,又有行索引 上一篇里只介绍了列索引: import pandas as pd df = pd.DataFrame({'A': [0, 1, ...

  3. Python多线程与多线程中join()的用法

    多线程实例 https://www.cnblogs.com/cnkai/p/7504980.html 知识点一:当一个进程启动之后,会默认产生一个主线程,因为线程是程序执行流的最小单元,当设置多线程时 ...

  4. Node入门教程(9)第七章:NodeJs的文件处理

    Node的文件处理涉及到前面说的ptah模块,以及fs文件系统.stream流处理.Buffer缓冲器等模块.内容可能比较多,相关内容请以官网文档为主,此处主要以案例讲解为主,分享给大家一些常用的经典 ...

  5. 【iCore4 双核心板_ARM】例程六:IWDG看门狗实验——复位ARM

    实验原理: STM32内部包含独立看门狗,通过看门狗可以监控程序远行,程序运行错误时, 未在规定时间内喂狗,自动复位ARM.本实验通过按键按下,停止喂狗,制造程序运行 错误,从而产生复位. 核心代码: ...

  6. Java编程的逻辑 (86) - 动态代理

    ​本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http: ...

  7. Java知多少(43)异常处理基础

    Java异常是一个描述在代码段中发生的异常(也就是出错)情况的对象.当异常情况发生,一个代表该异常的对象被创建并且在导致该错误的方法中被抛出(throw).该方法可以选择自己处理异常或传递该异常.两种 ...

  8. 将Java web应用部署到Tomcat 及部署到Tomcat根目录 的三种方式

    Tomcat作为Servlet/JSP容器(服务器)挺不错的,开源免费,需要知道的是Tomcat是一个Web服务器,其符合Servlet/JSP规范,但是却没有实现所有JavaEE规范,所以我们还是应 ...

  9. Java8学习笔记(二)--三个预定义函数接口

    三个函数接口概述 JDK预定义了很多函数接口以避免用户重复定义.最典型的是Function: @FunctionalInterface public interface Function<T, ...

  10. 每一个开发人员都应该有一款自己的App

    [谋哥每天一干货]          这篇文章不是鸡汤,是谋哥自己的感悟了. 谋哥近期每日一干货,坚持每天写,才发现这个事情你要是能坚持一年超级难.365天无论刮风下雨.心情好或不好.生病或生气.每天 ...