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


Posted in PHP onJune 12, 2019

什么是Actor?

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

Swoole协程与信箱

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

use Swoole\Coroutine\Channel;
go(function (){
  //创建十个信箱通道
  $mailBoxes = [];
  for ($i = 1;$i <= 10;$i++){
    $mailBoxes[$i] = new Channel(16);
  }
  //模拟master 邮局调度,随机像一个信箱投递消息
  go(function ()use($mailBoxes){
    while (1){
      \co::sleep(2);
      $key = rand(1,10);
      ($mailBoxes[$key])->push(time());
    }
  });
  //模拟actor 实体消费
  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
Actor 8 recv msg : 1559622691
Actor 10 recv msg : 1559622693
Actor 1 recv msg : 1559622695
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之中。

样例代码

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

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()
  {
    //每当一个RoomActor实体被创建的时候,都会执行该回调
    var_dump('room actor '.$this->actorId().' start');
  }
  public function onMessage($msg)
  {
    //每当一个RoomActor实体收到外部消息的时候,都会执行该回调当
    var_dump('room actor '.$this->actorId().' onmessage: '.$msg);
    return 'reply at '.time();
  }
  public function onExit($arg)
  { 
    //每当一个RoomActor实体退出的时候,都会执行该回调
    var_dump('room actor '.$this->actorId().' exit at arg: '.$arg);
    return 'exit at '.time();
  }
  protected function onException(\Throwable $throwable)
  {
    //每当一个RoomActor出现异常的时候,都会执行该回调
    var_dump($throwable->getMessage());
  }
}

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

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)

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

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

PHP 相关文章推荐
PHP.MVC的模板标签系统(一)
Sep 05 PHP
PHP新手上路(三)
Oct 09 PHP
PHP之生成GIF动画的实现方法
Jun 07 PHP
关于PHP模板Smarty的初级使用方法以及心得分享
Jun 21 PHP
php打开远程文件的方法和风险及解决方法
Nov 12 PHP
利用浏览器的Javascript控制台调试PHP程序
Jan 08 PHP
php实现倒计时效果
Dec 19 PHP
简单了解将WordPress中的工具栏移到底部的小技巧
Dec 31 PHP
PHP实现带重试功能的curl连接示例
Jul 28 PHP
php基于ob_start(ob_gzhandler)实现网页压缩功能的方法
Feb 18 PHP
PHP正则匹配到2个字符串之间的内容方法
Dec 24 PHP
PHP底层运行机制与工作原理详解
Jul 31 PHP
PHP面向对象类型约束用法分析
Jun 12 #PHP
PHP面向对象程序设计__tostring()和__invoke()用法分析
Jun 12 #PHP
php实现微信公众号创建自定义菜单功能的实例代码
Jun 11 #PHP
php转换上传word文件为PDF的方法【基于COM组件】
Jun 10 #PHP
php简单计算权重的方法示例【适合抽奖类应用】
Jun 10 #PHP
PHP7实现和CryptoJS的AES加密方式互通示例【AES-128-ECB加密】
Jun 08 #PHP
php使用scandir()函数扫描指定目录下所有文件示例
Jun 08 #PHP
You might like
如何使用php输出时间格式
2013/08/31 PHP
PHP获取MySql新增记录ID值的3种方法
2014/06/24 PHP
PHP使用PDO调用mssql存储过程的方法示例
2017/10/07 PHP
Laravel框架查询构造器 CURD操作示例
2019/09/04 PHP
JQuery 操作select标签实现代码
2010/05/14 Javascript
jquery下组织javascript代码(js函数化)
2010/08/25 Javascript
javascript字符串替换函数如何一次性全部替换掉
2015/10/30 Javascript
javascript实现全角转半角的方法
2016/01/23 Javascript
js replace(a,b)之替换字符串中所有指定字符的方法
2016/08/17 Javascript
使用smartupload组件实现jsp+jdbc上传下载文件实例解析
2017/01/05 Javascript
JS自定义函数实现时间戳转换成date的方法示例
2017/08/27 Javascript
深入理解Vue nextTick 机制
2018/04/28 Javascript
Vue.js 中取得后台原生HTML字符串 原样显示问题的解决方法
2018/06/10 Javascript
Vue实现动态添加或者删除对象和对象数组的操作方法
2018/09/21 Javascript
vue学习笔记五:在vue项目里面使用引入公共方法详解
2019/04/04 Javascript
Javascript三种字符串连接方式及性能比较
2019/05/28 Javascript
Vue 自定义指令功能完整实例
2019/09/17 Javascript
详解JavaScript类型判断的四种方法
2020/10/21 Javascript
[43:03]LGD vs Newbee 2019国际邀请赛小组赛 BO2 第一场 8.16
2019/08/19 DOTA
快速实现基于Python的微信聊天机器人示例代码
2017/03/03 Python
tensorflow TFRecords文件的生成和读取的方法
2018/02/06 Python
PyQt5实现下载进度条效果
2018/04/19 Python
python机器学习库scikit-learn:SVR的基本应用
2019/06/26 Python
Python 使用 docopt 解析json参数文件过程讲解
2019/08/13 Python
python3中的eval和exec的区别与联系
2019/10/10 Python
Python爬虫HTPP请求方法有哪些
2020/06/03 Python
详解pytorch tensor和ndarray转换相关总结
2020/09/03 Python
python-地图可视化组件folium的操作
2020/12/14 Python
6号汽车旅馆预订:Motel 6
2018/02/11 全球购物
应届毕业生自我评价分享
2013/12/15 职场文书
销售个人求职信范文
2014/04/28 职场文书
社会实践活动总结范文
2014/07/03 职场文书
党的群众路线教育实践活动个人整改方案
2014/10/25 职场文书
2014年房地产工作总结范文
2014/11/19 职场文书
董事长致辞
2015/07/29 职场文书
一次MySQL启动导致的事故实战记录
2021/09/15 MySQL