«1. Обзор

Apache ZooKeeper — это служба распределенной координации, упрощающая разработку распределенных приложений. Он используется такими проектами, как Apache Hadoop, HBase и другими, для различных вариантов использования, таких как выбор лидера, управление конфигурацией, координация узлов, управление арендой серверов и т. д.

Узлы в кластере ZooKeeper хранят свои данные в общем иерархическом пространстве имен, похожем на стандартная файловая система или древовидная структура данных.

В этой статье мы рассмотрим, как использовать Java API Apache Zookeeper для хранения, обновления и удаления информации, хранящейся в ZooKeeper.

2. Настройка

Последнюю версию Java-библиотеки Apache ZooKeeper можно найти здесь:

<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.11</version>
</dependency>

3. Модель данных ZooKeeper — ZNode

ZooKeeper имеет иерархическое пространство имен, очень похожее на распределенная файловая система, в которой хранятся данные координации, такие как информация о состоянии, информация о координации, информация о местоположении и т. д. Эта информация хранится на разных узлах.

Каждый узел в дереве ZooKeeper называется ZNode.

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

4. Установка

4.1. Установка

Последнюю версию ZooKeeper можно загрузить отсюда. Прежде чем сделать это, нам нужно убедиться, что мы соответствуем системным требованиям, описанным здесь.

4.2. Автономный режим

В этой статье мы будем запускать ZooKeeper в автономном режиме, так как он требует минимальной настройки. Выполните шаги, описанные в документации здесь.

Примечание. В автономном режиме репликация невозможна, поэтому в случае сбоя процесса ZooKeeper служба перестанет работать.

5. Примеры интерфейса командной строки ZooKeeper

Теперь мы будем использовать интерфейс командной строки ZooKeeper (CLI) для взаимодействия с ZooKeeper:

bin/zkCli.sh -server 127.0.0.1:2181

Приведенная выше команда запускает автономный экземпляр локально. Давайте теперь посмотрим, как создать ZNode и хранить информацию в ZooKeeper:

[zk: localhost:2181(CONNECTED) 0] create /MyFirstZNode ZNodeVal
Created /FirstZnode

Мы только что создали ZNode «MyFirstZNode» в корне иерархического пространства имен ZooKeeper и записали в него «ZNodeVal».

Поскольку мы не передали ни одного флага, созданный ZNode будет постоянным.

Давайте теперь выполним команду «get», чтобы получить данные, а также метаданные, связанные с ZNode:

[zk: localhost:2181(CONNECTED) 1] get /FirstZnode

“Myfirstzookeeper-app”
cZxid = 0x7f
ctime = Sun Feb 18 16:15:47 IST 2018
mZxid = 0x7f
mtime = Sun Feb 18 16:15:47 IST 2018
pZxid = 0x7f
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 22
numChildren = 0

Мы можем обновить данные существующего ZNode, используя операцию set.

Например:

set /MyFirstZNode ZNodeValUpdated

Это обновит данные в MyFirstZNode с ZNodeVal на ZNodeValUpdated.

6. Пример Java API ZooKeeper

Давайте теперь рассмотрим Java API Zookeeper и создадим узел, обновим узел и получим некоторые данные.

6.1. Java-пакеты

Java-привязки ZooKeeper состоят в основном из двух Java-пакетов:

  1. org.apache.zookeeper: which defines the main class of the ZooKeeper client library along with many static definitions of the ZooKeeper event types and states
  2. org.apache.zookeeper.data: that defines the characteristics associated with ZNodes, such as Access Control Lists (ACL), IDs, stats, and so on

В реализации сервера также используются Java-API ZooKeeper, такие как org.apache.zookeeper.server, org.apache.zookeeper.server.quorum, и org.apache.zookeeper.server.upgrade.

Однако они выходят за рамки этой статьи.

6.2. Подключение к экземпляру ZooKeeper

Давайте теперь создадим класс ZKConnection, который будет использоваться для подключения и отключения от уже запущенного ZooKeeper:

public class ZKConnection {
    private ZooKeeper zoo;
    CountDownLatch connectionLatch = new CountDownLatch(1);

