Introduction to Atomix – Atomix简介

最后修改: 2017年 10月 6日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

Most distributed applications require some stateful component to be consistent and fault-tolerant. Atomix is an embeddable library helping in achieving fault-tolerance and consistency for distributed resources.

大多数分布式应用都需要一些有状态的组件来实现一致性和容错性。Atomix是一个可嵌入的库,有助于实现分布式资源的容错和一致性。

It provides a rich set of APIs for managing its resources like collections, groups, and tools for concurrency.

它提供了一套丰富的API,用于管理其资源,如集合、组和并发的工具。

To get started, we need to add the following Maven dependency into our pom:

为了开始工作,我们需要在pom中添加以下Maven依赖项。

<dependency>
    <groupId>io.atomix</groupId>
    <artifactId>atomix-all</artifactId>
    <version>1.0.8</version>
</dependency>

This dependency provides a Netty-based transport needed by its nodes to communicate with each other.

这个依赖关系提供了一个基于Netty的传输,它的节点需要彼此之间的通信。

2. Bootstrapping a Cluster

2.引导一个集群

To get started with Atomix, we need to bootstrap a cluster first.

要开始使用Atomix,我们需要先启动一个集群。

Atomix consists of a set of replicas which are used for creating stateful distributed resources. Each replica maintains a copy of the state of each resource existing in the cluster.

Atomix由一组副本组成,用于创建有状态的分布式资源。每个副本维护集群中存在的每个资源的状态副本。

Replicas are two types in a cluster: active and passive.

复制在集群中有两种类型:主动和被动。

State changes of distributed resources are propagated through active replicas while passive replicas are kept in sync to maintain fault-tolerance.

分布式资源的状态变化通过主动副本传播,而被动副本则保持同步以保持容错。

2.1. Bootstrapping an Embedded Cluster

2.1.引导一个嵌入式集群

To bootstrap a single node cluster, we need to create an instance of AtomixReplica first:

为了启动一个单节点集群,我们需要首先创建一个AtomixReplica的实例。

AtomixReplica replica = AtomixReplica.builder(
  new Address("localhost", 8700))
   .withStorage(storage)
   .withTransport(new NettyTransport())
   .build();

Here replica is configured with Storage and Transport. Code snippet to declare storage:

这里的复制被配置为StorageTransport。声明存储的代码片断。

Storage storage = Storage.builder()
  .withDirectory(new File("logs"))
  .withStorageLevel(StorageLevel.DISK)
  .build();

Once the replica is declared and configured with storage and transport, we can bootstrap it by simply calling bootstrap() – which returns a CompletableFuture that can be used to block until the server is bootstrapped by calling associated blocking join() method:

一旦副本被声明并配置了存储和传输,我们可以通过简单地调用bootstrap()来引导它–它返回一个CompletableFuture,可以用来阻塞,直到服务器通过调用相关的阻塞join()方法来引导。

CompletableFuture<AtomixReplica> future = replica.bootstrap();
future.join();

So far we’ve constructed a single node cluster. Now we can add more nodes to it.

到目前为止,我们已经构建了一个单节点集群。现在我们可以向它添加更多的节点。

To do this, we need to create other replicas and join them with the existing cluster; we need to spawn a new thread for calling the join(Address) method:

要做到这一点,我们需要创建其他的副本,并将它们与现有的集群连接起来;我们需要催生一个新的线程来调用join(Address)方法。

AtomixReplica replica2 = AtomixReplica.builder(
  new Address("localhost", 8701))
    .withStorage(storage)
    .withTransport(new NettyTransport())
    .build();
  
replica2
  .join(new Address("localhost", 8700))
  .join();

AtomixReplica replica3 = AtomixReplica.builder(
  new Address("localhost", 8702))
    .withStorage(storage)
    .withTransport(new NettyTransport())
    .build();

replica3.join(
  new Address("localhost", 8700), 
  new Address("localhost", 8701))
  .join();

Now we have a three nodes cluster bootstrapped. Alternatively, we can bootstrap a cluster by passing a List of addresses in bootstrap(List<Address>) method:

现在我们有一个三节点的集群被引导。另外,我们可以通过在bootstrap(List<Address>) 方法中传递一个List地址来引导集群。

List<Address> cluster = Arrays.asList(
  new Address("localhost", 8700), 
  new Address("localhost", 8701), 
  new Address("localhsot", 8702));

AtomixReplica replica1 = AtomixReplica
  .builder(cluster.get(0))
  .build();
replica1.bootstrap(cluster).join();

AtomixReplica replica2 = AtomixReplica
  .builder(cluster.get(1))
  .build();
            
replica2.bootstrap(cluster).join();

AtomixReplica replica3 = AtomixReplica
  .builder(cluster.get(2))
  .build();

replica3.bootstrap(cluster).join();

We need to spawn a new thread for each replica.

我们需要为每个副本生成一个新的线程。

2.2. Bootstrapping a Standalone Cluster

2.2.引导一个独立的集群

Atomix server can be run as a standalone server which can be downloaded from Maven Central. Simply put – it’s a Java archive which can be run via the terminal by providing

Atomix服务器可以作为独立的服务器运行,可以从Maven中心下载。简单地说,它是一个Java归档文件,可以通过终端运行,只要提供

Simply put – it’s a Java archive which can be run via the terminal by providing host: port parameter in the address flag and using the -bootstrap flag.

简单地说–它是一个Java档案,可以通过终端运行,在地址标志中提供host: port参数并使用-bootstrap标志。

Here’s the command to bootstrap a cluster:

下面是启动集群的命令。

