2017 © Pedro Peláez
 

library cluster

Cluster

image

amphp/cluster

Cluster

  • Thursday, May 3, 2018
  • by kelunik
  • Repository
  • 5 Watchers
  • 10 Stars
  • 256 Installations
  • PHP
  • 0 Dependents
  • 0 Suggesters
  • 2 Forks
  • 3 Open issues
  • 1 Versions
  • 39 % Grown

The README.md

cluster , (*1)

This package provides tools to run multiple PHP processes that can listen on the same port, such as multiple HTTP server instances listening on the same port. Port-sharing is achieved with SO_REUSEPORT where available and socket transfers otherwise., (*2)

Additionally, this package provides a mechanism to gracefully restart such processes with zero downtime. The entire cluster may be manually restarted or failed workers will automatically be respawned by the cluster watcher process., (*3)

Usage

Instead of starting the processes with php server.php, cluster-enabled applications are started with php vendor/bin/cluster server.php., (*4)

A graceful restart can be initiated by sending a USR1 signal to the cluster process., (*5)

Cluster-enabled applications need only replace a few components with those provided by this package., (*6)

Creating a Server

Amp\Socket\Server::listen() is usually used to create an instance of Amp\Socket\Server for accepting client connections., (*7)

Instead, Amp\Cluster\Cluster::listen() returns a promise that resolves to an instance of Amp\Socket\Server. Cluster::listen() allows addresses and ports to be shared across multiple PHP processes (or threads)., (*8)

Logging

Log entries may be send to the cluster watcher to be logged to a single stream by using Amp\Cluster\Cluster::createLogHandler(). This handler can be attached to a Monolog\Logger instance., (*9)

Process Termination

Often signals are used to gracefully shutdown a server when a SIGTERM signal is received by the process. Instead of Amp\Loop::onSignal(), cluster-enable applications should use Amp\Cluster\Cluster::onTerminate() to define functions that handle graceful shutdown when SIGINT or SIGTERM is received., (*10)

Hot Reload in IntelliJ / PHPStorm

IntelliJ's file watchers can be used as trigger to send the USR1 signal to the cluster's watcher process automatically on every file save. You need to write a PID file using --pid-file /path/to/file.pid when starting the cluster and then setup a file watcher in the settings using the following settings:, (*11)

  • Program: bash
  • Arguments: -c "if test -f ~/test-cluster.pid; then kill -10 $(cat ~/test-cluster.pid); fi"

Example HTTP Server

The example below (which can be found in the examples directory as simple-http-server.php) uses amphp/http-server to create an HTTP server that can be run in any number of processes simultaneously., (*12)

<?php

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

// This example requires amphp/http-server to be installed.

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;

// Run using bin/cluster examples/simple-http-server.php
// Test using your browser by connecting to http://localhost:8080/

Amp\Loop::run(function () {
    // Create socket servers using the Cluster::listen() method to share ports across processes.
    // Cluster::listen() returns a promise, so yield the array to wait for all promises to resolve.
    $sockets = yield [
        Cluster::listen("0.0.0.0:8080"),
        Cluster::listen("[::]:8080"),
    ];

    // Creating a log handler in this way allows the script to be run in a cluster or standalone.
    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);

    // Set up a simple request 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);

    // Start the HTTP server
    yield $server->start();

    // Stop the server when the worker is terminated.
    Cluster::onTerminate(function () use ($server): Promise {
        return $server->stop();
    });
});

The Versions