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. java8学习的一点总结

    最近研究了一下java8 弄了几个例子学习了一下用法: 创建了一个实体类: @Data public class Apple { private Integer id; private String ...

  2. 《软件测试自动化之道》读书笔记 之 基于反射的UI测试

    <软件测试自动化之道>读书笔记 之 基于反射的UI测试 2014-09-24 测试自动化程序的任务待测程序测试程序  启动待测程序  设置窗体的属性  获取窗体的属性  设置控件的属性  ...

  3. shell生成连续天数日期

    #!/bin/bash #两个参数:起始时间和终止时间,循环输出每天 #输入格式:20171201 20171225 #输出格式:2017-12-01 2017-12-25 startdate=`da ...

  4. java框架篇---hibernate(多对多)映射关系

    以学生和老师为例的来讲解多对多映射. 实体类: Student package cn.itcast.g_hbm_manyToMany; import java.util.HashSet; import ...

  5. django template if return false

    如果if的参数不存在于context中就会返回false 参考:http://stackoverflow.com/questions/11107028/django-template-if-true- ...

  6. git命令之git clone用法

    在使用git来进行版本控制时,为了得一个项目的拷贝(copy),我们需要知道这个项目仓库的地址(Git URL). Git能在许多协议下使用,所以Git URL可能以ssh://, http(s):/ ...

  7. React Native常用组件之ScrollView

    1. 两个要点 1.1 ScrollView必须有一个确定的高度才能正常工作 它实际上所做的就是将一系列不确定高度的子组件装进一个确定高度的容器(通过滚动操作) 通常有两种做法: 第一种: 直接给该S ...

  8. 【转】在Win7的IIS上搭建FTP服务及用户授权

    [转]在Win7的IIS上搭建FTP服务及用户授权 [转]在Win7的IIS上搭建FTP服务及用户授权 FTP服务 FTP是文件传输协议(File Transfer Protocol)的简称,该协议属 ...

  9. Foxpro数据库连接错误解决方法--【VFP DBF文件不是一个有效的路径。 确定路径名称拼写是否正确,以及是否连接到文件存放的服务器】

    直接访问vfp dbf文件时报错: 错误描述: 'd:\vfpData\test.dbf'不是一个有效的路径. 确定路径名称拼写是否正确,以及是否连接到文件存放的服务器. 解决办法:Data Sour ...

  10. 第四百零一节,Django+Xadmin打造上线标准的在线教育平台—生产环境部署virtualenv虚拟环境安装,与Python虚拟环境批量安装模块

    第四百零一节,Django+Xadmin打造上线标准的在线教育平台—生产环境部署virtualenv虚拟环境安装,与Python虚拟环境批量安装模块 virtualenv简介 1.安装virtuale ...