開発前必読

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

一、Workerman開発と通常のPHP開発の違い

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

1、アプリケーション層プロトコルの違い

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

2、リクエストサイクルの差異

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

3、クラスと定数の重複定義を避ける

  • Workermanはコンパイル済みのPHPファイルをキャッシュするため、同じクラスや定数の定義ファイルを複数回require/includeしないようにしてください。ファイルを読み込む際は、require_once/include_onceを使用することをお勧めします。

4、シングルトンパターンの接続リソース解放に注意

  • Workermanはリクエストごとにグローバルオブジェクトやクラスの静的メンバーを解放しないため、データベースなどのシングルトンパターンでは、通常データベースインスタンス(内部にデータベースソケット接続を含む)をデータベースの静的メンバーに保持します。これにより、Workermanはプロセスのライフサイクル内でこのデータベースソケット接続を再利用します。注意が必要なのは、データベースサーバーが特定の接続が一定時間アクティブでない場合にソケット接続を自動的に閉じる可能性があるため、再度このデータベースインスタンスを使用する際にエラーが発生することです(エラーメッセージはmysql gone awayのようになります)。Workermanはデータベースクラスを提供しており、再接続機能がありますので、開発者はそれを直接利用できます。

5、exit、dieを使用しない

  • WorkermanはPHPコマンドラインモードで動作しているため、exit、dieの呼び出しは現在のプロセスを終了させることになります。子プロセスが終了すると直ちに同じ子プロセスが再作成されてサービスを続けますが、ビジネスに影響を与える可能性があります。

6、コードを変更した後はサービスを再起動する必要がある

Workermanは常駐メモリですが、PHPクラスおよび関数の定義が一度読み込まれた後は常駐メモリに残り、再度ディスクを読み込むことはありませんので、ビジネスコードを変更するたびにサービスを再起動する必要があります。

二、理解しておくべき基本概念

1、TCP輸送層プロトコル

TCPは接続指向で信頼性のあるIPに基づく輸送層プロトコルです。TCP輸送層プロトコルの重要な特徴は、TCPがデータストリームベースであることです。クライアントのリクエストは絶え間なくサーバーに送信され、サーバーが受け取るデータは完全なリクエストではない可能性があり、複数のリクエストが一緒に送信されることもあります。これにより、絶え間ないデータストリームの中で各リクエストの境界を区別する必要があります。アプリケーション層プロトコルは、リクエストの境界を定義する一連のルールを提供して、混乱を避ける役割を果たします。

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

アプリケーション層プロトコル(application layer protocol)は、異なる端末システム(クライアント、サーバー)上で実行されるアプリケーションプロセスがメッセージを相互に伝達する方法を定義します。例えば、HTTP、WebSocketなどがアプリケーション層プロトコルにあたります。例えば、次のようなシンプルなアプリケーション層プロトコルが考えられます{"module":"user","action":"getInfo","uid":456}\n"。このプロトコルは"\n"(ここで"\n"は改行を示します)でリクエストの終了をマークし、メッセージボディは文字列です。

3、短接続

短接続とは、通信双方がデータをやり取りする際に、接続を確立し、データ送信が完了した後に接続を切断することを指します。すなわち、各接続は一つのビジネスの送信を完了させるだけです。WEBサイトのHTTPサービスは一般的に短接続を使用します。

短接続アプリケーション開発については基本開発プロセスの章を参照してください

4、長接続

長接続は、1つの接続上で複数のデータパケットを連続で送信できることを指します。

注意:長接続アプリケーションはハートビートを追加する必要があります。さもなくば、接続は長期間アクティブでないためにルーターノードのファイアウォールによって切断される可能性があります。

長接続は、頻繁に操作が行われる点対点通信の状況で多く使用されます。各TCP接続は3ステップのハンドシェイクが必要で、これには時間がかかりますので、各操作が接続してから操作する場合、処理速度が大幅に低下します。これに対して長接続は、各操作が終了した後も接続を切断せず、次回の処理時には直接データパケットを送信できるため、TCP接続を確立する必要がありません。例えば、データベース接続は長接続を用いるべきで、短接続で頻繁に通信を行うとソケットエラーが発生し、頻繁なソケットの生成はリソースの無駄となります。

クライアントにデータをプッシュする必要がある場合、例えばチャットアプリ、リアルタイムゲーム、モバイルプッシュなどのアプリケーションは長接続が必要です。
長接続アプリケーション開発についてはGateway/Worker開発プロセスを参照してください

5、スムーズリスタート

一般的なリスタートプロセスは、すべてのプロセスを停止してから新しいサービスプロセスを作成することです。このプロセスの間、短時間にサービスを提供しているプロセスがないため、一時的にサービスが利用できなくなり、高い同時接続数の際にはリクエストの失敗につながることになります。

スムーズリスタートは、すべてのプロセスを一度に停止するのではなく、一つ一つのプロセスを停止して、停止するたびに新しいプロセスを再作成します。すべての古いプロセスが置き換えられるまでこの作業が行われます。

Workermanでスムーズリスタートを行うには、php your_file.php reloadコマンドを使用することで、サービス品質に影響を与えずにアプリケーションを更新できます。

注意:on{...}コールバックで読み込まれたファイルだけがスムーズリスタート後に自動的に更新され、起動スクリプト内で直接読み込まれたファイルや固定されたコードではreloadを実行しても自動的に更新されません。

三、主プロセスと子プロセスを区別する

コードが主プロセスで実行されているか、子プロセスで実行されているかを注意する必要があります。一般的に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();

注意: 主プロセス内でデータベース、memcache、redisなどの接続リソースを初期化しないでください。主プロセスで初期化した接続は、子プロセスによって自動的に継承される可能性があり(特にシングルトンを使用する場合)、すべてのプロセスが同じ接続を保持することになります。このため、サーバーからこの接続を介して返されるデータは複数のプロセスで読み取ることができ、データの混乱を引き起こすことになります。同様に、どのプロセスでも接続を閉じると(例えばデーモンモードで実行中の主プロセスが終了すると接続が閉じます)、すべての子プロセスの接続が同時に閉じられ、予測不能なエラー(例:mysql gone awayエラー)が発生します。

接続リソースはonWorkerStartの中で初期化することを推奨します。