JavaRush /Java блог /Random UA /Zookeeper або як живеться працівнику зоопарку
Viacheslav
3 рівень

Zookeeper або як живеться працівнику зоопарку

Стаття з групи Random UA
Zookeeper або як живеться працівнику зоопарку.

Вступ

Програми Java часто мають різні конфігурації. Наприклад, адресаа та порт підключення. Наприклад, це могло б виглядати так, якби використовували клас Properties :
public static void main(String []args) {
	Properties props = new Properties();
	props.setProperty("host", "www.tutorialspoint.com");
	System.out.println("Hello, " + props.getProperty("host"));
}
І цього начебто достатньо, т.к. ми можемо Properties отримати із файлу. І наче у нас все на одній машині вживається добре. Але уявіть, що наша система починає складатися із різних систем, які розділені між собою? Така система ще називається розподіленою (Distributed Systems). У вікіпедії можна знайти таке визначення: Розподілені системи - це системи, компоненти які розташовані на різних мережевих комп'ютерах, які спілкуються між собою та координують свої дії обмінюючись одна з одною повідомленнями. Можна поглянути на таку схему:
Zookeeper або як живеться працівнику зоопарку - 2
За такого підходу єдина система розділена на компоненти. Конфігурування – окремий загальний компонент. Кожен із інших компонентів виступає у ролі клієнта для компонента конфігурування. Такий випадок називається " Розподілена конфігурація ". Існує безліч різних реалізацій розподіленої конфігурації. І в сьогоднішньому огляді пропоную познайомитись із однією з них, яка називається Zookeeper.
Zookeeper або як живеться працівнику зоопарку - 3

Zookeeper

Шлях до знайомства з Zookeeper починається з їхнього офіційного сайту: zookeeper.apache.org На офіційному сайті необхідно перейти в розділ " Download ". У цьому розділі завантажуємо архів у форматі .tar.gz, наприклад "zookeeper-3.4.13.tar.gz". tar – це формат архіву, традиційний для Unit систем. gz означає, що для стиснення архіву використовується gzip. Якщо ми працюємо на Windows машині, нас це не повинно бентежити. Більшість сучасних архіваторів (наприклад, 7-zip), чудово вміють з ними працювати і на Windows. Витягнемо вміст у якийсь каталог. Заодно побачимо різницю - на диску у видобутому стані воно займатиме приблизно 60 мегабайт, а ми завантажували архів розміром близько 35 мегабайт. Як видно, стиск дійсно працює. Тепер потрібно запустити Zookeeper. Взагалі Zookeeper є свого роду сервером. Zookeeper може бути запущений в одному з двох режимів: Standalone або Replicated . Розглянемо найпростіший варіант, він перший варіант — Standalone mode. Щоб Zookeper запустився, йому потрібний файл конфігурації. Тому, створимо його тут: [КаталогРаспаковкиZookeeper]/conf/zoo.cfg. Для Windows скористаємося рекомендацією з Medium: " Installing Apache ZooKeeper on Windows". Зміст конфігураційного файлу буде приблизно наступним:
tickTime=2000
dataDir=C:/zookeeper-3.4.13/data
clientPort=2181
Додамо змінну середовища оточення ZOOKEEPER_HOME, що містить шлях до кореневого каталогу zookeper (як в інструкції на medium), а також додамо змінну середовища оточення PATH наступний фрагмент: Так само каталог, вказаний в dataDir, повинен існувати, інакше Zookeeper не зможе ;%ZOOKEEPER_HOME%\bin; запустити сервер. Тепер ми можемо сміливо запускати сервер за допомогою команди zkServer. Завдяки тому, що каталог Zookeeper був доданий до змінного середовища оточення path ми можемо викликати команди Zookeper звідки завгодно, а не тільки з каталогу bin.
Zookeeper або як живеться працівнику зоопарку - 4

ZNode

