Search by

cloud-castle / zookeeper

alex-4-17

Чистый PHP-клиент Apache ZooKeeper без ext-zookeeper: полный клиентский протокол (3.5-3.9) и рецепты координации.

v1.1.1 2026-09-03 05:00 UTC

This package is auto-updated.

Last update: 2026-09-03 05:31:12 UTC


README

🇷🇺 Русский · 🇬🇧 English · 🇩🇪 Deutsch · 🇫🇷 Français · 🇪🇸 Español · 🇮🇹 Italiano

CloudCastle Zookeeper

CloudCastle Zookeeper

Чистый PHP-клиент Apache ZooKeeper без расширений: полный и корректный клиентский протокол (совместимость с сервером 3.5 → 3.9) и рецепты координации уровня Apache Curator.

Packagist PHP License Downloads Monthly

Repository CI Issues

PHPStan Psalm PHPCS PHPMD Coverage Infection MSI OpenSSF Scorecard

Библиотека реализует бинарный протокол ZooKeeper (Jute) целиком на PHP — без PECL-расширения ext-zookeeper и без C-зависимостей. Достаточно PHP 8.1+. Клиент проверен на корректность интеграционными тестами против реального сервера ZooKeeper (3.5 → 3.9) в Docker.

Установка

composer require cloud-castle/zookeeper

Требуется 64-битный PHP 8.1+: идентификаторы транзакций (zxid), сессий и метки времени ZooKeeper — 64-битные целые числа, на 32-битном PHP они переполняются.

Быстрый старт

<?php

use CloudCastle\Zookeeper\Client;
use CloudCastle\Zookeeper\Configuration\Config;
use CloudCastle\Zookeeper\Protocol\CreateMode;

$client = new Client(new Config(host: '127.0.0.1', port: 2181));
$client->connect();

// Создание узла и чтение данных
$client->create('/app/config', 'value', CreateMode::PERSISTENT);
echo $client->get('/app/config')->data;          // value

// Оптимистичное обновление по версии
$stat = $client->exists('/app/config');
$client->set('/app/config', 'updated', $stat->version);

// Дочерние узлы и метаданные
$children = $client->getChildren('/app');
$total = $client->getAllChildrenNumber('/app');

$client->close();

⚠️ ACL по умолчанию открыт. create/create2 без явного списка ACL применяют Acl::openUnsafe() (world:anyone, все права) — удобно в доверенной сети, но небезопасно в общем окружении. Задавайте ACL явно или один раз через Config(defaultAcl: [...]). Подробнее — SECURITY.md.

Наблюдатели (watch)

use CloudCastle\Zookeeper\Contract\Watcher;
use CloudCastle\Zookeeper\Session\WatchedEvent;

$client->exists('/app/config', new class implements Watcher {
    public function process(WatchedEvent $event): void
    {
        if ($event->isNodeDataChanged()) {
            // отреагировать на изменение
        }
    }
});

$client->ping(); // доставляет накопившиеся уведомления обработчикам

Атомарные транзакции

$results = $client->getTransaction()
    ->create('/app/a', '1')
    ->create('/app/b', '2')
    ->setData('/app/a', '11')
    ->check('/app/b', 0)
    ->commit(); // все операции применяются атомарно либо ни одна

Рецепты координации

use CloudCastle\Zookeeper\Recipe\Lock;
use CloudCastle\Zookeeper\Recipe\LeaderElection;
use CloudCastle\Zookeeper\Recipe\SharedCounter;

// Справедливая распределённая блокировка (без «эффекта громового стада»)
$lock = new Lock($client, '/locks/resource');
$lock->acquire();
try {
    // критическая секция
} finally {
    $lock->release();
}

// Выборы лидера
$election = new LeaderElection($client, '/election/service', identity: 'node-1');
$election->enter();
if ($election->isLeader()) {
    // выполнять работу лидера
}

// Распределённый атомарный счётчик
$counter = new SharedCounter($client, '/counters/orders');
$next = $counter->increment();

Асинхронный клиент

Для неблокирующей работы есть AsyncClient на встроенном цикле событий (EventLoop, на stream_select, без внешних зависимостей): операции возвращают Promise, а один поток обслуживает множество параллельных запросов.

use CloudCastle\Zookeeper\Async\AsyncClient;
use CloudCastle\Zookeeper\Async\EventLoop;
use CloudCastle\Zookeeper\Configuration\Config;

