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 相关文章推荐
用ODBC的分页显示
Oct 09 PHP
PHP下编码转换函数mb_convert_encoding与iconv的使用说明
Dec 16 PHP
字符串长度函数strlen和mb_strlen的区别示例介绍
Sep 09 PHP
php常用的安全过滤函数集锦
Oct 09 PHP
PHP列出MySQL中所有数据库的方法
Mar 12 PHP
PHP获取客户端及服务器端IP的封装类
Jul 21 PHP
PHP实现RSA签名生成订单功能【支付宝示例】
Jun 06 PHP
PHP Socket网络操作类定义与用法示例
Aug 30 PHP
深入研究PHP中的preg_replace和代码执行
Aug 15 PHP
Mac下快速搭建PHP开发环境步骤详解
May 05 PHP
Laravel 创建指定表 migrate的例子
Oct 09 PHP
php实现记事本案例
Oct 20 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 rsa加密解密使用方法
2015/04/27 PHP
thinkphp中AJAX返回ajaxReturn()方法分析
2016/12/06 PHP
利用Laravel事件系统如何实现登录日志的记录详解
2017/05/20 PHP
Laravel中七个非常有用但很少人知道的Carbon方法
2017/09/21 PHP
PHP折半(二分)查找算法实例分析
2018/05/12 PHP
改写一个简单的菜单 弹性大小
2010/12/02 Javascript
JavaScript版DateAdd和DateDiff函数代码
2012/03/01 Javascript
JS两种定义方式的区别、内部原理
2013/11/21 Javascript
基于promise.js实现nodejs的promises库
2014/07/06 NodeJs
用javascript将数据导入Excel示例代码
2014/09/10 Javascript
JavaScript 动态加载脚本和样式的方法
2015/04/13 Javascript
jQuery实现宽屏图片轮播实例教程
2015/11/24 Javascript
javascript每日必学之循环
2016/02/19 Javascript
最简单的JS实现json转csv的方法
2019/01/10 Javascript
ES6 Promise对象的含义和基本用法分析
2019/06/14 Javascript
js判断复选框是否选中的方法示例【基于jQuery】
2019/10/10 jQuery
解决vue路由name同名,路由重复的问题
2020/08/05 Javascript
JS实现可以用键盘方向键控制的动画
2020/12/11 Javascript
Python 文件处理注意事项总结
2017/04/10 Python
python 数据的清理行为实例详解
2017/07/12 Python
flask-restful使用总结
2018/12/04 Python
彻底理解Python中的yield关键字
2019/04/01 Python
对Python3之方法的覆盖与super函数详解
2019/06/26 Python
Python-接口开发入门解析
2019/08/01 Python
Python利用matplotlib绘制约数个数统计图示例
2019/11/26 Python
Python 实现递归法解决迷宫问题的示例代码
2020/01/12 Python
django 取消csrf限制的实例
2020/03/13 Python
在主流系统之上安装Pygame的方法
2020/05/20 Python
Pycharm的Available Packages为空的解决方法
2020/09/18 Python
天猫超市:阿里巴巴打造的网上超市
2016/11/02 全球购物
澳大利亚波西米亚风情网上商店:Czarina
2019/03/18 全球购物
中科软笔试题和面试题
2014/10/07 面试题
中秋节超市促销方案
2014/01/30 职场文书
体育教师个人的自我评价
2014/02/16 职场文书
总经理岗位职责
2015/02/04 职场文书
2015年医德医风工作总结
2015/04/02 职场文书