Як сказано в " Zookeeper Overview ", дані в Zookeper представлені у вигляді ZNode (вузлів), які об'єднані в деревоподібну структуру. Тобто кожен ZNode може містити дані та мати дочірні ZNode. Докладніше про організацію ZNode можна прочитати в документації Zookeeper: " Data model and the hierarchical namespace ". Для роботи з Zookeeper та ZNode скористаємося Zookeeper CLI (Command Line interface – інтерфейс командного рядка). Раніше ми запустабо сервер за допомогою команди zkServer. Тепер, для підключення виконаємо zkCli.cmd -server 127.0.0.1:2181 При успішному виконанні буде створена сесія підключення до Zookeeper і ми побачимо приблизно такий висновок:
Zookeeper або як живеться працівнику зоопарку - 5
Цікаво, що навіть одразу після встановлення Zookeeper вже має ZNode. Має він наступний шлях:/zookeeper/quota
Zookeeper або як живеться працівнику зоопарку - 6
Це звані " квоти " . Як сказано в " Apache ZooKeeper Essentials ", кожен ZNode може мати асоційовану з ним квоту, що обмежує збережені дані. Може бути вказано обмеження кількості znode і обсяг збережених даних. Якщо це обмеження перевищено, то операції з ZNode не скасовується, але буде отримано попередження про перевищення ліміту. Про ZNode рекомендується прочитати в " ZooKeeper Programmer's Guide: ZNodes ". Декілька прикладів від туди, як можна працювати з ZNode:
Zookeeper або як живеться працівнику зоопарку - 7
Хочеться відзначити також, що ZNode бувають різні. Звичайні ZNode (якщо не вказати додаткові прапори) є типом " persistent ". Є ZNode типу " Ephemeral Node ". Такі ZNode існують лише на час існування сесії підключення до Zookeeper, в рамках якої вони створювалися. Є ZNode типу " Sequence Node ". До таких ZNode додається номер із послідовності, щоб гарантувати унікальність. Sequence Node може бути як persistent, і ephemeral. Про ZNode так само рекомендується невелика довідкова інформація тут: " Zookeeper ZNodes - Characteristics & Example ".
Zookeeper або як живеться працівнику зоопарку - 8

ZNode Watcher

Хотілося б поговорити про спостерігачів (watchers). Докладно про них написано в документації Zookeeper'а: " ZooKeeper Watches ". Якщо коротко, то вотчер – це такий одноразовий тригер, який спрацьовує на певну подію. Отримуючи дані, виконуючи операції getData(), getChildren() або exists(), ми можемо створити тригер як додаткову дію. Zookeeper забезпечує порядок обробки event. Крім того, в документації зазначено, що перш ніж ми зможемо побачити нове значення ZNode, ми побачимо event про зміну старого значення на нове. Докладніше про Watcher'ів можна прочитати тут: " ZooKeeper Watches - Features & Guarantees ". Для того щоб це спробувати, знову скористаємося CLI: Припустимо, у нас є деякий ZNode зі значенням, де ми зберігаємо статус деякого сервісу:
[zk: 127.0.0.1:2181(CONNECTED) 0] create /services/service1/status stopped
Created /services/service1/status
[zk: 127.0.0.1:2181(CONNECTED) 1] get /services/service1/status [watch]
stopped
Тепер, якщо дані /services/service1/statusзміниться, то відпрацює наш одноразовий тригер:
Zookeeper або як живеться працівнику зоопарку - 9
Цікаво, що при підключенні до Zookeeper'у ми також бачимо, як відпрацьовує вотчер:
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
SyncConnected є однією з можливих подій Zookeper. Докладніше про нього можна переглянути в описі API.
Zookeeper або як живеться працівнику зоопарку - 10

Zookeeper та Java