$loop = new EventLoop();
$client = AsyncClient::open(new Config(host: '127.0.0.1', port: 2181), $loop);

$loop->await($client->connect());

// Параллельные операции без блокировки
$client->get('/app/a')->then(fn ($response) => print($response->data . "\n"));
$client->get('/app/b')->then(fn ($response) => print($response->data . "\n"));

$loop->run();
$client->close();

Клиент не привязан к встроенному циклу: он работает и на цикле ReactPHP (адаптер Async\Adapter\ReactEventLoop, пакет react/event-loop), и на цикле Revolt/Amp (адаптер Async\Adapter\RevoltEventLoop, пакет revolt/event-loop) — оба в suggest. Так клиент разделяет единый цикл событий с уже существующим асинхронным приложением.

use CloudCastle\Zookeeper\Async\Adapter\ReactEventLoop;
use React\EventLoop\Loop;

$loop = new ReactEventLoop(Loop::get());
$client = AsyncClient::open(new Config(), $loop);
// ... операции возвращают Promise и исполняются в общем цикле ReactPHP

⚠️ AsyncClient не переподключается автоматически при обрыве и не поддерживает addAuth: при потере соединения ожидающие обещания отклоняются, а сессию нужно открыть заново. Где нужны автоматическое переподключение и аутентификация — используйте синхронный Client.

Отложенная запись через очередь

Изменяющие операции можно развязать с латентностью сервера через очередь: продюсер кладёт create/set/delete в очередь, не блокируясь на сети, а потребитель применяет их к серверу в фоне. Контракт Contract\OperationQueue абстрагирует транспорт: встроенная Queue\InMemoryOperationQueue держит операции в памяти процесса, а внешние очереди (cloud-castle/queue, Redis, RabbitMQ, Kafka) подключаются реализацией того же контракта.

use CloudCastle\Zookeeper\Queue\InMemoryOperationQueue;
use CloudCastle\Zookeeper\Queue\OperationProducer;
use CloudCastle\Zookeeper\Queue\OperationConsumer;

$queue = new InMemoryOperationQueue();

// В обработчике запроса — быстрая постановка в очередь без сетевого обмена
$producer = new OperationProducer($queue);
$producer->create('/app/jobs/job-1', 'payload');
$producer->set('/app/state', 'ready');

// В фоновом воркере — применение накопленных операций
$consumer = new OperationConsumer($queue, $client);
$applied = $consumer->drain();

Возможности

  • Соединение и сессия: ансамбль с перебором узлов, восстановление сессии с сохранением идентификатора и переустановкой watch, heartbeat/ping, chroot, режим только для чтения, согласованный таймаут, отслеживание last-zxid.
  • TLS/SSL: подключение к secureClientPort, X.509 и взаимная аутентификация (mTLS), строгая проверка сертификата по умолчанию.
  • Узлы: все режимы создания (persistent, ephemeral, sequential, container, TTL), create2 с метаданными, рекурсивное удаление, sync, getEphemerals, getAllChildrenNumber.
  • Наблюдатели: разовые (data/exists/child), постоянные и постоянно-рекурсивные (addWatch), снятие и проверка (removeWatches/checkWatches), корректная переустановка после reconnect.
  • ACL и аутентификация: getACL/setACL, схемы world/ip/digest, генерация digest-свёртки, addAuth, whoAmI.
  • Транзакции: атомарные multi построителем (create/delete/setData/check) с разбором частичных ошибок.
  • Кластер: динамическая реконфигурация ансамбля (reconfig), чтение /zookeeper/config.
  • Рецепты координации: распределённая блокировка, блокировка чтения/записи, выборы лидера, барьер и двойной барьер, очередь FIFO, атомарный счётчик, счётный семафор, кэш дерева путей.
  • Исключения: полный официальный перечень кодов KeeperException, сгруппированный по доменам (узел / сессия / сервер) для точного catch.
  • Асинхронность: неблокирующий AsyncClient с обещаниями (Promise) на встроенном цикле событий (stream_select, без зависимостей); контракт EventLoopInterface и адаптеры под циклы ReactPHP и Revolt/Amp; отложенная запись через очередь (OperationProducer/OperationConsumer, контракт OperationQueue) для развязки записи с латентностью сервера.

Сравнение с аналогами

