開発前に読むべき事項

Workermanを使用してアプリケーションを開発する場合、以下の内容を理解する必要があります。

1. Workerman開発と通常のPHP開発の違い

Workermanの開発と通常のPHP開発との大きな違いは、HTTPプロトコルに関連する変数関数が直接使用できないことです。それ以外には、Workermanの開発と通常のPHP開発には大きな違いはありません。

1.1 アプリケーションレイヤのプロトコルの違い

  • 通常のPHP開発は一般的にHTTPアプリケーション層プロトコルに基づいており、Webサーバーがプロトコルの解析を開発者のために完了しています
  • Workermanはさまざまなプロトコルをサポートしており、現在はHTTP、WebSocketなどのプロトコルが組み込まれています。Workermanでは、より簡単なカスタムプロトコル通信の使用を開発者に推奨しています

1.2 リクエストサイクルの違い

  • PHPはWebアプリケーションでのリクエストの後、すべての変数とリソースを解放します
  • Workermanで開発されたアプリケーションプログラムは、最初に読み込まれた後、常駐メモリになります。つまり、クラスの定義、グローバルオブジェクト、クラスの静的メンバーは解放されず、後続の再利用に便利です

1.3 クラスや定数の重複定義を回避する

  • Workermanはコンパイル済みのPHPファイルをキャッシュするため、同じクラスまたは定数の定義ファイルを複数回require/includeすることを避ける必要があります。ファイルをロードする際にはrequire_once/include_onceの使用をお勧めします。

1.4 シングルトンパターンの接続リソースの解放に注意する

  • Workermanはリクエスト後にグローバルオブジェクトやクラスの静的メンバーを解放しないため、データベースなどのシングルトンパターンでは、データベースインスタンス(内部にデータベースソケット接続が含まれる)をデータベースの静的メンバーに保存することがよくあります。これにより、Workermanはプロセスライフサイクル中にこのデータベースソケット接続を再利用します。注意すべきは、データベースサーバーが一定時間アクティブでない接続を検出した場合、このデータベースインスタンスを再利用しようとするとエラーが発生する可能性があります(エラーメッセージはmysql gone awayのようなものです)。Workermanは断続再接続の機能を備えたデータベースクラスを提供しており、開発者は直接使用できます。

1.5 exit、die文の使用を避ける

  • WorkermanはPHPコマンドラインモードで実行されるため、exit、die文を呼び出すと現在のプロセスが終了します。子プロセスは終了後すぐに同じ子プロセスを再作成しますが、それでもビジネスに影響を与える可能性があります。

1.6 コードの変更後は再起動が必要

Workermanは常駐メモリなので、PHPクラスや関数の定義は一度読み込んだ後に常駐メモリになり、ディスクから再度読み込まれることはありません。したがって、ビジネスコードを変更した後には、再起動する必要があります。

2. 理解すべき基本的な概念

2.1 TCP転送層プロトコル

TCPはIPに基づいた信頼性のある接続指向の転送層プロトコルです。TCP転送層プロトコルの重要な特徴は、TCPがデータストリームに基づいていることであり、クライアントのリクエストはサーバーに連続して送信され、サーバーが受信したデータは完全なリクエストでない場合があり、複数のリクエストが連続していることもあります。そのため、この継続的なデータストリームの中で各リクエストの境界を区別する必要があります。アプリケーション層プロトコルは主にリクエスト境界を定義する規則を避けるために存在します。

2.2 アプリケーション層プロトコル

アプリケーション層プロトコル(application layer protocol)は、異なるエンドシステム(クライアント、サーバー)で実行されるアプリケーションプロセス間のメッセージの受け渡し方法を定義します。例えば、HTTP、WebSocketはアプリケーション層プロトコルに属します。単純なアプリケーション層プロトコルの例としては、次のようになります: {"module":"user","action":"getInfo","uid":456}\n"。このプロトコルは"\n"(ここで"\n"は改行を表す)でリクエストの終了をマークし、メッセージ本体は文字列です。

2.3 ショートコネクション

ショートコネクションとは、通信する双方がデータを送受信する際に接続を確立し、データ送信が完了すると接続を切断することを指します。

ショートコネクションのアプリケーション開発については、基本的な開発手順の章を参照してください

2.4 ロングコネクション

ロングコネクションとは、1つの接続で複数のデータパケットを連続して送信することを指します。

注意:ロングコネクションアプリケーションにはハートビートが必要です。それを行わない場合、長時間アクティブでなかった接続は、ルーターノードやファイアウォールによって切断される可能性があります。

ロングコネクションは、頻繁な操作やP2P通信のような場合に使用されます。それぞれのTCP接続には3回のハンドシェイクが必要であり、時間がかかるため、各操作ごとに接続を確立してから操作を行うと処理速度が大幅に低下します。そのため、ロングコネクションは絶えず接続を切断せず、次回の処理時に直接データパケットを送信する必要があります。例えば、データベースの接続はロングコネクションを使用します。短い接続で頻繁に通信すると、ソケットエラーが発生し、頻繁なソケットの作成はリソースの浪費になります。

データをクライアントにアクティブにプッシュする必要がある場合、例えばチャットアプリ、リアルタイムゲーム、モバイルプッシュなどのアプリケーションはロングコネクションを必要とします。
ロングコネクションアプリケーションの開発については、Gateway/Workerの開発手順を参照してください

2.5 スムーズなリスタート

通常の再起動では、すべてのプロセスを停止し、その後新しいサービスプロセスを作成します。このプロセス中には一時的に外部サービスを提供しないため、高負荷状態でリクエストの失敗をもたらす可能性があります。

スムーズなリスタートはすべてのプロセスを一度に停止するのではなく、プロセスを1つずつ停止し、停止したプロセスの代わりに新しいプロセスをすぐに作成することであり、古いプロセスがすべて置き換えられるまで続けられます。

Workermanはphp your_file.php reloadコマンドを使用してスムーズなリスタートを行い、アプリケーションを更新することができます。

注意:on{...}コールバック内でロードされたファイルは、スムーズなリスタート後に自動的に更新されます。起動スクリプトで直接ロードされたファイルやハードコードされたコードの場合、再読み込みは自動的には行われません。

3. メインプロセスとサブプロセスの区別

コードがメインプロセスで実行されるかサブプロセスで実行されるかに注意する必要があります。一般に、Worker::runAll();の呼び出し前に実行されるコードはメインプロセスで実行され、onXXXコールバックで実行されるコードはサブプロセスに属します。Worker::runAll();の後ろに記述されたコードは常に実行されません。

以下はサンプルコードです。

use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';

// メインプロセスで実行
$tcp_worker = new Worker("tcp://0.0.0.0:2347");
// 設定はメインプロセスで実行
$tcp_worker->onMessage = function(TcpConnection $connection, $data)
{
    // サブプロセスで実行
    $connection->send('hello ' . $data);
};

Worker::runAll();

注意: メインプロセスでデータベース、メモリキャッシュ、Redisなどの接続リソースを初期化しないでください。メインプロセスで初期化された接続は、サブプロセスに自動的に継承される可能性があります(特にシングルトンを使用している場合)。全てのプロセスが同じ接続を持ち、サーバーから戻されたデータが複数のプロセスで読まれる可能性があり、データの混乱を引き起こす可能性があります。同様に、プロセスのいずれかが(デーモンモードで実行された場合、メインプロセスが終了するため)接続を閉じると、すべての子プロセスの接続が一緒に閉じられ、予測不可能なエラーが発生します。例えば、mysql gone awayエラーが発生する可能性があります。接続リソースの初期化は、onWorkerStartで行うことをお勧めします。