Рубрики
Uncategorized

Метод реализации модели параллелизма субъектов с помощью Swool в PHP

Автор оригинала: David Wong.

Что такое Актер?

Актер, вероятно, незнаком ФПЕру. Те, кто написал Java, знакомы с ним. В Java всегда была концепция потоков (хотя в PHP есть Pthread, но он не популярен). Это параллельная модель неразделяемой памяти. Данные в каждом акторе существуют независимо. Участники взаимодействуют друг с другом посредством обмена сообщениями. Актер-это очень абстрактная модель программирования, которая очень подходит для индустрии игр и аппаратного обеспечения.

Сотрудничество со Свулом и почтовый ящик

Благодаря Swool 4.x мы можем быстро реализовать планирование режима почтового ящика на основе протокола и канала Вулфа. Код моделирования выглядит следующим образом:

use Swoole\Coroutine\Channel;
go(function (){
  // Create ten mailbox channels
  $mailBoxes = [];
  for ($i = 1;$i <= 10;$i++){
    $mailBoxes[$i] = new Channel(16);
  }
  // Simulate master post office scheduling, randomly delivering messages like a mailbox
  go(function ()use($mailBoxes){
    while (1){
      \co::sleep(2);
      $key = rand(1,10);
      ($mailBoxes[$key])->push(time());
    }
  });
  // Simulated Actor Entity Consumption
  for ($i = 1;$i <= 10;$i++){
    go(function ()use($mailBoxes,$i){
      while (1){
        $msg = ($mailBoxes[$i])->pop();
        echo "Actor {$i} recv msg : {$msg} \n";
      }
    });
  }
});

Приведенный выше код выполняет вывод:

php test.php Актер 8 recvmsg: 1559622691 Актер 10 recvmsg: 1559622693 Актер 1 recvmsg: 1559622695 Актер 5 recvmsg: 1559622697

Каждый раз, когда POP не обнаруживает данных, кооперативный канал автоматически отказывается от прав на выполнение (см., в частности, Совместное планирование Swool).

Библиотека актеров

Основываясь на вышеуказанных принципах, мы внедрили распределенную библиотеку кооперативных субъектов с несколькими процессами.

composer require easyswoole/actor=2.x-dev

Мы полагаемся на библиотеки разработчиков для тестирования, а производство может полагаться на стабильные версии самостоятельно.

Взаимосвязи процессов

В модели субъектов Easyswool есть две группы процессов: одна-прокси-процесс, который используется для реализации внешних служб субъектов, а другая-рабочий процесс. Прокси – процесс взаимодействует с рабочим процессом через unixsock, и экземпляр субъекта равномерно распределен в рабочем.

Пример кода

Например, в комнате чата мы можем определить модель комнаты.

namespace EasySwoole\Actor\Test;


use EasySwoole\Actor\AbstractActor;
use EasySwoole\Actor\ActorConfig;

class RoomActor extends AbstractActor
{
  public static function configure(ActorConfig $actorConfig)
  {
    $actorConfig->setActorName('Room');
  }
  public function onStart()
  {
    // Whenever a RoomActor entity is created, the callback is executed
    var_dump('room actor '.$this->actorId().' start');
  }
  public function onMessage($msg)
  {
    // Whenever a RoomActor entity receives an external message, the callback is executed.
    var_dump('room actor '.$this->actorId().' onmessage: '.$msg);
    return 'reply at '.time();
  }
  public function onExit($arg)
  { 
    // Whenever a RoomActor entity exits, the callback is executed
    var_dump('room actor '.$this->actorId().' exit at arg: '.$arg);
    return 'exit at '.time();
  }
  protected function onException(\Throwable $throwable)
  {
    // Whenever an exception occurs to a RoomActor, the callback is executed
    var_dump($throwable->getMessage());
  }
}

Создайте службу субъектов в режиме командной строки

use EasySwoole\Actor\Actor;
use EasySwoole\Actor\Test\RoomActor;
use EasySwoole\Actor\ProxyProcess;

Actor::getInstance()->register(RoomActor::class);
$list = Actor::getInstance()->generateProcess();

foreach ($list['proxy'] as $proxy){
  /** @var ProxyProcess $proxy */
  $proxy->getProcess()->start();
}
foreach ($list['worker'] as $actors){
  foreach ($actors as $actorProcess){
    /** @var ProxyProcess $actorProcess */
    $actorProcess->getProcess()->start();
  }
}
while($ret = \Swoole\Process::wait()) {
  echo "PID={$ret['pid']}\n";
}

Создайте тестовый сценарий cli

use EasySwoole\Actor\Actor;
use EasySwoole\Actor\Test\RoomActor;
Actor::getInstance()->register(RoomActor::class);

go(function (){
  $actorId = RoomActor::client()->create('create arg1');
  var_dump($actorId);
  \co::sleep(3);
  var_dump(RoomActor::client()->send($actorId,'this is msg'));
  \co::sleep(3);
  var_dump(RoomActor::client()->exit($actorId,'this is exit arg'));
  \co::sleep(3);
  RoomActor::client()->create('create arg2');
  \co::sleep(3);
  RoomActor::client()->create('create arg3');
  \co::sleep(3);
  var_dump(RoomActor::client()->sendAll('sendAll msg'));
  \co::sleep(3);
  var_dump(RoomActor::client()->status());
  \co::sleep(3);
  var_dump(RoomActor::client()->exitAll('sendAll exit'));
});

Результаты выполнения приведенного выше кода следующие:

Сервер

php test.php 
string(40) "room actor 00101000000000000000001 start"
string(57) "room actor 00101000000000000000001 onmessage: this is msg"
string(64) "room actor 00101000000000000000001 exit at arg: this is exit arg"
string(40) "room actor 00101000000000000000002 start"
string(40) "room actor 00103000000000000000001 start"
string(57) "room actor 00101000000000000000002 onmessage: sendAll msg"
string(57) "room actor 00103000000000000000001 onmessage: sendAll msg"
string(60) "room actor 00101000000000000000002 exit at arg: sendAll exit"
string(60) "room actor 00103000000000000000001 exit at arg: sendAll exit"

Клиент

php test2.php 
string(23) "00101000000000000000001"
string(19) "reply at 1559623925"
string(18) "exit at 1559623928"
bool(true)
array(3) {
 [1]=>
 int(1)
 [2]=>
 int(0)
 [3]=>
 int(1)
}
bool(true)

Более подробную информацию можно найти на веб-сайте проекта easyswool для поддержки документации по адресу http://easyswoole.com/

Если вам нравится проект easyswool, вы можете начать https://github.com/easy-swoole/easyswoole

Выше приведено все содержание этой статьи. Я надеюсь, что это будет полезно для изучения каждого, и я надеюсь, что вы будете больше поддерживать разработчика.