Все таблицы ниже генерируются тест-скриптом benchmarks/compare.php. Функциональность и безопасность сравниваются с пятью аналогами: ext-zookeeper (C), swoole/ext-zookeeper, kafkiansky/zookeeper-php, Timandes/libzookeeper и Apache Curator. Производительность, нагрузка и память измеряются в реальных прогонах, но только против ext-zookeeper — единственного аналога, который воспроизводимо собирается и запускается в том же окружении; остальные столбцы функциональности/безопасности заполнены по их публичной документации (сентябрь 2026) и не измерялись. Apache Curator — Java-фреймворк, приведён как кросс-экосистемный эталон рецептов, поэтому графа «🏆 Победитель» определяется среди PHP-решений. Обновление: composer docs:compare (нужно загруженное расширение zookeeper), затем composer docs:sync-comparison. Обозначения: ✅ есть · ⚠️ частично/вне PHP-API · ❌ нет.

Функциональность (сгенерировано из проб)

Возможностьcloud-castle/zookeeperext-zookeeperswoole/zookeeperkafkianskyTimandesCurator (Java)🏆 Победитель
Чистый PHP (без C-расширения)наш · kafkiansky
Базовые операции с узлами (CRUD)все
Последовательные узлывсе
Container-узлынаш
TTL-узлынаш
Наблюдатели (watch)все
Постоянные/рекурсивные watch (addWatch)наш
ACL и аутентификациявсе
Атомарные multi-транзакциинаш
Реконфигурация ансамбля (reconfig)наш
Список эфемерных узлов (getEphemerals)⚠️наш
Число всех потомков (getAllChildrenNumber)⚠️наш
Сведения о сессии (whoAmI)⚠️наш
TLS / mTLS⚠️⚠️наш
Рецепты координации уровня Curatorнаш
Типизированные исключения KeeperException⚠️⚠️⚠️⚠️наш

Итог: среди PHP-клиентов наш поддерживает все 16 возможностей и не уступает ни в одной; Apache Curator (Java) сопоставим по функциям, но недоступен в PHP.

Пакет — суперсет по функционалу: чистый PHP + полный протокол + рецепты координации + TLS

  • типизированные исключения; он строго превосходит ext-zookeeper и не уступает ни в чём.

Производительность (сгенерировано из бенчмарков)

Оба клиента запускаются в одном процессе на PHP 8.5, против одного сервера ZooKeeper 3.9, с прогревом соединений и идентичными операциями; пропускная способность — медиана трёх раундов. Синхронный клиент: задержка ≈ сетевой round-trip.

Операциянаш pure-PHPext-zookeeper (C)🏆 Победитель
get3,949 оп/с1,783 оп/снаш ×2.21
getChildren4,422 оп/с3,542 оп/снаш ×1.25
exists4,762 оп/с3,569 оп/снаш ×1.33
set1,763 оп/с1,741 оп/снаш ×1.01

Наш чистый PHP-клиент не уступает C-расширению и заметно опережает его на точечных чтениях. Причина: синхронный API ext-zookeeper оборачивает асинхронное многопоточное ядро libzookeeper_mt межпоточной синхронизацией на каждый вызов, а наш клиент выполняет прямой блокирующий обмен без этой нагрузки.

Честные оговорки: абсолютные числа зависят от окружения (сняты на локальном низколатентном сервере) — устойчивая метрика это отношение при идентичных условиях; на канале с высокой задержкой обе реализации сходятся к сетевому пределу.

Под нагрузкой (сгенерировано из benchmarks/compare.php)

Устойчивый поток операций get с распределением задержки. На хвосте (p95/p99) синхронный клиент выигрывает особенно заметно — у C-обёртки на каждый вызов накладывается межпоточная синхронизация.

Метрика (поток get)наш pure-PHPext-zookeeper (C)🏆 Победитель
Пропускная способность4,977 оп/с1,907 оп/снаш ×2.61
Задержка p500.191 мс0.494 мснаш ×2.59
Задержка p950.252 мс0.689 мснаш ×2.73
Задержка p990.303 мс0.846 мснаш ×2.79

Память (сгенерировано из benchmarks/compare.php)

Прирост RSS процесса под нагрузкой, измеренный в изолированных подпроцессах — так сравнение честно учитывает и память C-библиотеки libzookeeper (потоки, буферы вне кучи PHP), а не только кучу PHP.

Метриканаш pure-PHPext-zookeeper (C)🏆 Победитель
Прирост RSS под нагрузкой (4000 операций)316 КБ320 КБнаш ×1.01

Дополнительно (без аналога): наш клиент даёт ≈237 КБ на подключение и рост +25.8 КБ за 60 000 операций (утечек нет).

