cloud-castle / zookeeper
Чистый PHP-клиент Apache ZooKeeper без ext-zookeeper: полный клиентский протокол (3.5-3.9) и рецепты координации.
Requires
- php: >=8.1
Requires (Dev)
- deptrac/deptrac: ^3.0 || ^4.0
- ergebnis/composer-normalize: ^2.45
- friendsofphp/php-cs-fixer: ^3.75
- icanhazstring/composer-unused: ^0.9
- infection/infection: ^0.29 || ^0.33
- php-parallel-lint/php-parallel-lint: ^1.4
- phpmd/phpmd: ^2.15
- phpstan/extension-installer: ^1.4
- phpstan/phpstan: ^1.12 || ^2.1
- phpstan/phpstan-deprecation-rules: ^1.2 || ^2.0
- phpstan/phpstan-phpunit: ^1.4 || ^2.0
- phpstan/phpstan-strict-rules: ^1.6 || ^2.0
- phpunit/phpunit: ^10.5 || ^11.5
- psalm/plugin-phpunit: ^0.19 || ^0.20
- react/event-loop: ^1.6
- rector/rector: ^1.2 || ^2.0
- revolt/event-loop: ^1.0
- roave/security-advisories: dev-latest
- squizlabs/php_codesniffer: ^3.12 || ^4.0
- vimeo/psalm: ^6.0
- webmozart/assert: ^1.11
Suggests
- react/event-loop: Запуск неблокирующего клиента на цикле событий ReactPHP (адаптер CloudCastle\Zookeeper\Async\Adapter\ReactEventLoop)
- revolt/event-loop: Запуск неблокирующего клиента на цикле событий Revolt/Amp (адаптер CloudCastle\Zookeeper\Async\Adapter\RevoltEventLoop)
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-03 05:31:12 UTC
README
🇷🇺 Русский · 🇬🇧 English · 🇩🇪 Deutsch · 🇫🇷 Français · 🇪🇸 Español · 🇮🇹 Italiano
CloudCastle Zookeeper
Чистый PHP-клиент Apache ZooKeeper без расширений: полный и корректный клиентский протокол (совместимость с сервером 3.5 → 3.9) и рецепты координации уровня Apache Curator.
Библиотека реализует бинарный протокол 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/zookeeper | ext-zookeeper | swoole/zookeeper | kafkiansky | Timandes | Curator (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-PHP | ext-zookeeper (C) | 🏆 Победитель |
|---|---|---|---|
| get | 3,949 оп/с | 1,783 оп/с | наш ×2.21 |
| getChildren | 4,422 оп/с | 3,542 оп/с | наш ×1.25 |
| exists | 4,762 оп/с | 3,569 оп/с | наш ×1.33 |
| set | 1,763 оп/с | 1,741 оп/с | наш ×1.01 |
Наш чистый PHP-клиент не уступает C-расширению и заметно опережает его на точечных чтениях.
Причина: синхронный API ext-zookeeper оборачивает асинхронное многопоточное ядро
libzookeeper_mt межпоточной синхронизацией на каждый вызов, а наш клиент выполняет прямой
блокирующий обмен без этой нагрузки.
Честные оговорки: абсолютные числа зависят от окружения (сняты на локальном низколатентном сервере) — устойчивая метрика это отношение при идентичных условиях; на канале с высокой задержкой обе реализации сходятся к сетевому пределу.
Под нагрузкой (сгенерировано из benchmarks/compare.php)
Устойчивый поток операций get с распределением задержки. На хвосте (p95/p99) синхронный
клиент выигрывает особенно заметно — у C-обёртки на каждый вызов накладывается межпоточная
синхронизация.
| Метрика (поток get) | наш pure-PHP | ext-zookeeper (C) | 🏆 Победитель |
|---|---|---|---|
| Пропускная способность | 4,977 оп/с | 1,907 оп/с | наш ×2.61 |
| Задержка p50 | 0.191 мс | 0.494 мс | наш ×2.59 |
| Задержка p95 | 0.252 мс | 0.689 мс | наш ×2.73 |
| Задержка p99 | 0.303 мс | 0.846 мс | наш ×2.79 |
Память (сгенерировано из benchmarks/compare.php)
Прирост RSS процесса под нагрузкой, измеренный в изолированных подпроцессах — так
сравнение честно учитывает и память C-библиотеки libzookeeper (потоки, буферы вне кучи PHP),
а не только кучу PHP.
| Метрика | наш pure-PHP | ext-zookeeper (C) | 🏆 Победитель |
|---|---|---|---|
| Прирост RSS под нагрузкой (4000 операций) | 316 КБ | 320 КБ | наш ×1.01 |
Дополнительно (без аналога): наш клиент даёт ≈237 КБ на подключение и рост +25.8 КБ за 60 000 операций (утечек нет).
Безопасность (сгенерировано из проб)
Проверяемые свойства: генерация digest-свёртки без хранения пароля открытым, строгая проверка TLS-сертификата по умолчанию, взаимный TLS из PHP, отсутствие нативной attack-surface.
| Возможность | cloud-castle/zookeeper | ext-zookeeper | swoole/zookeeper | kafkiansky | Timandes | Curator (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.
Документация
- Репозиторий: https://gitverse.ru/cloud-castle/zookeeper
- История изменений: CHANGELOG.md
- Как внести вклад: CONTRIBUTING.md
- Кодекс поведения: CODE_OF_CONDUCT.md
- Политика безопасности: SECURITY.md
Лицензия
MIT © CloudCastle (alex-4-17@yandex.ru)
🇷🇺 Русский · 🇬🇧 English · 🇩🇪 Deutsch · 🇫🇷 Français · 🇪🇸 Español · 🇮🇹 Italiano