public class CopycatServer extends Object
To create a new server, use the server CopycatServer.Builder. Servers require
cluster membership information in order to perform communication. Each server must be provided a local Address
to which to bind the internal Server and a set of addresses for other members in
the cluster.
StateMachine. The state machine is responsible for maintaining the state with
relation to Commands and Querys submitted to the server by a client. State machines are provided
in a factory to allow servers to transition between stateful and stateless states.
Address address = new Address("123.456.789.0", 5000);
Collection<Address> members = Arrays.asList(new Address("123.456.789.1", 5000), new Address("123.456.789.2", 5000));
CopycatServer server = CopycatServer.builder(address)
.withStateMachine(MyStateMachine::new)
.build();
Server state machines are responsible for registering Commands which can be submitted to the cluster. Raft
relies upon determinism to ensure consistency throughout the cluster, so it is imperative that each server in
a cluster have the same state machine with the same commands. State machines are provided to the server as
a factory to allow servers to transition between stateful
and stateless states.
NettyTransport for communication. You can configure the transport via
CopycatServer.Builder.withTransport(Transport). To use the Netty transport, ensure you have the
io.atomix.catalyst:catalyst-netty jar on your classpath.
CopycatServer server = CopycatServer.builder(address)
.withStateMachine(MyStateMachine::new)
.withTransport(NettyTransport.builder()
.withThreads(4)
.build())
.build();
Commands are received by the server, they're written to the Raft Log and replicated to other members
of the cluster. By default, the log is stored on disk, but users can override the default Storage configuration
via CopycatServer.Builder.withStorage(Storage). Most notably, to configure the storage module to store entries in
memory instead of disk, configure the StorageLevel.
CopycatServer server = CopycatServer.builder(address)
.withStateMachine(MyStateMachine::new)
.withStorage(Storage.builder()
.withDirectory(new File("logs"))
.withStorageLevel(StorageLevel.DISK)
.build())
.build();
Servers use the Storage object to manage the storage of cluster configurations, voting information, and
state machine snapshots in addition to logs. See the Storage documentation for more information.
Serializer. The serializer is shared across all components of
the server. Users are responsible for ensuring that commands and queries submitted to the
cluster can be serialized by the server serializer by registering serializable types as necessary.
By default, the server serializer does not allow arbitrary classes to be serialized due to security concerns. However,
users can enable arbitrary class serialization by disabling the whitelisting feature
on the Catalyst Serializer:
server.serializer().disableWhitelist();
However, for more efficient serialization, users should explicitly register serializable classes and binary
serializers. Explicit registration of serializable typs allows
types to be serialized using more compact 8- 16- 24- and 32-bit serialization IDs rather than serializing complete
class names. Thus, serializable type registration is strongly recommended for production systems.
server.serializer().register(MySerializable.class, 123, MySerializableSerializer.class);
bootstrapped to form a new cluster or
joined to an existing cluster. The simplest way to bootstrap a new cluster is to bootstrap
a single server to which additional servers can be joined.
CompletableFuture<CopycatServer> future = server.bootstrap();
future.thenRun(() -> {
System.out.println("Server bootstrapped!");
});
Alternatively, the bootstrapped cluster can include multiple servers by providing an initial configuration to the
bootstrap(Address...) method on each server. When bootstrapping a multi-node cluster, the bootstrap configuration
must be identical on all servers for safety.
List<Address> cluster = Arrays.asList(
new Address("123.456.789.0", 5000),
new Address("123.456.789.1", 5000),
new Address("123.456.789.2", 5000)
);
CompletableFuture<CopycatServer> future = server.bootstrap(cluster);
future.thenRun(() -> {
System.out.println("Cluster bootstrapped");
});
bootstrapped, often times users need to
add additional servers to the cluster. For example, some users prefer to bootstrap a single-node cluster and
add additional nodes to that server. Servers can join existing bootstrapped clusters using the join(Address...)
method. When joining an existing cluster, the server simply needs to specify at least one reachable server in the
existing cluster.
CopycatServer server = CopycatServer.builder(new Address("123.456.789.3", 5000))
.withTransport(NettyTransport.builder().withThreads(4).build())
.build();
List<Address> cluster = Arrays.asList(
new Address("123.456.789.0", 5000),
new Address("123.456.789.1", 5000),
new Address("123.456.789.2", 5000)
);
CompletableFuture<CopycatServer> future = server.join(cluster);
future.thenRun(() -> {
System.out.println("Server joined successfully!");
});
Member.Type. For example, the PASSIVE server type does not participate
directly in the Raft consensus algorithm and instead receives state changes via an asynchronous gossip protocol.
This allows passive members to scale sequential reads beyond the typical three- or five-node Raft cluster. The
RESERVE server type is a stateless server that can act as a standby to the stateful
servers in the cluster, being promoted to a stateful state when necessary.
Server types are defined in the server builder simply by passing the initial Member.Type to the
CopycatServer.Builder.withType(Member.Type) setter:
CopycatServer server = CopycatServer.builder(address)
.withType(Member.Type.PASSIVE)
.withTransport(new NettyTransport())
.build();
StateMachine,
Transport,
Storage| Modifier and Type | Class and Description |
|---|---|
static class |
CopycatServer.Builder
Builds a single-use Copycat server.
|
static class |
CopycatServer.State
Copycat server state types.
|
| Modifier and Type | Method and Description |
|---|---|
CompletableFuture<CopycatServer> |
bootstrap()
Bootstraps a single-node cluster.
|
CompletableFuture<CopycatServer> |
bootstrap(Address... cluster)
Bootstraps the cluster using the provided cluster configuration.
|
CompletableFuture<CopycatServer> |
bootstrap(Collection<Address> cluster)
Bootstraps the cluster using the provided cluster configuration.
|
static CopycatServer.Builder |
builder()
Returns a new Copycat server builder using the default host:port.
|
static CopycatServer.Builder |
builder(Address address)
Returns a new Copycat server builder.
|
static CopycatServer.Builder |
builder(Address clientAddress,
Address serverAddress)
Returns a new Copycat server builder.
|
Cluster |
cluster()
Returns the server's cluster configuration.
|
ThreadContext |
context()
Returns the server execution context.
|
boolean |
isRunning()
Returns a boolean indicating whether the server is running.
|
CompletableFuture<CopycatServer> |
join(Address... cluster)
Joins the cluster.
|
CompletableFuture<CopycatServer> |
join(Collection<Address> cluster)
Joins the cluster.
|
CompletableFuture<Void> |
leave()
Leaves the Copycat cluster.
|
String |
name()
Returns the server name.
|
Listener<CopycatServer.State> |
onStateChange(Consumer<CopycatServer.State> listener)
Registers a state change listener.
|
Serializer |
serializer()
Returns the server's binary serializer which is shared among the protocol, state machine, and storage.
|
CompletableFuture<Void> |
shutdown()
Shuts down the server without leaving the Copycat cluster.
|
CopycatServer.State |
state()
Returns the Copycat server state.
|
Storage |
storage()
Returns the server storage.
|
public static CopycatServer.Builder builder()
The server will be constructed at 0.0.0.0:8700.
public static CopycatServer.Builder builder(Address address)
The provided Address is the address to which to bind the server being constructed.
address - The address through which clients and servers connect to this server.public static CopycatServer.Builder builder(Address clientAddress, Address serverAddress)
The provided Addresses are the client and server address to which to bind the server being
constructed respectively.
clientAddress - The address through which clients connect to the server.serverAddress - The local server member address.public String name()
The server name is provided to the server via the builder configuration.
The name is used internally to manage the server's on-disk state. Log,
snapshot,
and configuration files stored on disk use
the server name as the prefix.
public Storage storage()
The returned Storage object is the object provided to the server via the builder
configuration. The storage object is immutable and is intended to provide runtime configuration information only. Users
should never open logs, snapshots, or other storage related files through the Storage API. Doing so
can conflict with internal server operations, resulting in the loss of state.
public Cluster cluster()
The Cluster is representative of the server's current view of the cluster configuration. The first time
the server is started, the cluster configuration will be initialized using the Address
list provided to the server builder. For persistent
servers, subsequent starts will result in the last known cluster configuration being loaded from disk.
The returned Cluster can be used to modify the state of the cluster to which this server belongs. Note,
however, that users need not explicitly join or leave the
cluster since starting and stopping the server results in joining and leaving the cluster respectively.
public Serializer serializer()
The returned serializer is linked to all serialization performed within the Copycat server. Serializable types
and associated serializers that are
registered on the returned serializer will be reflected in serialization
throughout the server, including in the state machine and logs.
By default, serializers support serialization of arbitrary classes and class names. However, this default
serialization is significantly slower than explicitly registering serializable types since. Users should
explicitly whitelist serializable types with the serializer to ensure the server can more efficiently serialize
and deserialize required objects, including state machine commands and queries and
any objects that need to be serialized within them.
server.serializer().register(MySerializableType.class, 123, MySerializableSerializer.class);
Alternatively, users can enabled whitelisting in the Catalyst serializer
to force serializable types to be explicitly registered.
server.serializer().enableWhitelist();
Whitelisting of serializable classes is recommended in insecure environments. The default disabled whitelisting
presents security risks wherein arbitrary classes can be deserialized by malicious actors. See the Serializer
documentation for more information.
It's important to note that because the returned Serializer affects the on-disk binary representation
of commands and queries and other objects submitted to the cluster, users must take care to avoid directly changing
the binary format of serializable objects once they have been written to the server's logs. Doing so may result in
errors during deserialization of objects in the log. To guard against this, serializable types can implement versioning
by writing 8-bit version numbers as part of their normal serialization.
public CopycatServer.State state()
The initial state of a Raft server is CopycatServer.State.INACTIVE. Once the server is started and
until it is explicitly shutdown, the server will be in one of the active states - CopycatServer.State.PASSIVE,
CopycatServer.State.FOLLOWER, CopycatServer.State.CANDIDATE, or CopycatServer.State.LEADER.
public Listener<CopycatServer.State> onStateChange(Consumer<CopycatServer.State> listener)
Throughout the lifetime of the cluster, the server will periodically transition between various states.
Users can listen for and react to state change events. To determine when this server is elected leader, simply
listen for the CopycatServer.State.LEADER state.
server.onStateChange(state -> {
if (state == CopycatServer.State.LEADER) {
System.out.println("Server elected leader!");
}
});
listener - The state change listener.Listener.close().NullPointerException - If listener is nullpublic ThreadContext context()
The thread context is the event loop that this server uses to communicate other Raft servers.
Implementations must guarantee that all asynchronous CompletableFuture callbacks are
executed on a single thread via the returned concurrent.ThreadContext.
The concurrent.ThreadContext can also be used to access the Raft server's internal
serializer via ThreadContext.serializer(). Catalyst serializers
are not thread safe, so to use the context serializer, users should clone it:
Serializer serializer = server.threadContext().serializer().clone();
Buffer buffer = serializer.writeObject(myObject).flip();
public CompletableFuture<CopycatServer> bootstrap()
Bootstrapping a single-node cluster results in the server forming a new cluster to which additional servers can be joined.
Only Member.Type#ACTIVE members can be included in a bootstrap configuration. If the local server is
not initialized as an active member, it cannot be part of the bootstrap configuration for the cluster.
When the cluster is bootstrapped, the local server will be transitioned into the active state and begin
participating in the Raft consensus algorithm. When the cluster is first bootstrapped, no leader will exist.
The bootstrapped members will elect a leader amongst themselves. Once a cluster has been bootstrapped, additional
members may be joined to the cluster. In the event that the bootstrapped members cannot
reach a quorum to elect a leader, bootstrap will continue until successful.
It is critical that all servers in a bootstrap configuration be started with the same exact set of members. Bootstrapping multiple servers with different configurations may result in split brain.
The CompletableFuture returned by this method will be completed once the cluster has been bootstrapped,
a leader has been elected, and the leader has been notified of the local server's client configurations.
public CompletableFuture<CopycatServer> bootstrap(Address... cluster)
Bootstrapping the cluster results in a new cluster being formed with the provided configuration. The initial nodes in a cluster must always be bootstrapped. This is necessary to prevent split brain. If the provided configuration is empty, the local server will form a single-node cluster.
Only Member.Type#ACTIVE members can be included in a bootstrap configuration. If the local server is
not initialized as an active member, it cannot be part of the bootstrap configuration for the cluster.
When the cluster is bootstrapped, the local server will be transitioned into the active state and begin
participating in the Raft consensus algorithm. When the cluster is first bootstrapped, no leader will exist.
The bootstrapped members will elect a leader amongst themselves. Once a cluster has been bootstrapped, additional
members may be joined to the cluster. In the event that the bootstrapped members cannot
reach a quorum to elect a leader, bootstrap will continue until successful.
It is critical that all servers in a bootstrap configuration be started with the same exact set of members. Bootstrapping multiple servers with different configurations may result in split brain.
The CompletableFuture returned by this method will be completed once the cluster has been bootstrapped,
a leader has been elected, and the leader has been notified of the local server's client configurations.
cluster - The bootstrap cluster configuration.public CompletableFuture<CopycatServer> bootstrap(Collection<Address> cluster)
Bootstrapping the cluster results in a new cluster being formed with the provided configuration. The initial nodes in a cluster must always be bootstrapped. This is necessary to prevent split brain. If the provided configuration is empty, the local server will form a single-node cluster.
Only Member.Type#ACTIVE members can be included in a bootstrap configuration. If the local server is
not initialized as an active member, it cannot be part of the bootstrap configuration for the cluster.
When the cluster is bootstrapped, the local server will be transitioned into the active state and begin
participating in the Raft consensus algorithm. When the cluster is first bootstrapped, no leader will exist.
The bootstrapped members will elect a leader amongst themselves. Once a cluster has been bootstrapped, additional
members may be joined to the cluster. In the event that the bootstrapped members cannot
reach a quorum to elect a leader, bootstrap will continue until successful.
It is critical that all servers in a bootstrap configuration be started with the same exact set of members. Bootstrapping multiple servers with different configurations may result in split brain.
The CompletableFuture returned by this method will be completed once the cluster has been bootstrapped,
a leader has been elected, and the leader has been notified of the local server's client configurations.
cluster - The bootstrap cluster configuration.public CompletableFuture<CopycatServer> join(Address... cluster)
Joining the cluster results in the local server being added to an existing cluster that has already been bootstrapped. The provided configuration will be used to connect to the existing cluster and submit a join request. Once the server has been added to the existing cluster's configuration, the join operation is complete.
Any type of server may join a cluster. In order to join a cluster, the provided list of
bootstrapped members must be non-empty and must include at least one active member of the cluster. If no member
in the configuration is reachable, the server will continue to attempt to join the cluster until successful. If
the provided cluster configuration is empty, the returned CompletableFuture will be completed exceptionally.
When the server joins the cluster, the local server will be transitioned into its initial state as defined by
the configured Member.Type. Once the server has joined, it will immediately begin participating in
Raft and asynchronous replication according to its configuration.
It's important to note that the provided cluster configuration will only be used the first time the server attempts
to join the cluster. Thereafter, in the event that the server crashes and is restarted by joining the cluster
again, the last known configuration will be used assuming the server is configured with persistent storage. Only when
the server leaves the cluster will its configuration and log be reset.
In order to preserve safety during configuration changes, Copycat leaders do not allow concurrent configuration
changes. In the event that an existing configuration change (a server joining or leaving the cluster or a
member being promoted or demoted) is under way, the local
server will retry attempts to join the cluster until successful. If the server fails to reach the leader,
the join will be retried until successful.
cluster - A collection of cluster member addresses to join.public CompletableFuture<CopycatServer> join(Collection<Address> cluster)
Joining the cluster results in the local server being added to an existing cluster that has already been bootstrapped. The provided configuration will be used to connect to the existing cluster and submit a join request. Once the server has been added to the existing cluster's configuration, the join operation is complete.
Any type of server may join a cluster. In order to join a cluster, the provided list of
bootstrapped members must be non-empty and must include at least one active member of the cluster. If no member
in the configuration is reachable, the server will continue to attempt to join the cluster until successful. If
the provided cluster configuration is empty, the returned CompletableFuture will be completed exceptionally.
When the server joins the cluster, the local server will be transitioned into its initial state as defined by
the configured Member.Type. Once the server has joined, it will immediately begin participating in
Raft and asynchronous replication according to its configuration.
It's important to note that the provided cluster configuration will only be used the first time the server attempts
to join the cluster. Thereafter, in the event that the server crashes and is restarted by joining the cluster
again, the last known configuration will be used assuming the server is configured with persistent storage. Only when
the server leaves the cluster will its configuration and log be reset.
In order to preserve safety during configuration changes, Copycat leaders do not allow concurrent configuration
changes. In the event that an existing configuration change (a server joining or leaving the cluster or a
member being promoted or demoted) is under way, the local
server will retry attempts to join the cluster until successful. If the server fails to reach the leader,
the join will be retried until successful.
cluster - A collection of cluster member addresses to join.public boolean isRunning()
public CompletableFuture<Void> shutdown()
public CompletableFuture<Void> leave()
Copyright © 2013–2016. All rights reserved.