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 相关文章推荐
搜索引擎技术核心揭密
Oct 09 PHP
URL Rewrite的设置方法
Jan 02 PHP
PHP 调试工具Debug Tools
Apr 30 PHP
php中截取中文字符串的代码小结
Jul 17 PHP
PHP计数器的实现代码
Jun 08 PHP
php class中public,private,protected的区别以及实例分析
Jun 18 PHP
ThinkPHP 404页面的设置方法
Jan 14 PHP
php+mysql实现无限分类实例详解
Jan 15 PHP
WordPress中创建用户角色的相关PHP函数使用详解
Dec 25 PHP
WordPress中用于创建以及获取侧边栏的PHP函数讲解
Dec 29 PHP
ThinkPHP下表单令牌错误与解决方法分析
May 20 PHP
php实现往pdf中加数字签名操作示例【附源码下载】
Aug 07 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简单的伪原创程序,配合商城采集用的
2010/10/12 PHP
PHP中利用sleep函数实现定时执行功能实现代码
2016/08/25 PHP
PHP实现微信图片上传到服务器的方法示例
2017/06/29 PHP
php源码的使用方法讲解
2019/09/26 PHP
ASP中用Join和Array,可以加快字符连接速度的代码
2007/08/22 Javascript
JavaScript中获取HTML元素值的三种方法
2016/06/20 Javascript
canvas雪花效果核心代码分享
2017/02/19 Javascript
angularjs过滤器--filter与ng-repeat配合有奇效
2017/04/20 Javascript
详解从新建vue项目到引入组件Element的方法
2017/08/29 Javascript
原生JS+HTML5实现跟随鼠标一起流动的粒子动画效果
2018/05/03 Javascript
垃圾回收器的相关知识点总结
2018/05/13 Javascript
vue-resource请求实现http登录拦截或者路由拦截的方法
2018/07/11 Javascript
Node.js命令行/批处理中如何更改Linux用户密码浅析
2018/07/22 Javascript
node使用Mongoose类库实现简单的增删改查
2018/11/08 Javascript
bootstrap table实现合并单元格效果
2018/12/24 Javascript
JavaScript学习笔记之DOM基础操作实例小结
2019/01/09 Javascript
解决vue单页面修改样式无法覆盖问题
2019/08/05 Javascript
微信小程序激励式视频广告组件使用详解
2019/12/06 Javascript
python实现将英文单词表示的数字转换成阿拉伯数字的方法
2015/07/02 Python
Python开启线程,在函数中开线程的实例
2019/02/22 Python
python Django框架实现web端分页呈现数据
2019/10/31 Python
将matplotlib绘图嵌入pyqt的方法示例
2020/01/08 Python
Django模板之基本的 for 循环 和 List内容的显示方式
2020/03/31 Python
Python+OpenCV检测灯光亮点的实现方法
2020/11/02 Python
美国本地交易和折扣网站:LocalFlavor.com
2017/10/26 全球购物
英国大码女性时装零售商:Evans
2018/08/29 全球购物
财产保全担保书范文
2014/04/01 职场文书
《望庐山瀑布》教学反思
2014/04/22 职场文书
爱护花草树木的标语
2014/06/11 职场文书
班级团队活动方案
2014/08/14 职场文书
校园学雷锋广播稿
2014/10/08 职场文书
介绍信样本
2015/01/31 职场文书
企业培训简报范文
2015/07/20 职场文书
停车场管理制度范本
2015/08/05 职场文书
php实例化对象的实例方法
2021/11/17 PHP
使用RedisTemplat实现简单的分布式锁
2021/11/20 Redis