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 5.0对象模型深度探索之类的静态成员
Mar 27 PHP
PHP下escape解码函数的实现方法
Aug 08 PHP
php删除与复制文件夹及其文件夹下所有文件的实现代码
Jan 23 PHP
采用memcache在web集群中实现session的同步会话
Jul 05 PHP
2014年最新推荐的10款 PHP 开发框架
Aug 01 PHP
php中base_convert()进制数字转换函数实例
Nov 20 PHP
PHP简单选择排序算法实例
Jan 26 PHP
Yii2实现log输出到file及database的方法
Nov 12 PHP
PHP Mysqli 常用代码集合
Nov 12 PHP
PHP isset()与empty()的使用区别详解
Feb 10 PHP
Thinkphp5行为使用方法汇总
Dec 21 PHP
laravel5.0在linux下解决.htaccess无效和去除index.php的问题
Oct 16 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
用cookies来跟踪识别用户
2006/10/09 PHP
php微信公众号开发模式详解
2016/11/28 PHP
php ajax confirm 删除实例详解
2019/03/06 PHP
基于js与flash实现的网站flv视频播放插件代码
2014/10/14 Javascript
JavaScript中的parse()方法使用简介
2015/06/12 Javascript
JS实现的另类手风琴效果网页内容切换代码
2015/09/08 Javascript
Bootstrap表格和栅格分页实例详解
2016/05/20 Javascript
jQuery的extend方法【三种】
2016/12/14 Javascript
Vue组件开发技巧总结
2018/03/04 Javascript
Vue动态控制input的disabled属性的方法
2018/06/26 Javascript
layui框架与SSM前后台交互的方法
2019/09/12 Javascript
JS实现动态星空背景效果
2019/11/01 Javascript
多线程爬虫批量下载pcgame图片url 保存为xml的实现代码
2013/01/17 Python
Linux下使用python调用top命令获得CPU利用率
2015/03/10 Python
Python中字典映射类型的学习教程
2015/08/20 Python
使用Python解析JSON数据的基本方法
2015/10/15 Python
Python队列的定义与使用方法示例
2017/06/24 Python
浅谈python函数之作用域(python3.5)
2017/10/27 Python
详解Django的CSRF认证实现
2018/10/09 Python
Python + selenium + requests实现12306全自动抢票及验证码破解加自动点击功能
2018/11/23 Python
python 实现视频流下载保存MP4的方法
2019/01/09 Python
Python使用ctypes调用C/C++的方法
2019/01/29 Python
基于html5 canvas做批改作业的小插件
2020/05/20 HTML / CSS
稀有和绝版书籍:Biblio.com
2017/02/02 全球购物
TOWER London官网:鞋子、靴子、运动鞋等
2019/07/14 全球购物
在加拿大在线租赁和购买电子游戏:Game Access
2019/09/02 全球购物
Wallis官网:英国女装零售商
2020/01/21 全球购物
俄罗斯厨房产品购物网站:COOK HOUSE
2021/03/15 全球购物
META-INF文件夹中的MANIFEST.MF的作用
2016/06/21 面试题
入党积极分子学习两会心得体会范文
2014/03/17 职场文书
政府门卫岗位职责
2014/04/29 职场文书
保护环境建议书300字
2014/05/13 职场文书
小学三好学生事迹材料
2014/08/15 职场文书
高校自主招生自荐信2015
2015/03/04 职场文书
低门槛开发iOS、Android、小程序应用的前端框架详解
2021/10/16 Javascript
Python实战实现爬取天气数据并完成可视化分析详解
2022/06/16 Python