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

PHP多进程系列笔记(5)

时间:2023-03-30 03:32:01 PHP

前面几节讲解了pcntl扩展实现的多进程程序。本节介绍swoole扩展的swoole_process模块??。Swoole多进程swoole_process是swoole提供的进程管理模块,用于替代PHP的pcntl扩展。首先确保安装的swoole版本大于1.7.2:$php--riswooleswooleswoolesupport=>enabledVersion=>1.10.1注意:swoole_process在最新的1.8.0已经禁止在web环境使用版本,所以它只能支持命令行。swoole提供的多进程扩展的基本功能与pcntl提供的基本功能相同,但是swoole更易用,提供:默认基于unixsock的进程间通信;支持消息队列作为进程间通信;基于signalfd和eventloop处理信号,几乎没有任何额外消耗;高精度微秒定时器;配合swoole_event模块,创建的PHP子流程可以是异步事件驱动方式。swoole_process模块??提供的方法(Method)主要分为四部分:基本方法swoole_process::__constructswoole_process->startswoole_process->nameswoole_process->execswoole_process->closeswoole_process->exitswoole_process::killswoole_process::waitswoole_process::daemonswoole_process::setAffinity管理通道通信swoole_process->writeswoole_process->readswoole_process->setTimeoutswoole_process->setBlocking消消息队列通信swoole_process->useQueueswoole_process->statQueueswoole_process->freeQueueswoole_process->pushswoole_process->popsignal_process:thisexampleprocessarmimplement:swoole_process:swooletcpserver,特点:多进程处理客户端连接子进程退出,Master进程会重新创建一个支持事件回调的主进程Exit,子进程完成手头工作后退出mpid=$id=getmypid();echotime()."Masterprocess,pid{$id}\n";//创建tcp服务器$this->socket=stream_socket_server("tcp://0.0.0.0:9201",$errno,$errstr);if(!$this->socket)exit("启动服务器错误:$errstr---$errno");for($i=0;$istart_worker_process();}echo"waitingclient...\n";//Master进程等候子进程退出,必然是死循环while(1){foreach($this->pidsas$k=>$pid){if($pid){$res=swoole_process::wait(false);if($res){echotime()."工作进程$pid退出,将启动新的...\n";$this->start_worker_process();unset($this->pids[$k]);}}}sleep(1);//让出1s时间给CPU}},false,false);//不开启管道通信swoole_process::daemon();//守护进程$process->start();//注意:start后的变量subprocess不可用}/***创建worker进程并接受客户端连接*/privatefunctionstart_worker_process(){$process=newswoole_process(function(swoole_process$worker){$this->acceptClient($worker);},false,false);$pid=$process->start();$this->pids[]=$pid;}privatefunctionacceptClient(&$worker){//子进程一直在等待客户端连接无法退出while(1){$conn=stream_socket_accept($this->socket,-1);如果($this->onConnect)call_user_func($this->onConnect,$conn);//回调连接事件//开始循环读取消息$recv='';//实际接收消息$buffer='';//缓冲消息while(1){$this->checkMpid($worker);$buffer=fread($conn,20);//没有收到正常消息if($buffer===false||$buffer===''){if($this->onClose)call_user_func($this->onClose,$conn);//回调断开事件break;//结束读取消息,等待下一次客户端连接}$pos=strpos($buffer,"\n");//消息结束字符if($pos===false){$recv.=$buffer;}else{$recv.=trim(substr($buffer,0,$pos+1));如果($this->onMessage)call_user_func($this->onMessage,$conn,$recv);//回调收到消息事件//客户端强行关闭连接if($recv=="quit"){echo"clientcloseconn\n";fclose($conn);休息;}$recv='';//清除消息,准备下次接收}}}}//检查主进程是否存在,如果没有子进程完成手头的工作后退出publicfunctioncheckMpid(&$worker){if(!swoole_process::kill($this->mpid,0)){$worker->exit();//这个提示其实是看不见的到了。需要写入日志echo"Masterprocessexited,I[{$worker['pid']}]alsoquit\n";}}函数__destruct(){@fclose($this->socket);}}$server=newTcpServer();$server->onConnect=function($conn){echo"onConnect--accepted".stream_socket_get_name($conn,true)。"\n";fwrite($conn,"connsuccess\n");};$server->onMessage=function($conn,$msg){echo"onMessage--".$味精。"\n";fwrite($conn,"received".$msg."\n");};$server->onClose=function($conn){echo"onClose--".stream_socket_get_name($conn,true)。"\n";fwrite($conn,"onClose"."\n");};$server->run();运行后可以使用telnet连接:telnet127.0.0.19201由于设置了最多3个子进程,所以最多只能接受3个客户端连接进程间通信上面解释的例子这里没有直接数据主进程和子进程之间的交互。如果主进程需要得到子进程的反馈,或者子进程从主进程接收数据,那么就需要进行进程间通信。Swoole内置了管道通信和消息队列通信。管道通信管道通信主要是数据传输:一个进程需要向另一个进程发送数据。这个swoole封装之后,使用起来就很简单了:read();ob_start();passthru($cmd);//执行外部程序并显示未处理的原始输出,并直接打印输出$return=ob_get_clean()?:'';$return=trim($return)..workerpid:".$worker->pid."\n";//$worker->write($return);//写入数据到thepipelineecho$return;//向管道写入数据。注意:echo在子进程中也会写入管道},true);//第二个参数为true,开启管道通信$pid=$process->start();$workers[$pid]=$process;}foreach($workers作为$pid=>$worker){$worker->write('whoami');//通过管道向子进程发送数据。管道是单向的:发出的数据必须由另一端读取。无法读取自己发送的$recv=$worker->read();//同步阻塞读管道数据echo"recvresult:$recv";}//回收子进程while(count($workers)){//echo时间()。"\n";foreach($workers作为$pid=>$worker){$ret=swoole_process::wait(false);if($ret){echo"workerexit:$pid\n";取消设置($workers[$pid]);}}}运行:$phpswoole_process_pipe.phprecvresult:Linuxrecvresult:2018-06-24Sunday16:18:01CSTrecvresult:yjcworkerexit:14519workerexit:14522workerexit:14525注意点:1.pipeline数据读取是同步的被阻止;在上面的例子中,如果你给子进程添加$worker->read(),它会一直被阻塞。可以使用swoole_event_add将管道加入到事件循环中,变成异步模式。2、子进程中的输出(如echo)与write效果相同。3.通过管道向子进程发送数据。管道是单向的:发出的数据必须由另一端读取。无法读取自己发送的。这里对swoole_process::wait()进行补充说明:1.swoole_process::wait()默认是阻塞的,而swoole_process::wait(false)是非阻塞的;2.swoole_process::wait()阻塞模式只能调用一次回收一个子进程,非阻塞模式调用一次目前可能无法回收子进程;3、如果不加swoole_process::wait(),主进程会死循环,主进程退出后会变成僵尸进程。ps-A-o统计、ppid、pid、cmd|grep-e'^[Zz]'可以查询僵尸进程。反盗版声明:本文为原创文章,发表于公众号飞鸿影博客(fhyblog)和博客园,转载需征得作者同意。消息队列通信消息队列与管道有些不同:消息队列是全局的,所有进程都可以发送和读取。你可以把它想象成一个Redis列表结构。消息队列更常见的用途是主进程分配任务,子进程消费执行。pop()){//echo"recvfrommaster:$cmd\n";ob_start();passthru($cmd);//执行外部程序并显示未处理的原始输出,这将直接打印输出。$return=ob_get_clean()?:'';$return="res:".trim($return).".workerpid:".$worker->pid."\n";回声$返回;//睡眠(1);}$工人->出口(0);},假的,假的);//不创建管道$process->useQueue(1,2|swoole_process::IPC_NOWAIT);//使用消息队列$pid=$process->start();$workers[$pid]=$process;}//由于所有进程共享一个消息队列,所以只需要给一个子进程发送消息$worker=current($workers);for($i=0;$i<3;$i++){$worker->push('whoami');//发送消息}//回收子进程while(count($workers)){foreach($workersas$pid=>$worker){$ret=swoole_process::wait();if($ret){echo"workerexit:$pid\n";取消设置($workers[$pid]);}}}运行结果:$phpswoole_process_quene.phpres:yjc.工人pid:15885res:yjc。工人pid:15886res:yjc。workerpid:15887workerexit:15885workerexit:15886workerexit:15887注意事项:1.所有进程共享一个消息队列;2.消息队列的读操作阻塞,可以在useQueue时进行第二次如果第一个参数mode改为2|swoole_process::IPC_NOWAIT,异步模式只设置为2才阻塞。例子中去掉swoole_process::IPC_NOWAIT后,读取消息的while会死循环。3.睡一觉(1);加在子进程前面,是为了防止父进程在向消息队列添加内容之前直接退出。4.子进程末尾也加入了sleep。这是为了防止一个进程消费所有的消息,实际应用中需要去掉。信号和定时器swoole_process::alarm支持微秒定时器:5){//清除定时器swoole_process::alarm(-1);//退出进程swoole_process::kill(getmypid());}}//安装信号swoole_process::signal(SIGALRM,'ev_timer');//触发定时器信号:单位为微秒。如果是负数,表示清除定时器swoole_process::alarm(100*1000);//100msechogetmypid()."\n";//这句话会顺序执行,不用while循环来防止进程直接退出:$phpswoole_process_alarm.php13660#0alarm#1alarm#2alarm#3alarm#4alarm#5alarm注意:alarm不能和Swoole\Timer同时使用。参考1.Process-Swoole-Swoole文档中心https://wiki.swoole.com/wiki/...欢迎关注公众号,及时获取最新文章!推荐!您每月只需2.5即可拥有带SSD的VPS!