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+java实现自动新闻滚动窗口
Oct 09 PHP
smarty 原来也不过如此~~呵呵
Nov 25 PHP
php 表单验证实现代码
Mar 10 PHP
PHP笔记之:日期函数的使用介绍
Apr 24 PHP
深入PHP与浏览器缓存的分析
Jun 03 PHP
解析php中如何调用用户自定义函数
Aug 06 PHP
在PHP上显示JFreechart画的统计图方法
Nov 03 PHP
php中实现精确设置session过期时间的方法
Jul 17 PHP
php自定义apk安装包实例
Oct 20 PHP
PHP中foreach()用法汇总
Jul 02 PHP
smarty模板数学运算示例
Dec 11 PHP
详解PHP5.6.30与Apache2.4.x配置
Jun 02 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
Web程序工作原理详解
2014/12/25 PHP
ThinkPHP3.1.x修改成功与失败跳转页面的方法
2017/09/29 PHP
PHP实现的MD5结合RSA签名算法实例
2017/10/07 PHP
PHP html_entity_decode()函数讲解
2019/02/25 PHP
javascript 动态参数判空操作
2008/12/22 Javascript
妙用Jquery的val()方法
2012/06/27 Javascript
jquery解析xml字符串简单示例
2014/04/11 Javascript
JavaScript数组深拷贝和浅拷贝的两种方法
2014/04/16 Javascript
让alert不出现弹窗的两种方法
2014/05/18 Javascript
jquery动态加载js/css文件方法(自写小函数)
2014/10/11 Javascript
jQuery文本框得到与失去焦点动态改变样式效果
2016/09/08 Javascript
bootstrap datetimepicker 日期插件在火狐下出现一条报错信息的原因分析及解决办法
2017/03/08 Javascript
jQuery+ajax实现局部刷新的两种方法
2017/06/08 jQuery
javascript算法之二叉搜索树的示例代码
2017/09/12 Javascript
微信小程序仿美团城市选择
2018/06/06 Javascript
Angular5中调用第三方库及jQuery的添加的方法
2018/06/07 jQuery
typescript nodejs 依赖注入实现方法代码详解
2019/07/21 NodeJs
JS简单表单验证功能完整示例
2020/01/26 Javascript
Python的动态重新封装的教程
2015/04/11 Python
详解Python中time()方法的使用的教程
2015/05/22 Python
WINDOWS 同时安装 python2 python3 后 pip 错误的解决方法
2017/03/16 Python
使用python获取电脑的磁盘信息方法
2018/11/01 Python
Python数据类型之List列表实例详解
2019/05/08 Python
python numpy 常用随机数的产生方法的实现
2019/08/21 Python
在python中修改.properties文件的操作
2020/04/08 Python
python virtualenv虚拟环境配置与使用教程详解
2020/07/13 Python
西班牙伏林航空公司:Vueling
2016/08/05 全球购物
阿迪达斯意大利在线商店:adidas意大利
2016/09/19 全球购物
Lulu & Georgia官方网站:购买地毯、家具、抱枕、壁纸、床上用品等
2018/03/19 全球购物
印度尼西亚电子产品购物网站:Kliknklik
2018/06/05 全球购物
医院总经理职责
2013/12/26 职场文书
幼儿园教研活动方案
2014/01/19 职场文书
黄金酒广告词
2014/03/21 职场文书
社区党的群众路线教育实践活动领导班子对照检查材料
2014/09/25 职场文书
2016大学军训心得体会
2016/01/11 职场文书
Django路由层如何获取正确的url
2021/07/15 Python