当前位置: 首页 > 后端技术 > PHP

Actor并发模型_0

时间:2023-03-29 17:38:39 PHP

PHP物联网开发工具不适合做物联网服务器?在传统思维中,经常有人跟你说php不适合做物联网服务器,让你转用java、node、go等其他语言。是的,是的,传统意义上的php真的很难做物联网服务器,因为太烂了,当然这不代表完全做不到。例如,当你要实现一个TCP服务器时,你可能需要按照以下原则编写代码:for($i=0;$i<=1;$i++){$pid=pcntl_fork();如果($pid){如果($i==0){$server=stream_socket_server("tcp://127.0.0.1:9501",$errno,$errstr,STREAM_SERVER_BIND);}elseif($i==1){$tickTime=time()+3600;while(1){usleep(1);if($tickTime==time()){//domytickfunc}}}}}上面代码的意思等于在一个进程中创建一个TCP服务器,在另一个进程中无限循环做时间检测,从而实现定时器逻辑。看起来真的很蹩脚,对于编程基础普遍薄弱的PHPer来说,维护起来确实很困难。当然这时候会有人说,不是有Workerman吗?是的,有工人。Workerman是一个PHP多进程框架,高度封装了上述代码原理,帮助您集中精力实现代码逻辑。所以,PHP时不时的做物联网其实是一个谬论。当然这时候可能有人会说go语言有协程。用Workerman阻塞数据库调用,效率很差,很难有高并发。确实如此,但实际上,我们可以使用尽可能多的进程来弥补这个不足,那就是堆机。当然,如果你真的要精打细算,没关系,这时候我们可以拿出我们的杀手锏,就是Swoole4.x的协程。以swoole作为TCP服务器为例,如下代码:$server=newswoole_server("127.0.0.1",9501);$server->on('workerstart',function($ser,$workerId){if($workerId==0){swoole_timer_tick(1000,function(){});}});$server->on('connect',function($server,$fd){echo"连接打开:{$fd}\n";});$server->on('receive',function($server,$fd,$reactor_id,$data){$server->send($fd,"Swoole:{$data}");$server->close($fd);});$server->on('close',function($server,$fd){echo"连接关闭:{$fd}\n";});$server->start();我们可以快速创建一个多进程的协程TCP服务器,在每个回调函数中,自动创建一个协程环境。我们可以在协程回调中调用协程数据库API,这样就避免了阻塞数据库调用导致无法处理其他客户端请求的问题。然而,尽管如此,可能很多人都没有想过如何优雅的编写自己的物联网服务器。比如在我们常见的互联网设备管理服务中,可能会出现如下代码:swoole_timer_tick(5000,function(){$deviceList=$db->getAll();foreach($deviceListas$device){//doyourcheck/**例如设备状态为1,则需要处理进程1*例如设备状态为2,则需要处理进程2*例如设备状态为3,则进程3需要处理*/}});定时遍历查看设备状态和广播乍一看无伤大雅,但是当有多个设备,每个设备的逻辑不一致时,那么这样的写法很容易写出大量的代码,而在协程下,如果不注意变量访问安全和协程上下文的隔离,很容易出现bug,难以维护。演员模型什么是演员?简单的说,actor就是一个高度抽象的并发模型。每个actor实例的内存空间相互隔离,降低用户编程和维护的难度。关于Swoole4.x是如何实现协程版的Actor的,我们在文章https://segmentfault.com/a/11....中已经讲解了如何使用Swoole实现协程的原理。我们还是用easyswoole/actor库来讲解Actor模型库的实战。比如我们有一种设备,那么我们可以定义一个设备Actor,把设备的所有逻辑都写在actor模型中。示例代码如下:namespaceApp\Device;useEasySwoole\Actor\AbstractActor;useEasySwoole\Actor\ActorConfig;useEasySwoole\EasySwoole\Logger;useEasySwoole\EasySwoole\ServerManager;useEasySwoole\EasySwoole\Trigger;classDeviceActorextendsAbstractActor{;private$$deviceId;私人$lastHeartBeat;publicstaticfunctionconfigure(ActorConfig$actorConfig){$actorConfig->setActorName('Device');}受保护函数onStart(){$this->lastHeartBeat=time();/**创建时传入的参数*/$this->fd=$this->getArg()['fd'];$this->deviceId=$this->getArg()['deviceId'];//记录到DeviceManager::addDevice(newDeviceBean(['deviceId'=>$this->deviceId,'actorId'=>$this->actorId(),'fd'=>$this->fd]))在表管理器中;//推送消息ServerManager::getInstance()->getSwooleServer()->push($this->fd,"连接服务器成功,你的actorId是{$this->actorId()}");//创建一个定时器,如果一个设备20s没有收到消息,它会自动下线$this->tick(20*2000,function(){if(time()-$this->lastHeartBeat>20){$this->exit(-1);}});}protectedfunctiononMessage($msg){if($msginstanceofCommand){switch($msg->getCommand()){case$msg::RECONNECT:{DeviceManager::updateDeviceInfo($this->deviceId,['fd'=>$msg->getArg()]);$this->fd=$msg->getArg();Logger::getInstance()->console("deviceId{$this->deviceId}atactorId{$this->actorId()}重连成功");ServerManager::getInstance()->getSwooleServer()->push($this->fd,"deviceId{$this->deviceId}atactorId{$this->actorId()}重新连接成功");break;}case$msg::WS_MSG:{$recv=$msg->getArg();Logger::getInstance()->console("deviceId{$this->deviceId}atactorId{$this->actorId()}recvwsmsg:{$recv}");ServerManager::getInstance()->getSwooleServer()->push($this->fd,'actorrecvmsgforhash'.md5($recv));break;}case$msg::REPLY_MSG:{$recv=$msg->getArg();Logger::getInstance()->console("deviceId{$this->deviceId}atactorId{$this->actorId()}recvreplymsg:{$recv}");ServerManager::getInstance()->getSwooleServer()->push($this->fd,'actorrecvreplymsg'.$recv);//这里return一个数据,会返回给客户端return"actorId{$this->actorId()}recv{$recv}";break;}}}}protectedfunctiononExit($arg){if($arg==-1){if(ServerManager::getInstance()->getSwooleServer()->exist($this->fd)){ServerManager::getInstance()->getSwooleServer()->push($this->fd,"心跳丢失,actor退出");ServerManager::getInstance()->getSwooleServer()->close($this->fd);}}DeviceManager::deleteDevice($this->deviceId);Logger::getInstance()->console("deviceId{$this->deviceId}在actorId{$this->actorId()}exit");}protectedfunctiononException(\Throwable$throwable){Trigger::getInstance()->throwable($throwable);}}在这个Actor中,我们定义了这个设备的生命周期periodicbehavior设备上线,记录设备id和fd信息,并创建心跳周期检查接收到的消息,可以向Actor下发数据,并处理相应的消息行为。设备下线。当设备下线时,它可以自动清理定时器和其他一些通知和清理日志ic我们可以清楚的看到,Actor模型可以让我们管理一个高度自治的设备模型。当然本章主要讲解如何优雅的使用Swoole协程实现Actor模型,从而更好的开发和管理我们的设备,所以我就不贴太多代码了,有兴趣的同学可以在Easyswoole框架demo中查看完整示例代码https://github.com/easy-swool...Easyswoole项目主页:http://easyswoole.com/Easyswoolegithub主仓库https://github.com/easy-swool...,如果你觉得我们的努力对您有帮助,记得给个star

猜你喜欢