アクターシステムを起動してみよう
アクターモデルを利用するためには、アクターシステムを起動する必要があります。PhluxorではPhluxor\ActorSystem
を利用してアクターシステムを起動します。
Phluxor内部でSwooleのコルーチンを利用しているため、アクターシステムを起動する際はSwoole\Coroutine\nun
やSwoole\Coroutine\go
関数を利用して起動します。
<?php require 'vendor/autoload.php'; use function Swoole\Coroutine\go; use function Swoole\Coroutine\nun; run(function () { go(function () { $system = \Phluxor\ActorSystem::create(); $props = Phluxor\ActorSystem\Props::fromProducer( fn() => new Sample\HelloActor() ); $ref = $system->root()->spawn($props); $future = $system->root()->requestFuture( $ref, new \Sample\Message\HelloRequest('World'), 10 ); $response = $future->result(); var_dump($response->value()); }); });
Phluxor\ActorSystem::create
メソッドを利用してアクターシステムを生成します。アクターシステムはアクターを管理するためのシステムであり、アクターの生成やメッセージの送受信などを行うためのインターフェイスを提供します。
次にPhluxor\ActorSystem\Props::fromProducer
メソッドを利用してアクターのプロパティを生成します。このメソッドはアクターの生成方法を指定するためのメソッドであり、さまざまな方法でアクターを生成できます。ここではfn() => new Sample\HelloActor()
としてアクターを生成する関数を指定しています。
そしてspawn
メソッドを利用してアクターを生成します。
最初のアクターはルートアクターに生成する必要があり、ルートアクターは自動的にアクターシステムのトップレベルのアクターとなります。トップレベルのアクターはroot
メソッドを介して生成できます。
アクターが生成されるとアクターはアクターシステムに登録され、それぞれがMailboxとライフサイクルを持つようになり、メッセージの受信や処理を行うことができます。
Mailboxはアクターがメッセージを受信するためのキューのようなものであり、アクターはMailboxからメッセージを取り出して処理を行います。
これらのメッセージはSwooleのコルーチンを利用してノンブロッキングで処理されますので、アクターがメッセージを受信している間に他のアクターが処理を行うことができます。
アクターが生成されるとアクターの位置情報を示すPhluxor\ActorSystem\Ref
が返されます。
アクターモデルでは位置透過性が保証されており、ヒエラルキー構造の中でアクターは一意の位置情報、識別子を持ちます。この位置情報としてRef
を利用してアクターにメッセージを送信しMailboxにメッセージを送信します。
これはアクターがローカルであるかリモートであるかを意識することなく利用できるようになっており、アクターモデルの特徴のひとつでもありますが、これまでのPHPを使った一般的なプログラミングとは大きく異なる点かもしれません。
トップレベルのアクターにメッセージを送信する方法はいくつもありますが、ここではrequestFuture
メソッドを利用して非同期でメッセージを送信し、結果を取得する方法を利用しています。
Futureは未来の結果を取得するためのオブジェクトで、内部ではアクターと同様の存在として扱っており、他言語で利用されているFutureと同様に、並行処理の結果が必要になるところまで後回しにできます。
つまり、生成したアクターに\Sample\Message\HelloRequest
メッセージを送信し、10秒以内に\Sample\Message\HelloResponse
メッセージを取得する処理となります。10秒以内にメッセージを取得できない場合はタイムアウトを示すエラーが取得できますので、タイムアウトに対する処理も行うことができます。
このコードをコンソールなどから下記のコマンドを実行してみましょう。
$ php main.php
実行すると次のように表示されます。
下記のようにループを利用して100回Hello Worldを並行で実行することも可能です。
<?php // 省略 for ($i = 0; $i < 100; $i++) { $ref = $system->root()->spawn($props); $future = $system->root()->requestFuture( $ref, new \Sample\Message\HelloRequest('World'), 10 ); $response = $future->result(); var_dump($response->value()); }
簡単なコードですが、アクターモデルの仕組みを利用することで非同期処理や並行処理を意識することなく実装できるようになります。