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-пакеты
Привязки ZooKeeper Java состоят в основном из двух пакетов Java:
org.apache.zookeeper
: определяет основной класс клиентской библиотеки ZooKeeper вместе со многими статическими определениями типов и состояний событий ZooKeeper.org.apache.zookeeper.data
: определяет характеристики, связанные с ZNodes, такие как списки контроля доступа (ACL), идентификаторы, статистика и т. д.
В реализации сервера также используются 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 .
В методе подключения
мы создаем экземпляр класса 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.
И, как всегда, все исходники для этого туториала можно найти на Github .