java -jar atomix-standalone-server.jar 
  -address 127.0.0.1:8700 -bootstrap -config atomix.properties

Here atomix.properties is the configuration file to configure storage and transport. To make a multinode cluster, we can add nodes to the existing cluster using -join flag.

这里atomix.properties是配置存储和传输的配置文件。为了建立一个多节点集群,我们可以使用-join标志向现有集群添加节点。

The format for it is:

它的格式是。

java -jar atomix-standalone-server.jar 
  -address 127.0.0.1:8701 -join 127.0.0.1:8700

3. Working With a Client

3.与客户合作

Atomix supports creating a client to have remote access to its cluster, via the AtomixClient API.

Atomix支持创建一个客户端,通过AtomixClient API对其集群进行远程访问。

Since clients need not be stateful, AtomixClient doesn’t have any storage. We simply need to configure transport while creating client since transport will be used to communicate with a cluster.

由于客户端不需要有状态,所以AtomixClient没有任何存储。我们只需要在创建客户端时配置传输,因为传输将被用于与集群通信。

Let’s create a client with a transport:

让我们创建一个带有运输工具的客户端。

AtomixClient client = AtomixClient.builder()
  .withTransport(new NettyTransport())
  .build();

We now need to connect the client to cluster.

我们现在需要将客户端连接到集群。

We can declare a List of Address and pass the List as an argument to the connect() method of client:

我们可以声明一个ListAddress,并将List作为一个参数传递给客户端的connect() 方法。

client.connect(cluster)
  .thenRun(() -> {
      System.out.println("Client is connected to the cluster!");
  });

4. Handling Resources

4.处理资源

The true power of Atomix lies in its strong set of APIs for creating and managing distributed resources. Resources are replicated and persisted in a cluster and are bolstered by a replicated state machine – managed by its underlying implementation of the Raft Consensus Protocol.

Atomix的真正力量在于其强大的API集,用于创建和管理分布式资源。资源在集群中被复制和持久化,并由一个复制的状态机支持–由其底层的Raft共识协议的实施管理。

Distributed resources can be created and managed by one of its get() method. We can create a distributed resource instance from AtomixReplica.

分布式资源可以通过它的一个get()方法来创建和管理。我们可以从AtomixReplica创建一个分布式资源实例。

Considering replica is an instance of AtomixReplica, the code snippet to create a distributed map resource and to set a value to it:

考虑到replicaAtomixReplica的一个实例,该代码段用于创建一个分布式地图资源并为其设置一个值。

replica.getMap("map")
  .thenCompose(m -> m.put("bar", "Hello world!"))
  .thenRun(() -> System.out.println("Value is set in Distributed Map"))
  .join();

Here join() method will block program until the resource is created and value is set to it. We can get the same object using AtomixClient and retrieve the value with the get(“bar”) method.

这里的join()方法将阻塞程序,直到资源被创建并为其设置值。我们可以使用AtomixClient获得相同的对象,并使用get(“bar”) 方法检索其值。

We can use get() method at the end to wait for the result :

我们可以在最后使用get() 方法来等待结果。

String value = client.getMap("map"))
  .thenCompose(m -> m.get("bar"))
  .thenApply(a -> (String) a)
  .get();

5. Consistency and Fault Tolerance

5.一致性和容错性

Atomix is utilized for mission-critical small scale data-sets for which consistency is a much bigger concern than availability.

Atomix用于关键任务的小规模数据集,对于这些数据集来说,一致性是比可用性更重要的问题。

It provides strong configurable consistency through linearizability for both reads and writes. In linearizability, once a write is committed, all clients are guaranteed to be aware of the resulting state.

它通过对读和写的线性化提供强大的可配置一致性。在线性化中,一旦写被提交,所有客户都被保证知道所产生的状态。

Consistency in Atomix’s cluster is guaranteed by underlying Raft consensus algorithm where an elected leader will have all the writes that were previously successful.

Atomix集群中的一致性是由底层的Raft共识算法保证的,在这种算法中,当选的领导者将拥有所有先前成功的写入。

All new writes will go through the cluster leader and synchronously replicated to a majority of the server before completion.

所有新的写入将通过集群领导者,并在完成之前同步复制到大多数的服务器。

To maintain fault-tolerance, majority server of the cluster needs to be alive. If minority number of nodes fail, nodes will be marked inactive and will be replaced by passive nodes or standby nodes.

为了保持容错性,集群的大多数服务器需要活着。如果少数节点发生故障,节点将被标记为不活动,并由被动节点或备用节点取代。

In case of leader failure, the remaining servers in the cluster will begin a new leader election. Meanwhile, the cluster will be unavailable.

在领导者失败的情况下,集群中的其余服务器将开始新的领导者选举。同时,集群将不可用。

In case of partition, if a leader is on the non-quorum side of the partition, it steps down, and a new leader is elected in the side with a quorum.

在分治的情况下,如果领导者在分治的非法定人数一侧,他就会下台,并在有法定人数的一侧选出一个新的领导者。

And, if the leader is on the majority side, it’ll continue with no change. When the partition is resolved, nodes on the non-quorum side will join the quorum and update their log accordingly.

而且,如果领导者在多数一方,就会继续下去,没有变化。当分区解决后,非法定人数一方的节点将加入法定人数,并相应地更新其日志。

6. Conclusion

6.结论

Like ZooKeeper, Atomix provides a robust set of libraries for dealing with distributed computing issues.

像ZooKeeper一样,Atomix提供了一套强大的库来处理分布式计算问题。

And, as always, the full source code for this task is available over on GitHub.

而且,像往常一样,这项任务的完整源代码可在GitHub上获得。