Архітектура Async\Pool
Ця стаття описує внутрішню будову універсального пулу ресурсів. Якщо вас цікавить посібник з використання, дивіться Async\Pool. Для PDO-специфічного шару дивіться Архітектура PDO Pool.
Структура даних
Пул реалізований у два шари: публічна ABI-структура в ядрі PHP та розширена внутрішня структура в розширенні async.
Два шляхи створення
Пул можна створити з PHP-коду (через конструктор Async\Pool)
або з C-розширення (через внутрішній API).
| Шлях | Функція | Зворотні виклики | Використовується |
|---|---|---|---|
| PHP | zend_async_pool_create() |
zend_fcall_t* (PHP callable) |
Код користувача |
| C API | zend_async_pool_create_internal() |
вказівники на функції | PDO, інші розширення |
Різниця полягає в handler_flags. Коли прапорець встановлено, пул викликає C-функцію напряму,
минаючи накладні витрати на виклик PHP callable через zend_call_function().
Acquire: отримання ресурсу
Очікування ресурсу
Коли всі ресурси зайняті і досягнуто max_size, корутина призупиняється
через ZEND_ASYNC_SUSPEND(). Механізм очікування аналогічний каналам:
- Створюється структура
zend_async_pool_waiter_t - Очікувач додається до FIFO-черги
waiters - Реєструється зворотний виклик для пробудження
- Якщо встановлено таймаут – реєструється таймер
ZEND_ASYNC_SUSPEND()– корутина віддає управління
Пробудження відбувається, коли інша корутина викликає release().
Release: повернення ресурсу
Healthcheck: фонове моніторування
Якщо healthcheckInterval > 0, при створенні пулу запускається періодичний таймер.
Таймер інтегрований з реактором через ZEND_ASYNC_NEW_TIMER_EVENT.
Healthcheck перевіряє тільки вільні ресурси. Зайняті ресурси не зачіпаються.
Якщо після видалення мертвих ресурсів загальна кількість падає нижче min, пул створює заміни.
Кільцевий буфер
Вільні ресурси зберігаються в кільцевому буфері – ring buffer з фіксованою ємністю. Початкова ємність – 8 елементів, розширюється за потребою.
Операції push та pop виконуються за O(1). Буфер використовує два вказівники (head та tail),
що забезпечує ефективне додавання та вилучення ресурсів без переміщення елементів.
Інтеграція з системою подій
Пул успадковує zend_async_event_t та реалізує повний набір обробників подій:
| Обробник | Призначення |
|---|---|
add_callback |
Реєстрація зворотного виклику (для очікувачів) |
del_callback |
Видалення зворотного виклику |
start |
Запуск події (NOP) |
stop |
Зупинка події (NOP) |
dispose |
Повне очищення: звільнення пам’яті, знищення зворотних викликів |
Це дозволяє:
- Призупиняти та відновлювати корутини через зворотні виклики подій
- Інтегрувати таймер healthcheck з реактором
- Коректно звільняти ресурси через disposal подій
Збирання сміття
PHP-обгортка пулу (async_pool_obj_t) реалізує власний get_gc,
який реєструє всі ресурси з idle-буфера як GC-корені.
Це запобігає передчасному збиранню вільних ресурсів,
на які немає явних посилань з PHP-коду.
Circuit Breaker
Пул реалізує інтерфейс CircuitBreaker з трьома станами:
Переходи можуть бути ручними або автоматичними через CircuitBreakerStrategy:
reportSuccess()викликається при успішномуrelease(ресурс пройшовbeforeRelease)reportFailure()викликається, колиbeforeReleaseповернувfalse- Стратегія вирішує, коли перемикати стани
Close: завершення роботи пулу
При закритті пулу:
- Подія пулу позначається як CLOSED
- Таймер healthcheck зупиняється
- Усі очікуючі корутини пробуджуються з
PoolException - Усі вільні ресурси знищуються через
destructor - Зайняті ресурси продовжують жити – вони будуть знищені при
release
C API для розширень
Розширення (PDO, Redis тощо) використовують пул через макроси:
| Макрос | Функція |
|---|---|
ZEND_ASYNC_NEW_POOL(...) |
Створити пул із C-зворотними викликами |
ZEND_ASYNC_NEW_POOL_OBJ(pool) |
Створити PHP-обгортку для пулу |
ZEND_ASYNC_POOL_ACQUIRE(pool, result, timeout) |
Отримати ресурс |
ZEND_ASYNC_POOL_RELEASE(pool, resource) |
Повернути ресурс |
ZEND_ASYNC_POOL_CLOSE(pool) |
Закрити пул |
Усі макроси викликають вказівники на функції, зареєстровані розширенням async під час завантаження. Це забезпечує ізоляцію: ядро PHP не залежить від конкретної реалізації пулу.
Послідовність: повний цикл Acquire-Release
Що далі?
- Async\Pool: Посібник – як використовувати пул
- Архітектура PDO Pool – PDO-специфічний шар
- Корутини – як працюють корутини