经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Swoole » 查看文章
PHP下用Swoole实现Actor并发模型的方法
来源:jb51  时间:2019/6/12 11:27:30  对本文有异议

什么是Actor?

Actor对于PHPer来说,可能会比较陌生,写过Java的同学会比较熟悉,Java一直都有线程的概念(虽然PHP有Pthread,但不普及),它是一种非共享内存的并发模型,每个Actor内的数据独立存在,Actor之间通过消息传递的形式进行交互调度,且Actor是一种高度抽象化的编程模型,非常适合于游戏、硬件行业。

Swoole协程与信箱

得益于Swoole4.x,我们可以基于Swoole的协程与Channel快速实现一个信箱模式调度。模拟代码如下:

  1. use Swoole\Coroutine\Channel;
  2. go(function (){
  3. //创建十个信箱通道
  4. $mailBoxes = [];
  5. for ($i = 1;$i <= 10;$i++){
  6. $mailBoxes[$i] = new Channel(16);
  7. }
  8. //模拟master 邮局调度,随机像一个信箱投递消息
  9. go(function ()use($mailBoxes){
  10. while (1){
  11. \co::sleep(2);
  12. $key = rand(1,10);
  13. ($mailBoxes[$key])->push(time());
  14. }
  15. });
  16. //模拟actor 实体消费
  17. for ($i = 1;$i <= 10;$i++){
  18. go(function ()use($mailBoxes,$i){
  19. while (1){
  20. $msg = ($mailBoxes[$i])->pop();
  21. echo "Actor {$i} recv msg : {$msg} \n";
  22. }
  23. });
  24. }
  25. });

以上代码执行输出:

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库

  1. composer require easyswoole/actor=2.x-dev

我们依赖dev库进行测试,生产可以自己依赖stable版本

进程关系

Easyswoole的Actor模型中,存在两组进程,一组是proxy进程,用来实现Actor对外服务,一组是worker进程,proxy进程与worker进程之间通过unixsock进行通讯,而Actor实例就均匀的分布worker之中。

样例代码

比如在一个聊天室中,我们可以定义一个房间模型。

  1. namespace EasySwoole\Actor\Test;
  2.  
  3.  
  4. use EasySwoole\Actor\AbstractActor;
  5. use EasySwoole\Actor\ActorConfig;
  6.  
  7. class RoomActor extends AbstractActor
  8. {
  9. public static function configure(ActorConfig $actorConfig)
  10. {
  11. $actorConfig->setActorName('Room');
  12. }
  13. public function onStart()
  14. {
  15. //每当一个RoomActor实体被创建的时候,都会执行该回调
  16. var_dump('room actor '.$this->actorId().' start');
  17. }
  18. public function onMessage($msg)
  19. {
  20. //每当一个RoomActor实体收到外部消息的时候,都会执行该回调当
  21. var_dump('room actor '.$this->actorId().' onmessage: '.$msg);
  22. return 'reply at '.time();
  23. }
  24. public function onExit($arg)
  25. {
  26. //每当一个RoomActor实体退出的时候,都会执行该回调
  27. var_dump('room actor '.$this->actorId().' exit at arg: '.$arg);
  28. return 'exit at '.time();
  29. }
  30. protected function onException(\Throwable $throwable)
  31. {
  32. //每当一个RoomActor出现异常的时候,都会执行该回调
  33. var_dump($throwable->getMessage());
  34. }
  35. }
  36.  

在cli模式下创建一个Actor服务

  1. use EasySwoole\Actor\Actor;
  2. use EasySwoole\Actor\Test\RoomActor;
  3. use EasySwoole\Actor\ProxyProcess;
  4.  
  5. Actor::getInstance()->register(RoomActor::class);
  6. $list = Actor::getInstance()->generateProcess();
  7.  
  8. foreach ($list['proxy'] as $proxy){
  9. /** @var ProxyProcess $proxy */
  10. $proxy->getProcess()->start();
  11. }
  12. foreach ($list['worker'] as $actors){
  13. foreach ($actors as $actorProcess){
  14. /** @var ProxyProcess $actorProcess */
  15. $actorProcess->getProcess()->start();
  16. }
  17. }
  18. while($ret = \Swoole\Process::wait()) {
  19. echo "PID={$ret['pid']}\n";
  20. }
  21.  

创建一个cli测试脚本

  1. use EasySwoole\Actor\Actor;
  2. use EasySwoole\Actor\Test\RoomActor;
  3. Actor::getInstance()->register(RoomActor::class);
  4.  
  5. go(function (){
  6. $actorId = RoomActor::client()->create('create arg1');
  7. var_dump($actorId);
  8. \co::sleep(3);
  9. var_dump(RoomActor::client()->send($actorId,'this is msg'));
  10. \co::sleep(3);
  11. var_dump(RoomActor::client()->exit($actorId,'this is exit arg'));
  12. \co::sleep(3);
  13. RoomActor::client()->create('create arg2');
  14. \co::sleep(3);
  15. RoomActor::client()->create('create arg3');
  16. \co::sleep(3);
  17. var_dump(RoomActor::client()->sendAll('sendAll msg'));
  18. \co::sleep(3);
  19. var_dump(RoomActor::client()->status());
  20. \co::sleep(3);
  21. var_dump(RoomActor::client()->exitAll('sendAll exit'));
  22. });
  23.  

以上代码执行结果如下:

服务端

  1. php test.php
  2. string(40) "room actor 00101000000000000000001 start"
  3. string(57) "room actor 00101000000000000000001 onmessage: this is msg"
  4. string(64) "room actor 00101000000000000000001 exit at arg: this is exit arg"
  5. string(40) "room actor 00101000000000000000002 start"
  6. string(40) "room actor 00103000000000000000001 start"
  7. string(57) "room actor 00101000000000000000002 onmessage: sendAll msg"
  8. string(57) "room actor 00103000000000000000001 onmessage: sendAll msg"
  9. string(60) "room actor 00101000000000000000002 exit at arg: sendAll exit"
  10. string(60) "room actor 00103000000000000000001 exit at arg: sendAll exit"

客户端

  1. php test2.php
  2. string(23) "00101000000000000000001"
  3. string(19) "reply at 1559623925"
  4. string(18) "exit at 1559623928"
  5. bool(true)
  6. array(3) {
  7. [1]=>
  8. int(1)
  9. [2]=>
  10. int(0)
  11. [3]=>
  12. int(1)
  13. }
  14. bool(true)

更多细节可以在EasySwoole项目官网得到文档支持 http://easyswoole.com/

喜欢EasySwoole项目的,可以给个star https://github.com/easy-swoole/easyswoole

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持w3xue。

 友情链接:直通硅谷  点职佳

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号