概观
Atomix 2.1是用于构建容错分布式系统的全功能框架。Atomix将ZooKeeper的一致性与Hazelcast的可用性和性能相结合,使用一组定制通信API,一个分区Raft集群和一个多主协议来为构建分布式系统提供一系列高级基元。这些原语包括:
背景
Atomix最初是在2014年将它的姊妹项目Copycat(已弃用)作为爱好项目构思出来的。随着时间的推移,Copycat成长为Raft共识协议的成熟实现,并且Copycat和Atomix都在各种项目中投入使用。2017年开始开发新版本,并将Copycat和Atomix结合在Atomix 2.x中。此外,最初为在ONOS中使用而开发的项目的重要扩展被迁移到Atomix 2.x. Atomix现在作为开放网络基金会 ONOS的核心组件。
依赖管理
Atomix打包在一个模块层次结构中,用户只能依赖他们打算使用的功能。几乎所有用户都希望使用Atomix核心模块,该模块由atomix工件ID标识:
<dependencies>
<dependency>
<groupId>io.atomix</groupId>
<artifactId>atomix</artifactId>
<version>2.1.0-SNAPSHOT</version>
</dependency>
</dependencies>
此外,大多数群集都配置有一组分区组。使用的分区组取决于系统的一致性,容错性和持久性要求。不同的用例可能需要不同的依赖关系。但与Atomix打包是两个主要协议:
-
atomix-raft
-
atomix-primary-backup
io.atomix atomix 2.1.0-SNAPSHOT io.atomix atomix-raft 2.1.0-SNAPSHOT io.atomix atomix-primary-backup 2.1.0-SNAPSHOT
引导群集
与Atomix合作的第一步是形成一个集群。Atomix群集由两种类型的成员组成:
- PERSISTENT 成员存在于群集配置中,无论是否可用
- EPHEMERAL 成员根据其可用性加入和离开群集
要形成群集,通常需要引导一组节点。此外,如果使用分布式基元,则必须配置一个或多个分区组。
使用Java API
用户可以使用各种方法在Atomix群集上运行。这些方法中最基本的是使用Java API,它提供了最好的性能,一致性和灵活性。
Atomix中的核心Java API是Atomix对象。Atomix严重依赖构建器模式来构建用于沟通和协调分布式系统的高级对象。
要创建一个新的Atomix实例,请创建一个Atomix构建器:
Atomix.Builder builder = Atomix.builder();
构建器应该配置本地节点配置:
builder.withLocalMember(Member.builder("node1")
.withType(Member.Type.EPHEMERAL)
.withAddress("localhost:5000")
.build());
除了配置本地节点信息之外,还必须为每个实例配置一组_引导节点_,从中形成一个集群。首次启动集群时,所有实例都应提供相同的引导节点集合。
builder.withMembers(
Member.builder("member1")
.withType(Member.Type.EPHEMERAL)
.withAddress("localhost:5000")
.build(),
Member.builder("member2")
.withType(Member.Type.EPHEMERAL)
.withAddress("localhost:5001")
.build(),
Member.builder("member3")
.withType(Member.Type.EPHEMERAL)
.withAddress("localhost:5002")
.build());
Bootstrap节点可以是节点PERSISTENT或EPHEMERAL节点。需要强一致性的集群必须由一组PERSISTENT能够参与共识的节点引导。具有更宽松的持久性/一致性要求的群集可以使用EPHEMERAL可动态扩展的节点。
要详细了解各种类型的节点之间的差异,请参阅用户手册
最后,该实例必须配置一个或多个分区组。通用分区组可以使用配置文件进行配置。
builder.addProfiles(Profiles.DATA_GRID);
通常情况下,需要强一致性保证的群集配置有CORE节点和至少一个RaftPartitionGroup群集,而群集设计用于DATA节点使用PrimaryBackupPartitionGroups的性能和可伸缩性。
Atomix atomix = builder.build();
调用start()实例来启动节点:
atomix.start().join();
该start()方法返回将在节点加入群集后完成的未来。
为了形成由CORE节点和Raft分区组组成的集群,大多数实例必须同时启动以允许Raft分区形成法定人数。在start()所有分区能够形成之前,该方法返回的未来将不会完成。如果您的Atomix实例在启动时无限期阻塞,请确保启用DEBUG日志记录以调试问题。
使用Atomix代理
如前所述,有多种方式可以管理和与Atomix集群进行交互。Atomix代理是Java API的一个简便替代方案。代理是一个独立的Atomix节点,其行为与Java节点的行为相似,但会为客户端交互提供REST API。代理可以用于在客户端 - 服务器体系结构中配置Atomix群集或提供对Atomix基元的多边形访问。
要使用Atomix代理,首先使用Maven下载并构建Atomix:
mvn clean package
项目建成后,要运行代理程序,$ATOMIX_ROOT必须设置环境变量:
export ATOMIX_ROOT=./
该代理程序与bin/atomix-agent脚本一起运行:
bin/atomix-agent -h
使用该-h选项查看代理脚本的选项列表。
在使用代理时,提供JSON或YAML配置文件是最方便的。配置文件也支持通过Java API支持的所有构建器配置。要配置代理,请创建一个atomix.yaml文件并定义群集:
atomix.yaml cluster: nodes: node1: type: ephemeral address: localhost:5001 node2: type: ephemeral address: localhost:5002 node3: type: ephemeral address: localhost:5003 profiles: - consensus - data-grid
Java API也支持配置文件。要Atomix使用配置文件配置实例,只需将该文件传递给Atomix构造函数即可。
配置文件创建完成后,通过引导配置的节点来启动群集:
bin/atomix-agent node1 -c atomix.yaml
bin/atomix-agent node2 -c atomix.yaml
bin/atomix-agent node3 -c atomix.yaml
可以在配置文件中指定本地节点,但在atomix-agent参数中指定本地实例名称可以使配置文件在所有实例之间共享。
创建一个Java客户端
所有的Atomix节点类型公开相同的API,并且能够执行与其对等体相同的功能。有状态节点和无状态节点,高度一致且最终一致的节点都可以在基元上操作,发送和接收消息以及管理集群。因此,Atomix不一定需要任何客户端节点。但是Atomix可以配置在各种不同的体系结构中,并且在有状态节点是独立代理的客户端 - 服务器体系结构中,客户端成为必需。
客户机节点的构建方式与所有其他节点相同,只是它们不参与复制,因此不是群集成员资格列表的成员。要配置客户端节点,只需创建一个CLIENT节点并将其指向一个或多个对等节点:
Atomix atomix = Atomix.builder()
.withLocalMember(Member.builder("client1")
.withAddress("localhost:6000")
.build())
.withMembers(
Member.builder("member1")
.withType(Member.Type.PERSISTENT)
.withAddress("localhost:5000")
.build(),
Member.builder("member2")
.withType(Member.Type.PERSISTENT)
.withAddress("localhost:5001")
.build(),
Member.builder("member3")
.withType(Member.Type.PERSISTENT)
.withAddress("localhost:5002")
.build());
最后,使用该start()方法启动实例将导致它加入群集:
atomix.start().join();
创建分布式基元
引导Atomix集群本身并不能提供很多功能。Atomix的真正力量来自其原始形式。基元是高级分布式对象,用于在分布式系统中复制状态和协调状态更改。
Atomix通常提供两种不同的方法来创建分布式基元:
- 使用构建器模式创建不同的实例
- 使用getter创建多个实例
与其他构建器一样,基元构造也提供与基元构建器相同的选项。构建者只需提供一个流畅的API来以编程方式配置基元。
要通过构建器模式创建分布式基元,请使用界面*Builder上的其中一种方法Atomix:
ConsistentMap<String, String> map = atomix.consistentMapBuilder("my-map")
.withCacheEnabled()
.build();
map.put("foo", "Hello world!");
Versioned<String> value = map.get("foo");
if (map.put("foo", "Hello world again!", value.version())) {
...
}
所有分布式基元都提供了API的同步和异步版本。默认情况下,getters和builders返回一个同步原语实例。要检索基元的基础异步实例,请使用以下async()方法:
AsyncConsistentMap<String, String> asyncMap = map.async();
asyncMap.put("foo", "Hello world!").thenRun(() -> {
...
});