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
php中cookie的作用域
Mar 27 PHP
PHP小技巧之函数重载
Jun 02 PHP
Yii框架关联查询with用法分析
Dec 02 PHP
PHP连接MSSQL时nvarchar字段长度被截断为255的解决方法
Dec 25 PHP
PHP超牛逼无限极分类生成树方法
May 11 PHP
浅谈ThinkPHP中initialize和construct的区别
Apr 01 PHP
php 二维数组快速排序算法的实现代码
Oct 17 PHP
搜索附近的人PHP实现代码
Feb 11 PHP
laravel框架语言包拓展实现方法分析
Nov 22 PHP
PHP pthreads v3下同步处理synchronized用法示例
Feb 21 PHP
php ActiveMQ的安装与使用方法图文教程
Feb 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
BBS(php &amp; mysql)完整版(一)
2006/10/09 PHP
php分页示例代码
2007/03/19 PHP
在php MYSQL中插入当前时间
2008/04/06 PHP
php Smarty初体验二 获取配置信息
2011/08/08 PHP
php删除字符串末尾子字符,删除开始字符,删除两端字符(实现代码)
2013/06/27 PHP
php中有关合并某一字段键值相同的数组合并的改进
2015/03/10 PHP
php版微信公众账号第三方管理工具开发简明教程
2016/09/23 PHP
thinkPHP框架实现图像裁剪、缩放、加水印的方法
2017/03/14 PHP
php实现记事本案例
2020/10/20 PHP
jQuery 源码分析笔记(2) 变量列表
2011/05/28 Javascript
JQuery插件Style定制化方法的分析与比较
2012/05/03 Javascript
jQuery之DOM对象和jQuery对象的转换与区别分析
2015/01/08 Javascript
浅谈JSON.parse()和JSON.stringify()
2015/07/14 Javascript
jquery UI Datepicker时间控件的使用方法(终结版)
2015/11/07 Javascript
详解js实现线段交点的三种算法
2016/08/09 Javascript
在JS中a标签加入单击事件屏蔽href跳转页面
2016/12/16 Javascript
vue-cli的工程模板与构建工具详解
2018/09/27 Javascript
深入浅出了解Node.js Streams
2019/05/27 Javascript
微信小程序分享小程序码的生成(带参数)以及参数的获取
2020/03/25 Javascript
利用PHP实现递归删除链表元素的方法示例
2020/10/23 Javascript
vue使用echarts画组织结构图
2021/02/06 Vue.js
nestjs中异常过滤器Exceptionfilter的具体使用
2021/02/07 Javascript
python中global与nonlocal比较
2014/11/21 Python
windows下pycharm安装、创建文件、配置默认模板
2018/07/31 Python
Python格式化字符串f-string概览(小结)
2019/06/18 Python
python自动化实现登录获取图片验证码功能
2019/11/20 Python
Python Http请求json解析库用法解析
2020/11/28 Python
夏威夷灵感服装及配饰:Reyn Spooner
2018/09/18 全球购物
Ruby如何定义一个类
2012/10/08 面试题
土木工程建筑专业毕业生求职信
2013/10/21 职场文书
县委常委班子对照检查材料思想汇报
2014/09/28 职场文书
运输公司工作总结
2015/08/11 职场文书
2016年大学生党员公开承诺书
2016/03/24 职场文书
手残删除python之后的补救方法
2021/06/26 Python
Python爬取用户观影数据并分析用户与电影之间的隐藏信息!
2021/06/29 Python
微软官方消息,在 2023 年 4 月 11 日之后微软将不再为 Office 2013 和 Skype for Business 2015 提供安全更新
2022/04/21 数码科技