PHP Socket Programming Handbook 読書メモ PHPによるReactorパターンの実装

php_socket_programming_handbook


https://github.com/phpsphb/book-examples/blob/master/reactor/reactor.php

<?php

namespace SocketProgrammingHandbook;

final class StreamSelectLoop {
    private $readStreams = [];
    private $readHandlers = [];
    private $writeStreams = [];
    private $writeHandlers = [];
    private $running = true;

    function addReadStream($stream, callable $handler) {
        if (empty($this->readStreams[(int) $stream])) {
            $this->readStreams[(int) $stream] = $stream;
            $this->readHandlers[(int) $stream] = $handler;
        }
    }

    function addWriteStream($stream, callable $handler) {
        if (empty($this->writeStreams[(int) $stream])) {
            $this->writeStreams[(int) $stream] = $stream;
            $this->writeHandlers[(int) $stream] = $handler;
        }
    }

    function removeReadStream($stream) {
        unset($this->readStreams[(int) $stream]);
    }

    function removeWriteStream($stream) {
        unset($this->writeStreams[(int) $stream]);
    }

    function removeStream($stream) {
        $this->removeReadStream($stream);
        $this->removeWriteStream($stream);
    }

    /**
     * Runs the event loop, which blocks the current process. Make sure you do
     * any necessary setup before running this.
     */
    function run() {
        while ($this->running) {
            $read = $this->readStreams;
            $write = $this->writeStreams;
            $except = null;

            if ($read || $write) {
                @stream_select($read, $write, $except, 0, 100);

                foreach ($read as $stream) {
                    $this->readHandlers[(int) $stream]($stream);
                }

                foreach ($write as $stream) {
                    $this->writeHandlers[(int) $stream]($stream);
                }
            } else {
                usleep(100);
            }
        }
    }
}

Reactorパターンを使うべき場合:

  • 複数のクライアントコネクションを同時に処理する必要があるが、サーバで複雑なコネクションマネジメントをしたくない場合
  • メモリが限られていて、マシン上で1つのサーバプロセスしか実行できない場合
  • forkが遅かったりメモリ消費量が多いプラットフォーム(主にWindows)で実行する必要がある場合
  • クライアントを処理している最中にコードがクラッシュしても、新しいサーバプロセスを再起動すれば住む場合

Reactorパターンを使うべきでない場合:

  • forkが高速でメモリ効率が良いプラットフォーム(主にUNIX、Linux)で実行する場合
  • 本当に並列処理を行う必要がある場合(Reactorは完全な並列ではない)
  • メモリが潤沢にある場合

PHPライブラリ

PHPによるReactorパターンの実装として著名なものにReactPHPライブラリがある。
また、stream_selectselect(2)に依存しているが、より高パフォーマンスのシステムコールにepoll(2)kqueue(2)などがある。
PHPでepoll(2)等を使いたい場合、これらのシステムコールを抽象化したライブラリであるlibeventのPHP向けインタフェースLibevent等を使用することができる。

コメントを残す

コメントを残す