Сегодня я хочу рассказать, как вывести ваш PHP за рамки классического HTTP и научить его работать с долгоживущими процессами, real-time взаимодействием и IoT-устройствами. Мы разберем два мощных инструмента, Swoole и RoadRunner. И научимся создавать решения, которые раньше казались невозможными для PHP.
Почему PHP «недостаточно» для real-time и IoT?
Традиционно PHP работает в связке с веб-сервером (например, Apache или Nginx) по модели «запрос-ответ». Каждый HTTP-запрос запускает новый процесс, который умирает после отправки ответа. Это создает две проблемы:
- Невозможность долгосрочных операций. Например, поддержка WebSocket-соединений или обработка потоковых данных с IoT-датчиков.
- Накладные расходы. Постоянное создание и уничтожение процессов «съедает» ресурсы сервера.
Swoole и RoadRunner ломают эту парадигму, позволяя PHP-коду работать в режиме постоянного процесса. Давайте разберемся, как это работает.
Swoole: асинхронность и многопоточность в PHP
Swoole это расширение для PHP, написанное на C, которое добавляет поддержку асинхронного программирования, корутин и многопоточности. Оно работает как серверная платформа, позволяя запускать PHP-скрипты в фоновом режиме.
Пример 1: TCP-сервер на Swoole
Создадим простой сервер, который принимает TCP-соединения и отвечает на сообщения клиентов.
<?php
$server = new Swoole\Server('0.0.0.0', 9501);
// Обработка подключения
$server->on('connect', function ($server, $fd) {
echo "Клиент $fd подключен.\n";
});
// Обработка сообщения
$server->on('receive', function ($server, $fd, $fromId, $data) {
$server->send($fd, "Сервер получил: $data");
});
// Обработка отключения
$server->on('close', function ($server, $fd) {
echo "Клиент $fd отключен.\n";
});
$server->start();
Запустите скрипт:
php server.php
Теперь подключитесь к серверу через telnet:
telnet 127.0.0.1 9501
Что происходит? Сервер работает постоянно, обрабатывая множество подключений асинхронно. Это идеально для чатов, игровых серверов или IoT-шлюзов.
RoadRunner: PHP-воркеры на стероидах
RoadRunner это менеджер процессов, написанный на Go, который управляет PHP-воркерами. В отличие от Swoole, он не требует установки расширений в PHP.
Пример 2: запуск HTTP-сервера с RoadRunner
- Установите RoadRunner:
composer require spiral/roadrunner
./vendor/bin/rr get
- Создайте
worker.php:
<?php
require __DIR__ . '/vendor/autoload.php';
use Spiral\RoadRunner\Worker;
use Spiral\RoadRunner\Http\PSR7Worker;
$worker = Worker::create();
$psr7 = new PSR7Worker($worker);
while ($req = $psr7->waitRequest()) {
try {
$psr7->respond(new Response(200, [], "Hello, RoadRunner!"));
} catch (\Throwable $e) {
$psr7->getWorker()->error((string)$e);
}
}
- Запустите сервер:
./rr serve
RoadRunner создаст пул воркеров, которые обрабатывают запросы, сохраняя состояние между ними. Это полезно для задач, требующих долгих вычислений, например, обработки очередей.
Swoole или RoadRunner
| Параметр | Swoole | RoadRunner |
|---|---|---|
| Производительность | ~1.5M запросов/сек | ~120K запросов/сек |
| Архитектура | Расширение PHP на C | Go-приложение + PHP-воркеры |
| Асинхронность | Да (корутины) | Нет (синхронные воркеры) |
| Установка | Требует компиляции | composer require + бинарник |
| Поддержка протоколов | HTTP, WebSocket, TCP, UDP | HTTP, GRPC, Websockets (плагины) |
| Лучший сценарий | Real-time, высоконагруженные системы | Очереди, фоновые задачи |
Создание real-time чата на Swoole
Рассмотрим практический пример: чат с WebSocket.
<?php
$server = new Swoole\WebSocket\Server("0.0.0.0", 9502);
// Хранилище подключенных клиентов
$clients = new \SplObjectStorage();
$server->on('open', function ($server, $request) use ($clients) {
$clients->attach($request);
echo "Новое подключение: {$request->fd}\n";
});
$server->on('message', function ($server, $frame) use ($clients) {
foreach ($clients as $client) {
if ($client->fd !== $frame->fd) {
$server->push($client->fd, $frame->data);
}
}
});
$server->on('close', function ($server, $fd) use ($clients) {
$clients->detach($fd);
echo "Клиент $fd отключен\n";
});
$server->start();
Этот сервер поддерживает постоянные соединения и мгновенно пересылает сообщения между клиентами.
IoT-сервер на RoadRunner
Допустим, у нас есть датчики, отправляющие данные по HTTP. Создадим обработчик, который накапливает данные в Redis.
- Установите Redis:
composer require predis/predis
- Модифицируем
worker.php:
<?php
use Spiral\RoadRunner\Worker;
use Spiral\RoadRunner\Http\PSR7Worker;
use Predis\Client;
$redis = new Client(['host' => '127.0.0.1']);
while ($req = $psr7->waitRequest()) {
$sensorData = $req->getParsedBody();
$redis->lpush('sensor:data', json_encode($sensorData));
$psr7->respond(new Response(200, [], 'Данные сохранены'));
}
- Запустите RoadRunner с 10 воркерами:
./rr serve -w 10
Теперь сервер обрабатывает до 10 параллельных запросов от датчиков, сохраняя данные в Redis.
Рекомендации для разработчиков
- Выбирайте Swoole, если:
- Нужна максимальная производительность.
- Требуется асинхронная обработка (WebSocket, TCP).
- Готовы к работе с низкоуровневым API.
- Выбирайте RoadRunner, если:
- Хотите минимальной настройки.
- Работаете с HTTP/GRPC.
- Используете очереди (например, через плагин Jobs).
- Общие советы:
- Используйте супервизоры (Supervisor или systemd) для управления процессами.
- Мониторьте потребление памяти: долгоживущие процессы склонны к утечкам.
- Избегайте глобальных переменных — они сохраняются между запросами.
Swoole и RoadRunner ломают стереотипы о PHP, открывая двери в мир real-time и IoT. Больше не нужно переходить на Node.js или Go для таких задач. Используйте знакомый инструмент с новыми возможностями.
Поддержка автора осуществляется с помощью специальной формы ниже, предоставленной сервисом «ЮMoney». Все платёжные операции выполняются на защищённой странице сервиса, что обеспечивает их корректность и полную безопасность.