Тепер у нас є деяке базове уявлення про те, що може Zookeeper. Давайте тепер із ним попрацюємо через Java, а не через CLI. І для цього нам знадобиться Java додаток, на якому ми побачимо, як же з Zookeeper'ом працювати. Для створення програми скористаємося системою складання проектів Gradle . За допомогою " Gradle Build Init plugin " виконаємо створення проекту. Для цього виконаємо команду:gradle init --type java-application Якщо Gradle нас буде запитувати уточнюючі питання, то залишимо значення за замовчуванням (просто натискаємо Enter). Тепер відкриємо білд-скрипт, тобто. файл build.gradle. У ньому опис того, з чого влаштований наш проект і від яких артефактів (бібліотек, фреймворків) залежить. Т.к. ми хочемо використовувати Zookeeper, треба додати його. Тому, додамо в блок залежності залежність від Zookeeper'а:
dependencies {
    implementation 'org.apache.zookeeper:zookeeper:3.4.13'
Докладніше про Gradle можна прочитати в огляді: " Коротке знайомство з Gradle ". Отже, ми маємо Java проект, до нього ми підключабо бібліотеку Zookeeper'а. Давайте тепер щось напишемо. Як ми пам'ятаємо, за допомогою CLI ми підключалися приблизно так: zkCli.cmd -server 127.0.0.1:2181 Давайте в класі App в main методі оголосимо атрибут "сервер":
String server = "127.0.0.1:2181";
Підключення – дія не миттєва. Нам доведеться якось у головному потоці виконання програми чекати, коли відбудеться підключення. Тому нам знадобиться лок. Оголосимо його нижче:
Object lock = new Object();
Тепер нам потрібний хтось, хто скаже, що підключення встановлено. Як ми пам'ятаємо, коли ми це робабо через CLI, у нас спрацьовував вотчер. Так ось в Java коді все так само. Наш вотчер виводитиме повідомлення про успішне виконання і повідомлятиме про це всіх, хто чекає через лок. Напишемо вотчер:
Watcher connectionWatcher = new Watcher() {
	public void process(WatchedEvent we) {
		if (we.getState() == Event.KeeperState.SyncConnected) {
			System.out.println("Connected to Zookeeper in " + Thread.currentThread().getName());
			synchronized (lock) {
            	lock.notifyAll();
            }
		}
	}
};
Тепер допишемо підключення до сервера zooKeeper'а:
int sessionTimeout = 2000;
ZooKeeper zooKeeper = null;
synchronized (lock) {
	zooKeeper = new ZooKeeper(server, sessionTimeout, connectionWatcher);
	lock.wait();
}
Тут все просто. При виконанні main методу в головному потоці програми ми захоплюємо lock і запитуємо підключення до zookeeper'у. При цьому ми відпускаємо лок і чекаємо, поки хтось інший не захопить лок і не повідомить нас, що можна продовжувати. Коли підключення буде встановлено, то спрацює вічер. Він перевірить, що настала подія - SyncConnected (як ми пам'ятаємо, саме її ловив вотчер через CLI), і тоді напише повідомлення. Далі ми захоплюємо lock (т.к. раніше головний потік його відпустив) і повідомляємо всі потоки, що чекають lock, що можна продовжувати. Потік обробки події виходить із synchronized блоку, тим самим звільняючи lock. Головний потік отримав повідомлення та дочекавшись звільнення lock продовжує виконання, т.к. поки не отримає lock, то він не зможе вийти з synchronized блоку та продовжити роботу. Таким чином, використовуючи багатопотоковість і Zookeeper API ми можемо виконувати різні дії. Zookeeper API набагато ширше, ніж дозволяє використовувати CLI. Наприклад:
// створення нового узла
String znodePath = "/zookeepernode2";
List<ACL> acls = ZooDefs.Ids.OPEN_ACL_UNSAFE;
if (zooKeeper.exists(znodePath, false) == null) {
	zooKeeper.create(znodePath, "data".getBytes(), acls, CreateMode.PERSISTENT);
}

// Получение данных из узла
byte[] data = zooKeeper.getData(znodePath, null, null);
System.out.println("Result: " + new String(data, "UTF-8"));
Як видно, під час створення вузла ми можемо налаштувати ACL. Це ще одна важлива особливість. ACL – це дозволи, які поширюються на дії з ZNode. Налаштувань багато, тому за подробицями рекомендую звернутися до офіційної документації: " Zookeeper ACL Permissions ".
Zookeeper або як живеться працівнику зоопарку - 11

Висновок

Навіщо ми це прочитали? Тому що Zookeeper використовується і в інших затребуваних технологіях. Наприклад, Apache Kafka вимагає Zookeeper, про що можна прочитати в " Kafka Quick Start Guide ". Так само використовується в NOSQL базі даних HBase, про що можна докладніше прочитати в їх " HBase Quickstart Guide ". Насправді багато інших проектів використовують Zookeeper. Частина з них наведена у списку у " Використання Zookeeper у реальному світі ". Сподіваюся, на запитання "навіщо" я відповів. Найголовніше питання тепер: "Що далі?" По-перше, на тему Apache Zookeeper можна почитати такі книги: По-друге, є чудові відео доповіді про Zookeeper. Рекомендуються до перегляду: По-третє, є кілька корисних статей, які доповнять картину світу: Вийшов зовсім невеликий огляд, але як вступне слово, сподіваюся, буде корисним. #Viacheslav
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