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+mysql开源XNA 聚合程序发布 下载
Jul 13 PHP
php session 错误
May 21 PHP
MySQL连接数超过限制的解决方法
Jul 17 PHP
phpmyadmin显示utf8_general_ci中文乱码的问题终级篇
Apr 08 PHP
PHP删除HTMl标签的三种解决方法
Jun 30 PHP
PHP仿博客园 个人博客(1) 数据库与界面设计
Jul 05 PHP
php生成缩略图示例代码分享(使用gd库实现)
Jan 20 PHP
php遍历目录输出目录及其下的所有文件示例
Jan 27 PHP
使用php清除bom示例
Mar 03 PHP
Yii中实现处理前后台登录的新方法
Dec 28 PHP
ThinkPHP中html:list标签用法分析
Jan 09 PHP
Thinkphp和Bootstrap结合打造个性的分页样式(推荐)
Aug 01 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实现加强版加密解密类实例
2015/07/29 PHP
PHP基于迭代实现文件夹复制、删除、查看大小等操作的方法
2017/08/11 PHP
JsEasy简介 JsEasy是什么?与下载
2007/03/07 Javascript
Div自动滚动到末尾的代码
2008/10/26 Javascript
基于JQuery 的消息提示框效果代码
2011/07/31 Javascript
js限制文本框输入长度两种限制方式(长度、字节数)
2012/12/19 Javascript
js 赋值包含单引号双引号问题的解决方法
2014/02/26 Javascript
用原生JS获取CLASS对象(很简单实用)
2014/10/15 Javascript
jQuery延迟加载图片插件Lazy Load使用指南
2015/03/25 Javascript
Bootstrap源码解读下拉菜单(4)
2016/12/23 Javascript
Bootstrap源码解读标签、徽章、缩略图和警示框(8)
2016/12/26 Javascript
Vue.js 2.0 移动端拍照压缩图片预览及上传实例
2017/04/27 Javascript
javascript将16进制的字符串转换为10进制整数hex
2020/03/05 Javascript
js 将多个对象合并成一个对象 assign方法的实现
2020/09/24 Javascript
[03:26]回顾2015国际邀请赛中国区预选赛
2015/06/09 DOTA
Python基于回溯法子集树模板解决取物搭配问题实例
2017/09/02 Python
Python_LDA实现方法详解
2017/10/25 Python
python加载自定义词典实例
2019/12/06 Python
代码总结Python2 和 Python3 字符串的区别
2020/01/28 Python
Python进阶之迭代器与迭代器切片教程
2020/01/29 Python
Python接口开发实现步骤详解
2020/04/26 Python
为什么说python适合写爬虫
2020/06/11 Python
Django Admin 上传文件到七牛云的示例代码
2020/06/20 Python
Python爬虫之App爬虫视频下载的实现
2020/12/08 Python
matplotlib 使用 plt.savefig() 输出图片去除旁边的空白区域
2021/01/05 Python
林清轩官方网站:山茶花润肤油开创者
2016/10/26 全球购物
Johnson Fitness澳大利亚:高级健身器材
2021/03/16 全球购物
会计电算化专业应届大学生求职信
2013/10/22 职场文书
法学院方阵解说词
2014/01/29 职场文书
关于孝道的演讲稿
2014/05/21 职场文书
文艺晚会策划方案
2014/06/11 职场文书
城南旧事电影观后感
2015/06/16 职场文书
《秋天的雨》教学反思
2016/02/19 职场文书
工作转正自我鉴定范文
2019/06/21 职场文书
2019个人年度目标制定攻略!
2019/07/12 职场文书
24年收藏2000多部退役军用电台
2022/02/18 无线电