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 缓存函数代码
Aug 27 PHP
自动把纯文本转换成Web页面的php代码
Aug 27 PHP
PHP CURL模拟GET及POST函数代码
Apr 25 PHP
浏览器关闭后,能继续执行的php函数(ignore_user_abort)
Aug 01 PHP
php制作中间带自己定义图片二维码的方法
Jan 27 PHP
PHP中对于浮点型的数据需要用不同的方法解决
Mar 11 PHP
smarty中js的调用方法示例
Oct 27 PHP
完美利用Yii2微信后台开发的系列总结
Jul 18 PHP
PHP 中 DOMDocument保存xml时中文出现乱码问题的解决方案
Sep 19 PHP
PHP不使用递归的无限级分类简单实例
Nov 05 PHP
php使用gd2绘制基本图形示例(直线、圆、正方形)
Feb 15 PHP
PHP实现cookie跨域session共享的方法分析
Aug 23 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函数
2006/10/09 PHP
用php实现批量查询清除一句话后门的代码
2008/01/20 PHP
PHP与SQL注入攻击防范小技巧
2011/09/16 PHP
php实现的中秋博饼游戏之掷骰子并输出结果功能详解
2017/11/06 PHP
实例讲解PHP表单
2020/06/10 PHP
$.ajax json数据传递方法
2008/11/19 Javascript
jQuery JSON的解析方式分享
2011/04/05 Javascript
js 获取坐标 通过JS得到当前焦点(鼠标)的坐标属性
2013/01/04 Javascript
jquery和雅虎的yql服务实现天气预报服务示例
2014/02/08 Javascript
jQuery on()方法使用技巧详解
2015/04/16 Javascript
Js制作点击输入框时默认文字消失的效果
2015/09/05 Javascript
11种ASP连接数据库的方法
2015/09/18 Javascript
JavaScript登录验证码的实现
2016/10/27 Javascript
js+html制作简单验证码
2017/02/16 Javascript
vue实现分页组件
2020/06/16 Javascript
使用 Vue 实现一个虚拟列表的方法
2019/08/20 Javascript
vue下axios拦截器token刷新机制的实例代码
2020/01/17 Javascript
javascript设计模式 ? 工厂模式原理与应用实例分析
2020/04/09 Javascript
[01:03:22]LGD vs OG 2018国际邀请赛淘汰赛BO3 第一场 8.25
2018/08/29 DOTA
利用python获取某年中每个月的第一天和最后一天
2016/12/15 Python
python获取当前用户的主目录路径方法(推荐)
2017/01/12 Python
基于python元祖与字典与集合的粗浅认识
2017/08/23 Python
pycharm下打开、执行并调试scrapy爬虫程序的方法
2017/11/29 Python
python双端队列原理、实现与使用方法分析
2019/11/27 Python
python函数不定长参数使用方法解析
2019/12/14 Python
python3 正则表达式基础廖雪峰
2020/03/25 Python
Python爬虫之Selenium多窗口切换的实现
2020/12/04 Python
澳大利亚吉他在线:Artist Guitars
2017/03/30 全球购物
Dockers鞋官网:Dockers Shoes
2018/11/13 全球购物
意大利网上药房:Farmacia 33
2020/01/27 全球购物
opencv实现图像几何变换
2021/03/24 Python
传媒专业推荐信范文
2013/11/23 职场文书
医院辞职信范文
2014/01/17 职场文书
六一儿童节演讲稿
2014/05/23 职场文书
费城故事观后感
2015/06/10 职场文书
Python PIL按比例裁剪图片
2022/05/11 Python