Безопасность (сгенерировано из проб)

Проверяемые свойства: генерация digest-свёртки без хранения пароля открытым, строгая проверка TLS-сертификата по умолчанию, взаимный TLS из PHP, отсутствие нативной attack-surface.

Возможностьcloud-castle/zookeeperext-zookeeperswoole/zookeeperkafkianskyTimandesCurator (Java)🏆 Победитель
Генерация digest-свёртки (пароль не в открытом виде)⚠️наш
Строгая проверка TLS-сертификата по умолчанию⚠️⚠️⚠️наш
Взаимный TLS (клиентский сертификат) из PHP⚠️⚠️наш
Нет нативной attack-surface (чистый PHP)наш · kafkiansky

Аналоги в сравнении (по документации)

Столбцы таблиц выше, кроме измеренного ext-zookeeper, заполнены по публичной документации аналогов (сентябрь 2026) и в измерениях не участвуют:

  • swoole/ext-zookeeper — C-расширение поверх libzookeeper с корутинами Swoole; протокол без встроенных рецептов, требует расширения Swoole и сборки.
  • kafkiansky/zookeeper-php — чистый PHP, асинхронный (Revolt); базовые операции, watch и ACL, без рецептов/TLS/транзакций. Репозиторий архивирован (декабрь 2025).
  • Timandes/libzookeeper — C-обёртка над клиентской библиотекой ZooKeeper, только PHP 5–7, на актуальных версиях PHP не собирается.
  • Apache Curator — Java-фреймворк, канонический эталон координационных рецептов ZooKeeper; функционально сопоставим, но недоступен в PHP (приведён как кросс-экосистемный ориентир).

Среди PHP-решений ни один аналог не сочетает чистый PHP, полный протокол и рецепты координации одновременно — это делает только наш клиент.

Качество кода (измерено для нашего пакета)

Метрики нашего пакета сняты реальными прогонами инструментов; аналоги подобных данных не публикуют.

Метрикаcloud-castle/zookeeperаналоги
PHPStan max + strict-rulesн/д
Psalm errorLevel 1 + findUnusedCodeн/д
Покрытие тестами100%н/д
Мутационный MSI (Infection)100% (1588 мутантов)н/д
PSR-12 · PHPMD · Deptrac · Rectorн/д
Интеграция против реального сервера 3.5→3.9н/д

Плюсы и минусы

Плюсы:

  • Чистый PHP — не нужны PECL-расширения, C-библиотеки и сборка; работает везде, где есть PHP 8.1+.
  • Полный и корректный клиентский протокол, проверенный против реального сервера (3.5 → 3.9).
  • Рецепты координации уровня Curator из коробки.
  • Безопасность по умолчанию: TLS со строгой проверкой сертификата, digest без утечки пароля в ACL.
  • Строгая типизация, 100% покрытие тестами, полный стек статического анализа.
  • Два режима на выбор: простой синхронный клиент и неблокирующий асинхронный на встроенном цикле событий (без внешних зависимостей); async-клиент подключается к циклам ReactPHP и Revolt/Amp, а изменяющую запись можно развязать через очередь.

Минусы:

  • Библиотека моложе и менее распространена, чем PECL ext-zookeeper и Java-клиенты.

Когда применять

  • Приложения и сервисы на PHP, которым нужна координация (блокировки, выборы лидера, конфигурация, service discovery) без установки нативных расширений.
  • Среды, где нельзя ставить ext-zookeeper (управляемый хостинг, контейнеры без сборки).
  • Сценарии с требованием TLS к ZooKeeper и строгой проверкой сертификатов.
  • Когда важна прозрачность реализации протокола и полное покрытие тестами.

Разработка

composer install
composer check    # линтеры + статический анализ + тесты
composer fix      # автоисправления (Rector, PHP CS Fixer, PHPCBF)
composer ci       # полный CI-пайплайн локально

Интеграционные тесты запускаются против реального сервера при заданной переменной окружения:

docker run -d --name zk -p 2181:2181 zookeeper:3.9
ZOOKEEPER_TEST_SERVER=127.0.0.1:2181 composer test:integration

Полный список команд: composer run-script --list.

Документация

Лицензия

MIT © CloudCastle (alex-4-17@yandex.ru)

🇷🇺 Русский · 🇬🇧 English · 🇩🇪 Deutsch · 🇫🇷 Français · 🇪🇸 Español · 🇮🇹 Italiano