    // ...

    public ZooKeeper connect(String host) 
      throws IOException, 
      InterruptedException {
        zoo = new ZooKeeper(host, 2000, new Watcher() {
            public void process(WatchedEvent we) {
                if (we.getState() == KeeperState.SyncConnected) {
                    connectionLatch.countDown();
                }
            }
        });

        connectionLatch.await();
        return zoo;
    }

    public void close() throws InterruptedException {
        zoo.close();
    }
}

Чтобы использовать сервис ZooKeeper, приложение должно сначала создать экземпляр объекта класса ZooKeeper, который является основным классом клиентской библиотеки ZooKeeper.

В методе connect мы создаем экземпляр класса ZooKeeper. Кроме того, мы зарегистрировали метод обратного вызова для обработки WatchedEvent от ZooKeeper для принятия соединения и, соответственно, завершения метода соединения с использованием метода обратного отсчета CountDownLatch.

Как только соединение с сервером установлено, идентификатор сеанса назначается клиенту. Чтобы сеанс оставался действительным, клиент должен периодически отправлять на сервер тактовые импульсы.

Клиентское приложение может вызывать API-интерфейсы ZooKeeper, пока его идентификатор сеанса остается действительным.

6.3. Клиентские операции

Теперь мы создадим интерфейс ZKManager, который предоставляет различные операции, такие как создание ZNode и сохранение некоторых данных, получение и обновление данных ZNode:

public interface ZKManager {
    public void create(String path, byte[] data)
      throws KeeperException, InterruptedException;
    public Object getZNodeData(String path, boolean watchFlag);
    public void update(String path, byte[] data) 
      throws KeeperException, InterruptedException;
}

«

public class ZKManagerImpl implements ZKManager {
    private static ZooKeeper zkeeper;
    private static ZKConnection zkConnection;

    public ZKManagerImpl() {
        initialize();
    }

    private void initialize() {
        zkConnection = new ZKConnection();
        zkeeper = zkConnection.connect("localhost");
    }

    public void closeConnection() {
        zkConnection.close();
    }

    public void create(String path, byte[] data) 
      throws KeeperException, 
      InterruptedException {
 
        zkeeper.create(
          path, 
          data, 
          ZooDefs.Ids.OPEN_ACL_UNSAFE, 
          CreateMode.PERSISTENT);
    }

    public Object getZNodeData(String path, boolean watchFlag) 
      throws KeeperException, 
      InterruptedException {
 
        byte[] b = null;
        b = zkeeper.getData(path, null, null);
        return new String(b, "UTF-8");
    }

    public void update(String path, byte[] data) throws KeeperException, 
      InterruptedException {
        int version = zkeeper.exists(path, true).getVersion();
        zkeeper.setData(path, data, version);
    }
}

«Давайте теперь посмотрим на реализацию вышеуказанного интерфейса:

В приведенном выше коде вызовы подключения и отключения делегируются ранее созданному классу ZKConnection. Наш метод create используется для создания ZNode по заданному пути из данных массива байтов. Только для демонстрационных целей мы оставили ACL полностью открытым.

После создания ZNode является постоянным и не удаляется при отключении клиента.

Логика извлечения данных ZNode из ZooKeeper в нашем методе getZNodeData довольно проста. Наконец, с помощью метода обновления мы проверяем наличие ZNode по заданному пути и извлекаем его, если он существует.

Кроме того, для обновления данных мы сначала проверяем наличие ZNode и получаем текущую версию. Затем мы вызываем метод setData с путем к ZNode, данными и текущей версией в качестве параметров. ZooKeeper обновит данные только в том случае, если переданная версия совпадает с последней версией.

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

При разработке распределенных приложений Apache ZooKeeper играет критическую роль в качестве службы распределенной координации. В частности, для таких случаев использования, как хранение общей конфигурации, выбор главного узла и т. д.

ZooKeeper также предоставляет элегантный API на основе Java, который можно использовать в коде клиентского приложения для беспрепятственного взаимодействия с ZooKeeper ZNodes.