Вся правда об асинхронном PHP: Fibers, epoll и PHP 8.6
Я годами работал с Laravel. Я использовал синхронный PHP. Приходит запрос, процесс выполняется, и отправляется ответ. Мне никогда не требовался асинхронный код.
Затем я прочитал о новом PHP 8.6 Polling API. Это изменило мой взгляд на вещи.
Вот что я узнал о том, как асинхронность работает «под капотом».
Проблема ввода-вывода (I/O)
Когда вы вызываете API, ваш PHP-процесс ждет.
Пример:
$response = Http::get('https://api.example.com');
Если этот вызов занимает 300 мс, ваш процессор ничего не делает в течение 300 мс. Он находится в состоянии ожидания. Это блокирующий ввод-вывод (blocking I/O).
Если у вас есть три вызова API:
- API A: 300 мс
- API B: 400 мс
- API C: 200 мс
Общее время последовательно: 900 мс. Общее время асинхронно: 400 мс (время самого медленного вызова).
Асинхронность позволяет вашему процессу выполнять другую работу, пока ожидаются данные.
Select против epoll
Чтобы реализовать асинхронность, нужно знать, в каком сокете готовы данные.
1. select()
PHP использует stream_select() начиная с 4-й версии. Он работает, запрашивая ядро следить за списком сокетов.
Проблема: каждый раз, когда приходят данные, вам приходится сканировать весь список заново. Это «налог на повторное сканирование» (rescan tax). Также существует ограничение — около 1024 соединений.
2. epoll (Linux) / kqueue (macOS)
Это функции ядра. Вместо сканирования списка ядро поддерживает список готовых сокетов (ready-list). Оно сообщает вам только о тех конкретных сокетах, которые готовы к работе. Это масштабируется до тысяч соединений без дополнительных затрат.
epoll — это не функция PHP. Это функция Linux. Go, Rust и Node.js — все они используют её.
Fibers: кнопка паузы
PHP 8.1 представил Fibers. Я думал, что Fibers будут «просыпаться» сами по себе. Но это не так.
Fiber — это как поставленное на паузу видео. Оно остается на паузе, пока кто-нибудь не вызовет $fiber->resume().
Event Loop — это просто фрагмент PHP-кода, который решает, когда вызвать resume().
Для асинхронного ввода-вывода (Async I/O) требуются три составляющие:
- Пауза: Fibers (ядро PHP 8.1)
- Решение: Event Loop (обычный PHP-код)
- Знание: Kernel Polling (epoll/kqueue)
До PHP 8.6 у PHP были части «Пауза» и «Решение», но часть «Знание» полагалась на старый select() или медленные расширения на C.
PHP 8.6 устраняет этот пробел. Он внедряет нативный Polling API непосредственно в ядро. Теперь PHP может использовать epoll или kqueue напрямую без дополнительных расширений.
Итог
Если вы используете Laravel с PHP-FPM, вам сегодня не нужно ничего менять.
Но поймите одно: асинхронность — это не магия. Это просто умный способ управления временем ожидания.
Перестаньте просто потреблять код. Запустите простой скрипт. Сломайте его. Только так можно по-настоящему научиться.
