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面向对象法则
Feb 23 PHP
php cookie使用方法学习笔记分享
Nov 07 PHP
PHP解析RSS的方法
Mar 05 PHP
php程序内部post数据的方法
Mar 31 PHP
PHP中Session和Cookie是如何操作的
Oct 10 PHP
PHP使用Memcache时模拟命名空间及缓存失效问题的解决
Feb 27 PHP
Yii列表定义与使用分页方法小结(3种方法)
Jul 15 PHP
PHP判断表达式中括号是否匹配的简单实例
Oct 22 PHP
PHP 文件上传后端处理实用技巧方法
Jan 06 PHP
完美解决在ThinkPHP控制器中命名空间的问题
May 05 PHP
iis 7下安装laravel 5.4环境的方法教程
Jun 14 PHP
php经典趣味算法实例代码
Jan 21 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
zend Framework中的Layout(模块化得布局)详解
2013/06/28 PHP
PHP变量内存分配问题记录整理
2013/11/27 PHP
php常用字符串处理函数实例分析
2014/11/22 PHP
php版阿里云OSS图片上传类详解
2016/12/01 PHP
PHP面向对象中new self()与 new static()的区别浅析
2017/08/17 PHP
Laravel 默认邮箱登录改成用户名登录的实现方法
2019/08/12 PHP
laravel 获取某个查询的查询SQL语句方法
2019/10/12 PHP
JavaScript 在网页上单击鼠标的地方显示层及关闭层
2012/12/30 Javascript
原生js做的手风琴效果的导航菜单
2013/11/08 Javascript
asp.net刷新本页面的六种方法总结
2014/01/07 Javascript
第六篇Bootstrap表格样式介绍
2016/06/21 Javascript
JS插件plupload.js实现多图上传并显示进度条
2016/11/29 Javascript
微信小程序 获取session_key和openid的实例
2017/08/17 Javascript
JavaScript程序设计高级算法之动态规划实例分析
2017/11/24 Javascript
three.js实现3D模型展示的示例代码
2017/12/31 Javascript
JavaScript中window和document用法详解
2020/07/28 Javascript
vue动态合并单元格并添加小计合计功能示例
2020/11/26 Vue.js
[46:59]完美世界DOTA2联赛PWL S2 GXR vs Ink 第二场 11.19
2020/11/20 DOTA
使用python将mdb数据库文件导入postgresql数据库示例
2014/02/17 Python
使用python解析xml成对应的html示例分享
2014/04/02 Python
python基础入门学习笔记(Python环境搭建)
2016/01/13 Python
Python 多线程抓取图片效率对比
2016/02/27 Python
浅谈Python2.6和Python3.0中八进制数字表示的区别
2017/04/28 Python
Python使用progressbar模块实现的显示进度条功能
2018/05/31 Python
Python中的groupby分组功能的实例代码
2018/07/11 Python
在unittest中使用 logging 模块记录测试数据的方法
2018/11/30 Python
python 字符串常用函数详解
2019/09/11 Python
django admin管理工具自定义时间区间筛选器DateRangeFilter介绍
2020/05/19 Python
酒店管理毕业生自荐信
2013/10/24 职场文书
担保书格式及范文
2014/04/01 职场文书
文明倡议书范文
2014/04/15 职场文书
医师定期考核实施方案
2014/05/07 职场文书
师范生免费教育协议书范本
2014/10/09 职场文书
校园文化艺术节开幕词
2016/03/04 职场文书
Python采集爬取京东商品信息和评论并存入MySQL
2022/04/12 Python
阿里云国际版 使用Nginx作为HTTPS转发代理服务器
2022/05/11 Servers