当前位置:首页 > PHP教程 > php高级应用 > 列表

PHP下用Swoole实现Actor并发模型的方法

发布:smiling 来源: PHP粉丝网  添加日期:2021-11-25 15:02:59 浏览: 评论:0 

这篇文章主要介绍了PHP下用Swoole实现Actor并发模型的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。

什么是Actor?

Actor对于PHPer来说,可能会比较陌生,写过Java的同学会比较熟悉,Java一直都有线程的概念(虽然PHP有Pthread,但不普及),它是一种非共享内存的并发模型,每个Actor内的数据独立存在,Actor之间通过消息传递的形式进行交互调度,且Actor是一种高度抽象化的编程模型,非常适合于游戏、硬件行业。

Swoole协程与信箱

得益于Swoole4.x,我们可以基于Swoole的协程与Channel快速实现一个信箱模式调度,模拟代码如下:

  1. use Swoole\Coroutine\Channel; 
  2. go(function (){ 
  3.   //创建十个信箱通道 
  4.   $mailBoxes = []; 
  5.   for ($i = 1;$i <= 10;$i++){ 
  6.     $mailBoxes[$i] = new Channel(16); 
  7.   } 
  8.   //模拟master 邮局调度,随机像一个信箱投递消息 
  9.   go(function ()use($mailBoxes){ 
  10.     while (1){ 
  11.       \co::sleep(2); 
  12.       $key = rand(1,10); 
  13.       ($mailBoxes[$key])->push(time()); 
  14.     } 
  15.   }); 
  16.   //模拟actor 实体消费 
  17.   for ($i = 1;$i <= 10;$i++){ 
  18.     go(function ()use($mailBoxes,$i){ 
  19.       while (1){ 
  20.         $msg = ($mailBoxes[$i])->pop(); 
  21.         echo "Actor {$i} recv msg : {$msg} \n"
  22.       } 
  23.     }); 
  24.   } 
  25. }); 

以上代码执行输出:

  1. php test.php 
  2. Actor 8 recv msg : 1559622691 
  3. Actor 10 recv msg : 1559622693 
  4. Actor 1 recv msg : 1559622695 
  5. Actor 5 recv msg : 1559622697 

协程通道每次在POP遇到无数据的时候,都会自动让出执行权(具体可以去看Swoole协程调度)

Actor库

基于上面的原理,我们实行了一个多进程分布的协程Actor库

composer require easyswoole/actor=2.x-dev

我们依赖dev库进行测试,生产可以自己依赖stable版本

进程关系

Easyswoole的Actor模型中,存在两组进程,一组是proxy进程,用来实现Actor对外服务,一组是worker进程,proxy进程与worker进程之间通过unixsock进行通讯,而Actor实例就均匀的分布worker之中。

样例代码

比如在一个聊天室中,我们可以定义一个房间模型。

  1. namespace EasySwoole\Actor\Test; 
  2.  
  3. use EasySwoole\Actor\AbstractActor; 
  4. use EasySwoole\Actor\ActorConfig; 
  5.  
  6. class RoomActor extends AbstractActor 
  7.   public static function configure(ActorConfig $actorConfig
  8.   { 
  9.     $actorConfig->setActorName('Room'); 
  10.   } 
  11.   public function onStart() 
  12.   { 
  13.     //每当一个RoomActor实体被创建的时候,都会执行该回调 
  14.     var_dump('room actor '.$this->actorId().' start'); 
  15.   } 
  16.   public function onMessage($msg
  17.   { 
  18.     //每当一个RoomActor实体收到外部消息的时候,都会执行该回调当 
  19.     var_dump('room actor '.$this->actorId().' onmessage: '.$msg); 
  20.     return 'reply at '.time(); 
  21.   } 
  22.   public function onExit($arg
  23.   {  
  24.     //每当一个RoomActor实体退出的时候,都会执行该回调 
  25.     var_dump('room actor '.$this->actorId().' exit at arg: '.$arg); 
  26.     return 'exit at '.time(); 
  27.   } 
  28.   protected function onException(\Throwable $throwable
  29.   { 
  30.     //每当一个RoomActor出现异常的时候,都会执行该回调 
  31.     var_dump($throwable->getMessage()); 
  32.   } 

在cli模式下创建一个Actor服务

  1. use EasySwoole\Actor\Actor; 
  2. use EasySwoole\Actor\Test\RoomActor; 
  3. use EasySwoole\Actor\ProxyProcess; 
  4.  
  5. Actor::getInstance()->register(RoomActor::class); 
  6. $list = Actor::getInstance()->generateProcess(); 
  7.  
  8. foreach ($list['proxy'as $proxy){ 
  9.   /** @var ProxyProcess $proxy */ 
  10.   $proxy->getProcess()->start(); 
  11. foreach ($list['worker'as $actors){ 
  12.   foreach ($actors as $actorProcess){ 
  13.     /** @var ProxyProcess $actorProcess */ 
  14.     $actorProcess->getProcess()->start(); 
  15.   } 
  16. while($ret = \Swoole\Process::wait()) { 
  17.   echo "PID={$ret['pid']}\n"

创建一个cli测试脚本

  1. use EasySwoole\Actor\Actor; 
  2. use EasySwoole\Actor\Test\RoomActor; 
  3. Actor::getInstance()->register(RoomActor::class); 
  4.  
  5. go(function (){ 
  6.   $actorId = RoomActor::client()->create('create arg1'); 
  7.   var_dump($actorId); 
  8.   \co::sleep(3); 
  9.   var_dump(RoomActor::client()->send($actorId,'this is msg')); 
  10.   \co::sleep(3); 
  11.   var_dump(RoomActor::client()->exit($actorId,'this is exit arg')); 
  12.   \co::sleep(3); 
  13.   RoomActor::client()->create('create arg2'); 
  14.   \co::sleep(3); 
  15.   RoomActor::client()->create('create arg3'); 
  16.   \co::sleep(3); 
  17.   var_dump(RoomActor::client()->sendAll('sendAll msg')); 
  18.   \co::sleep(3); 
  19.   var_dump(RoomActor::client()->status()); 
  20.   \co::sleep(3); 
  21.   var_dump(RoomActor::client()->exitAll('sendAll exit')); 
  22. }); 

以上代码执行结果如下:

服务端

  1. php test.php  
  2. string(40) "room actor 00101000000000000000001 start" 
  3. string(57) "room actor 00101000000000000000001 onmessage: this is msg" 
  4. string(64) "room actor 00101000000000000000001 exit at arg: this is exit arg" 
  5. string(40) "room actor 00101000000000000000002 start" 
  6. string(40) "room actor 00103000000000000000001 start" 
  7. string(57) "room actor 00101000000000000000002 onmessage: sendAll msg" 
  8. string(57) "room actor 00103000000000000000001 onmessage: sendAll msg" 
  9. string(60) "room actor 00101000000000000000002 exit at arg: sendAll exit" 
  10. string(60) "room actor 00103000000000000000001 exit at arg: sendAll exit" 

客户端

  1. php test2.php  
  2. string(23) "00101000000000000000001" 
  3. string(19) "reply at 1559623925" 
  4. string(18) "exit at 1559623928" 
  5. bool(true) 
  6. array(3) { 
  7.  [1]=> 
  8.  int(1) 
  9.  [2]=> 
  10.  int(0) 
  11.  [3]=> 
  12.  int(1) 
  13. bool(true) 

更多细节可以在EasySwoole项目官网得到文档支持 http://easyswoole.com/

喜欢EasySwoole项目的,可以给个star https://github.com/easy-swoole/easyswoole

Tags: Swoole Actor

分享到: