cluster

Этот пакет предоставляет инструменты для запуска нескольких процессов PHP, которые могут прослушивать один и тот же порт, например, несколько экземпляров HTTP-сервера прослушивают один и тот же порт. Совместное использование портов достигается с помощью SO_REUSEPORT , где это возможно, и передачей сокетов в противном случае.

Кроме того,этот пакет предоставляет механизм для изящного перезапуска таких процессов с нулевым временем простоя.Весь кластер может быть перезапущен вручную или отказавшие рабочие будут автоматически перезапущены процессом наблюдателя кластера.

Usage

Вместо того, чтобы запускать процессы с помощью php server.php , приложения с поддержкой кластера запускаются с помощью php vendor/bin/cluster server.php .

Мягкий перезапуск можно инициировать, отправив кластерному процессу сигнал USR1 .

Приложениям с поддержкой кластеров достаточно заменить несколько компонентов на те,которые предоставляет этот пакет.

Создание сервера

Amp\Socket\Server::listen() обычно используется для создания экземпляра Amp\Socket\Server для приема клиентских подключений.

Вместо этого Amp\Cluster\Cluster::listen() возвращает обещание, которое разрешается в экземпляр Amp\Socket\Server . Cluster::listen() позволяет совместно использовать адреса и порты для нескольких процессов (или потоков) PHP.

Logging

Записи журнала могут быть отправлены наблюдателю кластера для записи в один поток с помощью Amp\Cluster\Cluster::createLogHandler() . Этот обработчик может быть присоединен к экземпляру Monolog\Logger .

Process Termination

Часто сигналы используются для корректного завершения работы сервера, когда процесс получает сигнал SIGTERM .Вместо Amp\Loop::onSignal() приложения с поддержкой кластера должны использовать Amp\Cluster\Cluster::onTerminate() для определения функций, которые обрабатывают корректное завершение работы при получении SIGINT или SIGTERM .

Горячая перезагрузка в IntelliJ/PHPStorm

Наблюдатели за файлами IntelliJ можно использовать в качестве триггера для автоматической отправки сигнала USR1 в процесс-наблюдатель кластера при каждом сохранении файла. Вам нужно написать PID-файл, используя --pid-file /path/to/file.pid при запуске кластера, а затем настроить файловый наблюдатель в настройках, используя следующие настройки:

  • Program: bash
  • Аргументы: -c "if test -f ~/test-cluster.pid; then kill -10 $(cat ~/test-cluster.pid); fi"

Пример HTTP-сервера

Пример ниже (который можно найти в каталоге примеров как simple-http-server.php ) использует amphp/http-server для создания HTTP-сервера, который можно запускать в любом количестве процессов одновременно.

<?php

require \dirname(__DIR__) . "/vendor/autoload.php";

// Для этого примера необходимо установить ampphp/http-server.

use Amp\ByteStream;
use Amp\Cluster\Cluster;
use Amp\Http\Server\Request;
use Amp\Http\Server\RequestHandler\CallableRequestHandler;
use Amp\Http\Server\Response;
use Amp\Http\Server\Server;
use Amp\Http\Status;
use Amp\Log\ConsoleFormatter;
use Amp\Log\StreamHandler;
use Amp\Promise;
use Monolog\Logger;

// Запуск с использованием примеров bin/cluster/simple-http-server.php
// Протестируйте с помощью браузера, подключившись к http://localhost:8080/

Amp\Loop::run(function () {
    // Создаем серверы сокетов, используя метод Cluster::listen(), чтобы совместно использовать порты между процессами.
    // Cluster::listen() возвращает промис, поэтому передайте массив, чтобы дождаться разрешения всех промисов.
    $sockets = yield [
        Cluster::listen("0.0.0.0:8080"),
        Cluster::listen("[::]:8080"),
    ];

    // Создание обработчика журнала таким образом позволяет запускать скрипт в кластере или автономно.
    if (Cluster::isWorker()) {
        $handler = Cluster::createLogHandler();
    } else {
        $handler = new StreamHandler(ByteStream\getStdout());
        $handler->setFormatter(new ConsoleFormatter);
    }

    $logger = new Logger('worker-' . Cluster::getId());
    $logger->pushHandler($handler);

    // Настраиваем простой обработчик запросов.
    $server = new Server($sockets, new CallableRequestHandler(function (Request $request): Response {
        return new Response(Status::OK, [
            "content-type" => "text/plain; charset=utf-8"
        ], "Hello, World!");
    }), $logger);

    // Запускаем HTTP-сервер
    yield $server->start();

    // Остановить сервер, когда рабочий завершится.
    Cluster::onTerminate(function () use ($server): Promise {
        return $server->stop();
    });
});