【コマンドUNITクラスの実装】
はじめに
このクラスでは、クライアントからのリクエストやサーバーの通知から始まる一連の流れを1つのコマンド処理とみなし、それをサーバーコンテンツを構成する1つの要素としてイベントハンドラを構成していきます。
そしてプロトコルUNITクラスと同様に、イベントハンドラをキューとUNITという単位に分けたモデルで専用のコマンドを使ってスキャルフォールディングできます。
プロトコルUNITクラスとは異なり、予約されているキューはありませんので、実装するイベントに合わせてキューとUNITを自由に構成できます。
※キューとUNITの詳細については▶イベントハンドラについてのページを参照。
コマンドUNITクラスを生成するコマンドは以下の通り。
コマンドを実行する事で
以降では生成されたファイルの内容を見ていきます。
そしてプロトコルUNITクラスと同様に、イベントハンドラをキューとUNITという単位に分けたモデルで専用のコマンドを使ってスキャルフォールディングできます。
プロトコルUNITクラスとは異なり、予約されているキューはありませんので、実装するイベントに合わせてキューとUNITを自由に構成できます。
※キューとUNITの詳細については▶イベントハンドラについてのページを参照。
コマンドUNITクラスを生成するコマンドは以下の通り。
> php worker craft:command CommandForTest [success] コマンドUNITクラスの生成に成功しました (CommandForTest) [success] コマンドUNITのキュー名Enumの生成に成功しました (CommandForTestQueueEnum) [success] コマンドUNITのステータス名Enumの生成に成功しました (CommandForTestStatusEnum)
コマンドを実行する事で
app/CommandUnits
の場所に以下3つのファイルが生成されます。- ・CommandForTest
-
IEntryUnits
インターフェイスがimplementsされたコマンドUNITクラスです。
ここにコマンド処理のイベントハンドラを実装します。
- ・CommandForTestQueueEnum
-
ここにコマンド処理のキュー(イベント)名を定義します。
- ・CommandForTestStatusEnum
-
ここにコマンド処理のステータス(UNIT)名を定義します。
以降では生成されたファイルの内容を見ていきます。
キュー名の定義
CommandForTestQueueEnum.php
のファイルは、イベントに対応するキュー名を定義するEnumファイルです。これらのキュー名はコマンドUNITクラスで定義されるイベントごとのUNITの集合を特定するためのものです。
今回のケースでは
CommandForTest
クラス内でキューとUNITの紐づけを行います。紐づけの方法は以下の>> コマンドUNITクラスの実装の項で説明しています。
ステータス名の定義
CommandForTestStatusEnum.php
のファイルは、コマンドUNITのステータス名を定義するEnumファイルです。コマンドUNITのステータス名であらかじめ予約されているものは
StatusEnum::START
だけで、生成されたEnumファイルにはその内容が代入されています。それ以外のステータス名は自由に定義する事ができます。
定義済みのステータス名は以下の通り。
- CommandForTestStatusEnum::START
-
同じキューに登録されているUNITの集合のうち一番最初に実行されるステータスです。
UNITは必ずSTARTステータスから始まるルールになっています。
CommandForTest
クラス内でUNITとステータス名の紐づけを行います。紐づけの方法は以下の>> コマンドUNITクラスの実装の項で説明しています。
※ここで定義したステータス名は異なるキューで再利用が可能です。
コマンドUNITクラスの実装
CommandForTest.php
のファイルにはUNIT定義を含めた各種メソッドが実装されています。各メソッドの仕様と実装例は以下の通り。
➤キューリストの取得
フレームワークは以下のメソッドをコールして必要なキューのリストを登録します。【メソッド】getQueueList(): array 【パラメータ】なし 【戻り値】array - キュー名のリスト
例えば▶イベントハンドラについてのページでご紹介したチャットサーバーを例に挙げると、以下のように実装します。
protected const QUEUE_LIST = [ CommandForTestQueueEnum::CHAT_MESSAGE->value, // チャットメッセージを処理するキュー CommandForTestQueueEnum::PRIVATE_MESSAGE->value // プライベートメッセージを処理するキュー ]; public function getQueueList(): array { return (array)static::QUEUE_LIST; }
➤ステータスUNITリストの取得
以下のメソッドがフレームワーク内部でコールされる事によって、キューとUNITの紐づけが登録されます。【メソッド】getUnitList(string $p_que): array 【パラメータ】 $p_que - string - 必須 - キュー名 【戻り値】array - キューごとのUNIT集合のリスト(連想配列)
フレームワーク内部では
getQueueList
メソッドで取得したキュー名を元に、getUnitList
メソッドがコールされるため、引数にはキュー名が渡されます。例えば
CHAT_MESSAGE
キューとPRIVATE_MESSAGE
キューにそれぞれ2つずつUNITの集合を登録する場合、以下のように引数で与えられたキュー名に対応するリストを返す必要があります。public function getUnitList(string $p_que): array { $ret = []; // CHAT_MESSAGEキューのUNIT集合を登録 if($p_que === CommandForTestQueueEnum::CHAT_MESSAGE->value) { // STARTステータスとUNIT(getLogWrite)の紐づけ $ret[] = [ 'status' => CommandForTestStatusEnum::START->value, 'unit' => $this->getLogWrite() ]; // SENDステータスとUNIT(getChatSend)の紐づけ $ret[] = [ 'status' => CommandForTestStatusEnum::SEND->value, 'unit' => $this->getChatSend() ]; } // PRIVATE_MESSAGEキューのUNIT集合を登録 if($p_que === CommandForTestQueueEnum::PRIVATE_MESSAGE->value) { // STARTステータスとUNIT(getLogWrite)の紐づけ $ret[] = [ 'status' => CommandForTestStatusEnum::START->value, 'unit' => $this->getLogWrite() ]; // SENDステータスとUNIT(getPrivateSend)の紐づけ $ret[] = [ 'status' => CommandForTestStatusEnum::SEND->value, 'unit' => $this->getPrivateSend() ]; } . . . return $ret; }
※STARTステータスで紐づけを行っているUNIT(getLogWrite)は再利用UNITとして登録しているので内容は同じものです。
➤ステータスUNITの実装
ここではgetUnitList
メソッド内で紐づけたUNITを定義します。【メソッド】<任意のメソッド名>(): Closure|string|null 【パラメータ】なし 【戻り値】 Closure|string - ステータスUNITの定義: Closure パラメータ: $p_param - SocketManagerParameter - 必須 - UNITパラメータクラスのインスタンス 各イベントハンドラで共通の引数として使用されるインスタンス。 SocketManagerParameterクラスを継承した拡張クラスを指定する事も可能。 戻り値: string|null - 遷移先がある場合: string 遷移先のキュー名 - 遷移先がない(終了する)場合: null - ステータスUNITの定義: string ヘルパー関数などの関数名
以下では
CHAT_MESSAGE
キューのUNIT集合とPRIVATE_MESSAGE
キューのUNIT集合を再利用UNITを使って実装した例をご紹介します。再利用UNITの詳細については▶イベントハンドラについての説明をご覧ください。
【チャットメッセージ】 { "message" => <メッセージ> } 【プライベートメッセージ】 { "message" => <メッセージ>, "cid" => <相手先クライアントの接続ID> }
- 【CHAT_MESSAGEキューのUNIT集合】
-
ここでは、受信したデータをディスクリプタ(クライアント接続子)へ保存し、メッセージをログファイルへ記録する再利用UNITを経由してから自身を除く全員へ配信する例をご紹介しています。
protected function getLogWrite() { return function(ParameterForTest $p_param): ?string { // 現在のキューを取得 $que = $p_param->getQueueName(); // 受信データを取得 $recv_data = $p_param->getRecvData(); // メッセージをディスクリプタへ保存 $p_param->setTempBuff(['message_data' => $recv_data]); // 自身のキューがCHAT_MESSAGEかどうかを判定してからログファイルへ記録 $key = 'private_message'; // プライベートメッセージ if($que === CommandForTestQueueEnum::CHAT_MESSAGE->value) { $key = 'chat_message'; // チャットメッセージ } $p_param->logWriter('info', [$key => $recv_data['message']]); // SENDステータス(getChatSend or getPrivateSend)のUNITへ遷移 return CommandForTestStatusEnum::SEND->value; }; } protected function getChatSend() { return function(ParameterForTest $p_param): ?string { // ディスクリプタからメッセージを取得 $message_data = $p_param->getTempBuff(['message_data']); // 自身を除く全員へメッセージを配信 $p_param->setSendStackAll($message_data['message'], true); // nullを返して終了する return null; }; }
- 【PRIVATE_MESSAGEキューのUNIT集合】
-
ここでは
CHAT_MESSAGE
キューに登録されている再利用UNIT(getLogWrite)と同じものを使うので、プライベートメッセージを送信するUNITだけを実装しています。
protected function getPrivateSend() { return function(ParameterForTest $p_param): ?string { // ディスクリプタからメッセージを取得 $message_data = $p_param->getTempBuff(['message_data']); // 宛先を指定してメッセージを配信 $p_param->setSendStack($message_data['message'], $message_data['cid']); // nullを返して終了する return null; }; }
今回使用した
$p_param->getTempBuff()
や$p_param->setTempBuff()
のメソッドはディスクリプタ(クライアント接続子)へ対する読み込み/書き込みを行います。つまりUNITを跨ったグローバルデータではあるものの、各クライアント個別のデータとして利用できます。
メソッド(UNIT)内で使用している
$p_param->setSendStack()
や$p_param->setSendStackAll()
を使って送信データを設定すると、送信スタックエリアを通じてプロトコルUNITに引き渡され、自動で送信されます。※UNITパラメータクラスには▶UNITパラメータクラスのページで使用した
ParameterForTest
を指定しています。おわりに
生成されたクラスのインスタンスは、メイン処理クラス内で
複数のコマンドUNITクラスやメイン処理クラスを用意している場合は、サーバーの実装内容によって最適なインスタンスを動的、あるいは静的に適用する事で柔軟なサーバー構築が可能になります。
$manager->setCommandUnits()
メソッドに引き渡す事で適用されます。複数のコマンドUNITクラスやメイン処理クラスを用意している場合は、サーバーの実装内容によって最適なインスタンスを動的、あるいは静的に適用する事で柔軟なサーバー構築が可能になります。