Введение в Atomix

Введение в Atomix

1. обзор

Большинство распределенных приложений требуют, чтобы какой-то компонент с состоянием был согласованным и отказоустойчивым. Atomix is an embeddable library helping in achieving fault-tolerance and consistency for distributed resources.с

Он предоставляет богатый набор API для управления своими ресурсами, такими как коллекции, группы и инструменты для параллелизма.

Для начала нам нужно добавить следующую зависимость Maven в наш pom:


    io.atomix
    atomix-all
    1.0.8

Эта зависимость обеспечивает основанный на Netty транспорт, необходимый его узлам для связи друг с другом.

2. Загрузка кластера

Чтобы начать работу с Atomix, нам нужно сначала загрузить кластер.

Atomix состоит из набора реплик, которые используются для создания распределенных ресурсов с сохранением состояния. Each replica maintains a copy of the state of each resource existing in the cluster.с

Реплики в кластере бывают двух типов: активные и пассивные.

Изменения состояния распределенных ресурсов распространяются через активные реплики, а пассивные реплики синхронизируются для обеспечения отказоустойчивости.

2.1. Загрузка встроенного кластера

Для начальной загрузки кластера с одним узлом нам нужно сначала создать экземплярAtomixReplica:

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

Здесь реплика настроена сStorage иTransport. Фрагмент кода для объявления хранения:

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

После того, как реплика объявлена ​​и настроена с хранением и транспортировкой, мы можем запустить ее, просто вызвавbootstrap() - который возвращаетCompletableFuture, который можно использовать для блокировки до тех пор, пока сервер не будет загружен, вызывая связанную блокировкуjoin() метод:

CompletableFuture future = replica.bootstrap();
future.join();

Пока что мы построили кластер с одним узлом. Теперь мы можем добавить к нему больше узлов.

Для этого нам нужно создать другие реплики и присоединить их к существующему кластеру; нам нужно создать новый поток для вызова метода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();

Теперь у нас есть загрузочный кластер из трех узлов. В качестве альтернативы, мы можем запустить кластер, передавList адресов в методеbootstrap(List<Address>):

List
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();

Нам нужно создать новую ветку для каждой реплики.

2.2. Загрузка автономного кластера

Сервер Atomix можно запускать как отдельный сервер, который можно загрузить из Maven Central. Проще говоря - это Java-архив, который можно запустить через терминал, предоставив

Проще говоря, это Java-архив, который можно запустить через терминал, указав параметрhost: port во флаге адреса и используя флаг-bootstrap.

Вот команда для начальной загрузки кластера:

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

Здесьatomix.properties - это файл конфигурации для настройки хранения и транспорта. Чтобы создать многоузловой кластер, мы можем добавить узлы к существующему кластеру, используя флаг-join.

Формат для этого:

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

3. Работа с клиентом

Atomix поддерживает создание клиента для удаленного доступа к своему кластеру через APIAtomixClient.

Поскольку клиенты не обязательно должны отслеживать состояние,AtomixClient не имеет хранилища. Нам просто нужно настроить транспорт при создании клиента, так как транспорт будет использоваться для связи с кластером.

Создадим клиента с транспортом:

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

Теперь нам нужно подключить клиента к кластеру.

Мы можем объявитьList дляAddress и передатьList в качестве аргумента методу клиентаconnect():

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

4. Обработка ресурсов

Истинная сила Atomix заключается в его мощном наборе API для создания и управления распределенными ресурсами. Resources are replicated and persisted in a cluster and are bolstered by a replicated state machine - управляется его основной реализацией протокола консенсуса Raft.

Распределенные ресурсы могут быть созданы и управляться одним из методовget(). Мы можем создать экземпляр распределенного ресурса изAtomixReplica.

Учитывая, чтоreplica является экземпляромAtomixReplica, фрагмента кода для создания ресурса распределенной карты и установки для него значения:

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

Здесь методjoin() блокирует программу до тех пор, пока ресурс не будет создан и ему не будет присвоено значение. Мы можем получить тот же объект с помощьюAtomixClient и получить значение с помощью методаget(“bar”).

Мы можем использовать методget() в конце, чтобы дождаться результата:

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

5. Последовательность и отказоустойчивость

Atomix используется для критически важных небольших наборов данных, для которых согласованность является гораздо более важной задачей, чем доступность.

It provides strong configurable consistency through linearizability for both reads and writes. Что касается линеаризации, то после фиксации записи все клиенты гарантированно узнают о результирующем состоянии.

Согласованность в кластере Atomix гарантируется лежащим в основе алгоритмом консенсуса Raft, в котором избранный лидер будет иметь все записи, которые ранее были успешными.

Все новые записи будут проходить через лидер кластера и синхронно реплицироваться на большинство серверов до завершения.

Для поддержания отказоустойчивости,majority server of the cluster needs to be alive. Если отказывает меньшинство узлов, узлы будут помечены как неактивные и будут заменены пассивными узлами или резервными узлами.

В случае отказа лидера, остальные серверы в кластере начнут новые выборы лидера. Между тем кластер будет недоступен.

В случае разделения, если лидер находится на стороне, не являющейся кворумом раздела, он уходит в отставку, и новый лидер избирается на стороне с кворумом.

И, если лидер находится на стороне большинства, все останется без изменений. Когда раздел разрешен, узлы на стороне, не являющейся кворумом, присоединятся к кворуму и обновят свой журнал соответствующим образом.

6. Заключение

Как и ZooKeeper, Atomix предоставляет надежный набор библиотек для решения проблем распределенных вычислений.

И, как всегда, доступен полный исходный код этой задачиover on GitHub.