https://www.php.net/manual/zh/function.posix-getpid.php
<?php$id = posix_getpid();echo $id;while (1){ sleep(10);}//查看进程 ps -ef | grep xxx
设置进程名称
https://www.php.net/manual/zh/function.cli-set-process-title

<?php$id = posix_getpid();cli_set_process_title('001');echo $id;while (1){ sleep(10);}
02创建第一个子进程、获取子进程ID、回收子进程defunct去世进程
root 7986 7985 0 11:18 pts/1 00:00:00 [php] <defunct>
<?php$id = posix_getpid();cli_set_process_title('php1');echo $id.PHP_EOL;$child = new \Swoole\Process(function(){ echo '我是一个子进程'.posix_getpid().PHP_EOL;});$child->start();while (1){ sleep(10);}
利用主进程创建子进程,每个子进程结束后子进程没有被主进程收回资源,子进程会变成僵尸进程。
每个子进程结束后,父进程必须都要实行一次 wait() 进行回收,否则子进程会变成僵尸进程,会摧残浪费蹂躏操作系统的进程资源。
<?php //主进程代码$id = posix_getpid();cli_set_process_title('php-main');echo $id.PHP_EOL;$child = new \Swoole\Process(function(){ //子进程实行 cli_set_process_title('php-child'); echo '我是一个子进程'.posix_getpid().PHP_EOL; while (1){ //去世循环,不让进程不退出 sleep(10); }});$child->start();\Swoole\Process::wait(); //回收子进程资源//主进程代码while (1){ sleep(10);}
03重定向子进程标准输出 、父进程获取子进程数据初步
父子进程相互通信并且交互数据
Swoole\Process::__construct(callable $function, bool $redirect_stdin_stdout = false, int $pipe_type = SOCK_DGRAM, bool $enable_coroutine = false);
bool $redirect_stdin_stdout
功能:重定向子进程的标准输入和输出。【启用此选项后,在子进程内输出的内容将不是打印屏幕,而是写入到主进程管道。读取键盘输入将变为从管道中读取数据。默认为壅塞读取。参考 exec() 方法内容】<?php$id = posix_getpid();cli_set_process_title('php-main');echo $id.PHP_EOL;$child = new \Swoole\Process(function(){ //子进程实行 cli_set_process_title('php-child'); echo 'my name is:'; while (1){ sleep(10); }},true); // true 重定向写入到主进程管道$child->start();//从管道中读取数据 https://wiki.swoole.com/wiki/page/217.htmlecho $child->read().'jackin.chen';\Swoole\Process::wait();//主进程代码while (1){ sleep(10);}
\Swoole\Process::wait() 回收结束运行的子进程。
array Process::wait(bool $blocking = true);$result = array('code' => 0, 'pid' => 15001, 'signal' => 15);
$blocking 参数可以指定是否壅塞等待,默认为壅塞操作成功会返回一个数组包含子进程的PID、退出状态码、被哪种旗子暗记KILL失落败返回false
子进程结束必须要实行wait进行回收,否则子进程会变成僵尸进程
<?php$id = posix_getpid();cli_set_process_title('php-main');echo $id.PHP_EOL;$child = new \Swoole\Process(function(){ //子进程实行 cli_set_process_title('php-child'); $num = 0; while (1){ echo 'my name is:'.++$num; sleep(2); }},true); // true 重定向写入到主进程管道$child->start();\Swoole\Process::wait(false); // 非壅塞//主进程代码while (1){ echo $child->read().'jackin.chen'.PHP_EOL; sleep(2);}
04多个子进程的回收、Linux旗子暗记入门
<?phpcli_set_process_title('php-main');echo '当提高程ID:'.posix_getpid().PHP_EOL;use \Swoole\Process;$child1 = new Process(function() { //子进程实行 cli_set_process_title('php-child1'); echo 'child1:'.posix_getpid().PHP_EOL; while (1) { sleep(2); }});$child1->start();$child2 = new Process(function() { cli_set_process_title('php-child2'); echo 'child2:'.posix_getpid().PHP_EOL;});$child2->start();Process::wait(); //回收子进程
实行脚本:进程直接退出,由于个中有一个子进程wait退出,子进程一旦被kill会引起父进程退出。办理方案
<?phpcli_set_process_title('php-main');echo '当提高程ID:'.posix_getpid().PHP_EOL;use \Swoole\Process;$child1 = new Process(function() { //子进程实行 cli_set_process_title('php-child1'); echo 'child1:'.posix_getpid().PHP_EOL; while (1) { sleep(2); }});$child1->start();$child2 = new Process(function() { cli_set_process_title('php-child2'); echo 'child2:'.posix_getpid().PHP_EOL;});$child2->start();//第一种方案/for($i=0;$i<2;$i++){ Process::wait(); //等待回收子进程}///第二种方案 :在异步旗子暗记回调中实行waitProcess::signal(SIGCHLD, function($sig) { //必须为false,非壅塞模式 while($ret = Process::wait(false)) { var_dump($ret); }});//旗子暗记发生时可能同时有多个子进程退出//必须循环实行wait直到返回false
[root@localhost ~]# ps -ef | grep php
root 16569 6315 0 14:15 pts/1 00:00:00 php-main
root 16570 16569 0 14:15 pts/1 00:00:00 php-child1
Linux旗子暗记完全对照表旗子暗记
取值
默认动作
含义(发出旗子暗记的缘故原由)
SIGHUP
1
Term
终真个挂断或进程去世亡
SIGINT
2
Term
来自键盘上的中断旗子暗记
SIGQUIT
3
Core
来自键盘上的离开旗子暗记
SIGILL
4
Core
造孽指令
SIGABRT
6
Core
来自abort的非常旗子暗记
SIGFPE
8
Core
浮点例外
SIGKILL
9
Term
杀去世
SIGSEGV
11
Core
段造孽缺点(内存引用无效)
SIGPIPE
13
Term
管道破坏:向一个没有读进程的管道写数据
SIGALRM
14
Term
来自alarm的计时器导师
理解下旗子暗记 一个最大略的例子:我们按下键盘ctd±a我们的进程就停滞了。 这便是旗子暗记 旗子暗记是进程间通信机制中唯一的异步通信机制,一个进程不必通 过任何操作来等待旗子暗记的到达,事实上,进程也不知道旗子暗记到底是什么 么时候到达。进程之间可以相互通过系统调用kill发送软中断旗子暗记05在子进程中运行httpserver、修正进程名称Server只能用于php-cli环境,在其他环境下会抛出致命缺点
构建Http\Server工具$serv = new Swoole\Server('0.0.0.0', 9501, SWOOLE_BASE, SWOOLE_SOCK_TCP);
创建一个异步做事器程序,支持TCP、UDP、UnixSocket 3种协议,支持IPv4和IPv6,支持SSL/TLS单向双向证书的隧道加密。利用者无需关注底层实现细节,仅须要设置网络事宜的回调函数即可。
请勿在利用Server创建之前调用其他异步IO的API,否则将会创建失落败。可以在Server启动后onWorkerStart回调函数中利用。
设置运行时参数$serv->set(array( 'worker_num' => 4, 'daemonize' => true, 'backlog' => 128,));
注册事宜回调函数
$serv->on('Connect', 'my_onConnect');$serv->on('Receive', 'my_onReceive');$serv->on('Close', 'my_onClose');
PHP中可以利用4种回调函数的风格
启动做事器$serv->start();
属性列表
$serv->manager_pid; //管理进程的PID,通过向管理进程发送SIGUSR1旗子暗记可实现柔性重启$serv->master_pid; //主进程的PID,通过向主进程发送SIGTERM旗子暗记可安全关闭做事器$serv->connections; //当前做事器的客户端连接,可利用foreach遍历所有连接
运行流程图
启动后在主进程(master)的主线程回调此函数,函数原型
function onStart(Server $server);
在此事宜之前Server已进行了如下操作
已创建了manager进程已创建了worker的进程已监听所有TCP/UDP/UnixSocket端口,但未开始Accept连接和要求已经监听了定时器接下来要实行
主Reactor开始吸收事宜,客户端可以connect到ServeronStart回调中,仅许可echo、打印Log、修正进程名称。不得实行其他操作。onWorkerStart和onStart回调是在不同进程中并行实行的,不存在先后顺序。
可以在onStart回调中,将$serv->master_pid和$serv->manager_pid的值保存到一个文件中。这样可以编写脚本,向这两个PID发送旗子暗记来实现关闭和重启的操作。
onStart事宜在Master进程的主线程中被调用。
在onStart中创建的全局资源工具不能在Worker进程中被利用,由于发生onStart调用时,worker进程已经创建好了 新创建的工具在主进程内,Worker进程无法访问到此内存区域 因此全局工具创建的代码须要放置在Server::start之前
<?phpuse \Swoole\Process;echo '当提高程ID:'.posix_getpid().PHP_EOL;cli_set_process_title('swoole-main');$child = new Process(function() { $http = new Swoole\Http\Server("0.0.0.0", 9501); $http->set([ "worker_num"=>4 ]); $http->on('request', function ($request, $response) { $response->end("<h1>Hello Swoole. #".rand(1000, 9999)."</h1>"); }); $http->on('start', function ( $serv) { cli_set_process_title('swoole-master'); }); $http->on('managerstart', function ($serv) { cli_set_process_title('swoole-manager'); }); $http->on('workerstart', function ($serv, $worker_id) { cli_set_process_title('swoole-worker'); }); $http->start();});$child->start();echo '当提高程名称:'.cli_get_process_title().PHP_EOL;Process::signal(SIGCHLD, function($sig) { //必须为false,非壅塞模式 while($ret = Process::wait(false)) { var_dump($ret); }});
事宜
Swoole\Server是事宜驱动模式,所有的业务逻辑代码必须写在事宜回调函数中。当特定的网络事宜发生后,底层会主动回调指定的PHP函数。
共支持13种事宜,详细详情请参考各个页面详细页PHP措辞有4种回调函数的写法事宜实行顺序所有事宜回调均在$server->start后发生做事器关闭程序终止时末了一次事宜是onShutdown做事器启动成功后,onStart/onManagerStart/onWorkerStart会在不同的进程内并发实行onReceive/onConnect/onClose在Worker进程中触发Worker/Task进程启动/结束时会分别调用一次onWorkerStart/onWorkerStoponTask事宜仅在task进程中发生onFinish事宜仅在worker进程中发生onStart/onManagerStart/onWorkerStart 3个事宜的实行顺序是不愿定的
非常捕获swoole不支持set_exception_handler函数如果你的PHP代码有抛出非常逻辑,必须在事宜回调函数顶层进行try/catch来捕获非常$serv->on('Receive', function() { try { //some code } catch(Exception $e) { //exception code }}
协程模式
Swoole2/4版本支持了协程,利用协程后事宜回调函数将会并发地实行。协程是一种用户态线程实现,没有额外的调度花费,仅占用内存。利用协程模式,可以理解为“每次事宜回调函数都会创建一个新的线程去实行,事宜回调函数实行完成后,线程退出”。
如果希望关闭协程,可设置:
$server->set(["enable_coroutine" => false, ]);
06场景练习监控文件变动、进程感知
1、比如有个指定配置文件db.conf在当前 项目目录下的tmp文件夹中 2、文件一旦发生变动则进程能感知到,然后发送什么等等 3、这种功能是很适宜用多进程监控来做的
<?phpuse \Swoole\Process;echo '当提高程ID:'.posix_getpid().PHP_EOL;cli_set_process_title('swoole-main');$child = new Process(function() { $file = __DIR__.'/db.conf'; $md5 = md5_file($file); while (true){ $md5_check = md5_file($file); if(strcmp($md5,$md5_check)){ echo '文件被修正'.date('Y-m-d H:i:s').PHP_EOL; } sleep(1); }});$child->start();echo '当提高程名称:'.cli_get_process_title().PHP_EOL;Process::wait();/Process::signal(SIGCHLD, function($sig) { //必须为false,非壅塞模式 while($ret = Process::wait(false)) { var_dump($ret); }});?
07场景练习mysql大略单纯巡检监控
!mysql监控 1、连接是否正常I 2、查询连接数 3、查询线程利用情形则进程能刚知到,然后发送什么等等
这种功能是很适宜用多进程监控来做的 把稳:本日的代码须要把swoole升级到4.3.X Peel install swoole 即可
监控:
select count() as c from information_schema.processlistselect from information_schema.GLOBAL_STATUS where VARIABLE_NAME like 'Thread%'select from information_schema.GLOBAL_STATUS where VARIABLE_NAME like 'Abort%'select from information_schema.processlist
进程里面运行协程代码
<?phpuse \Swoole\Process;use \Swoole\Coroutine\MySQL;echo '当提高程ID:'.posix_getpid().PHP_EOL;cli_set_process_title('swoole-main');$child = new Process(function() { $swoole_mysql = new MySQL(); $swoole_mysql->connect([ 'host' => '192.168.33.10', 'port' => 3306, 'user' => 'root', 'password' => 'root', 'database' => 'information_schema', ]); $checkConnect = "select 1"; $checkProcessCount = "select count() as c from information_schema.processlist"; $checkThread = "select from information_schema.GLOBAL_STATUS where VARIABLE_NAME like 'Thread%'"; //$res = $swoole_mysql->query('select 1'); //测试 while (true){ $checkResult[] = date('Y-m-d H:i:s'); try{ $swoole_mysql->query($checkConnect); $checkResult[] = '检讨连接正常'; $res = $swoole_mysql->query($checkProcessCount); $checkResult[] = '当前连接数'.$res[0]['c']; $res = $swoole_mysql->query($checkThread); $checkResult[] = '检讨线程情形'; foreach ($res as $row){ foreach ($row as $key => $value){ $checkResult[] = $key.':'.$value; } } $checkResult[] = '---------------------'; echo implode(PHP_EOL,$checkResult); }catch (Exception $exception){ echo $exception->getMessage().PHP_EOL; } sleep(5); }},false,0,true);$child->start();echo '当提高程名称:'.cli_get_process_title().PHP_EOL;Process::wait();/Process::signal(SIGCHLD, function($sig) { //必须为false,非壅塞模式 while($ret = Process::wait(false)) { var_dump($ret); }});/
08场景练习 多进程监控订单表状态、父子进程通信
Process->write
向管道内写入数据。
function Process->write(string $data) int | bool;
在子进程内调用write,父进程可以调用read吸收此数据在父进程内调用write,子进程可以调用read吸收此数据
测试数据
<?phpuse \Swoole\Process;use \Swoole\Coroutine\MySQL;echo '当提高程ID:'.posix_getpid().PHP_EOL;cli_set_process_title('swoole-main');/CREATE TABLE `zerg`.`swoole_order` (`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '表ID',`order_no` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '订单号',`is_pay` tinyint(2) NOT NULL DEFAULT 0 COMMENT '是否支付',`is_notice` tinyint(2) NOT NULL DEFAULT 0 COMMENT '是否关照',PRIMARY KEY (`id`) USING BTREE) ENGINE = InnoDB AUTO_INCREMENT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact; /$child = new Process(function() { $swoole_mysql = new MySQL(); $swoole_mysql->connect([ 'host' => '192.168.33.10', 'port' => 3306, 'user' => 'root', 'password' => 'root', 'database' => 'zerg', ]); while (true){ try{ $test = 'insert into swoole_order (order_no) value ("'.md5(time()).'")'; $swoole_mysql->query($test); var_dump($swoole_mysql); }catch (Exception $exception){ echo $exception->getMessage().PHP_EOL; } sleep(1); }},false,0,true);$child->start();echo '当提高程名称:'.cli_get_process_title().PHP_EOL;Process::wait();/Process::signal(SIGCHLD, function($sig) { //必须为false,非壅塞模式 while($ret = Process::wait(false)) { var_dump($ret); }});/
案例代码:父子进程通信,子进程之间通信
<?phpuse \Swoole\Process;use \Swoole\Coroutine\MySQL;echo '当提高程ID:'.posix_getpid().PHP_EOL;cli_set_process_title('swoole-main');//该子进程卖力查询已支付并未关照订单信息$child_select = new Process(function(Process $process) { $swoole_mysql = new MySQL(); $swoole_mysql->connect([ 'host' => '192.168.33.10', 'port' => 3306, 'user' => 'root', 'password' => 'root', 'database' => 'zerg', ]); while (true){ try{ $order = "select id,order_no,is_pay,is_notice from swoole_order where is_pay=1 and is_notice=0 limit 1"; $res = $swoole_mysql->query($order); if($res && count($res)){ $process->write($res[0]['order_no']); } }catch (Exception $exception){ echo $exception->getMessage().PHP_EOL; } sleep(3); }},false,1,true); //1:创建SOCK_STREAM类型管道$child_select->start();//该子进程卖力对已支持订单发送关照$child_notice = new Process(function(Process $process) { while (true){ $order_notice = $process->read(); if($order_notice){ echo '进程2获取到订单号:'.$order_notice.PHP_EOL; } usleep(0.5 1000 1000); //微秒 }},false,1,true); //1:创建SOCK_STREAM类型管道$child_notice->start();//主进程吸收子进程的后往另一个子进程写while (true){ $order_no = $child_select->read(); if($order_no){ $child_notice->write($order_no); } usleep(0.5 1000 1000); //微秒}echo '当提高程名称:'.cli_get_process_title().PHP_EOL;Process::signal(SIGCHLD, function($sig) { //必须为false,非壅塞模式 while($ret = Process::wait(false)) { var_dump($ret); }});
09场景练习 多进程监控订单表状态、行列步队通信
利用行列步队通信
<?phpuse \Swoole\Process;use \Swoole\Coroutine\MySQL;echo '当提高程ID:'.posix_getpid().PHP_EOL;cli_set_process_title('swoole-main');//该子进程卖力查询已支付并未关照订单信息$child_select = new Process(function(Process $process) { $swoole_mysql = new MySQL(); $swoole_mysql->connect([ 'host' => '192.168.33.10', 'port' => 3306, 'user' => 'root', 'password' => 'root', 'database' => 'zerg', ]); $offset = 0; while (true){ try{ $order = "select order_no from swoole_order where is_pay=1 and is_notice=0 limit {$offset},1"; $res = $swoole_mysql->query($order); if($res && count($res)){ $process->push($res[0]['order_no']); } }catch (Exception $exception){ echo $exception->getMessage().PHP_EOL; } $offset++; sleep(3); }},false,1,true); //1:创建SOCK_STREAM类型管道$child_select->useQueue(2);$child_select->start();//该子进程卖力对已支持订单发送关照$child_notice1 = new Process(function(Process $process) { while (true){ $order_notice = $process->pop(); if($order_notice){ echo '1子进程从行列步队取消息:'.$order_notice.PHP_EOL; } usleep(0.5 1000 1000); //微秒 }},false,1,true); //1:创建SOCK_STREAM类型管道$child_notice1->useQueue(2);$child_notice1->start();//该子进程卖力对已支持订单发送关照$child_notice2 = new Process(function(Process $process) { while (true){ $order_notice = $process->pop(); if($order_notice){ echo '2子进程从行列步队取消息:'.$order_notice.PHP_EOL; } usleep(0.5 1000 1000); //微秒 }},false,1,true); //1:创建SOCK_STREAM类型管道$child_notice2->useQueue(2);$child_notice2->start();/ 思路: 1、进程之间不会发生同时争夺 2、如果发送关照比较耗时,没有处理完又从数据库中读取消息 -> 考虑利用redis锁实现 /echo '当提高程名称:'.cli_get_process_title().PHP_EOL;Process::wait();/Process::signal(SIGCHLD, function($sig) { //必须为false,非壅塞模式 while($ret = Process::wait(false)) { var_dump($ret); }});/
10调用外部程序成为子进程之调用Go写的程序、设置进程名称完全路径/usr/bin/php xx.php$(which php) xx.php/usr/bin/env php xx.php
实行一个外部程序,此函数是exec系统调用的封装。
bool Process->exec(string $execfile, array $args)
$execfile指定可实行文件的绝对路径,如 "/usr/bin/python"$args是一个数组,是exec的参数列表,如 array('test.py', 123),相称于python test.py 123
实行成功后,当提高程的代码段将会被新程序更换。子进程蜕变成其余一套程序。父进程与当提高程仍旧是父子进程关系。
父进程与新进程之间可以通过标准输入输出进行通信,必须启用标准输入输出重定向。
$execfile必须利用绝对路径,否则会报文件不存在缺点 由于exec系统调用会利用指定的程序覆盖当出路序,子进程须要读写标准输出与父进程进行通信 如果未指定redirect_stdin_stdout = true,实行exec后子进程与父进程无法通信
$process = new \Swoole\Process(function (\Swoole\Process $childProcess) { // 不支持这种写法 // $childProcess->exec('/usr/local/bin/php /var/www/project/yii-best-practice/cli/yii t/index -m=123 abc xyz'); // 封装 exec 系统调用 // 绝对路径 // 参数必须分开放到数组中 $childProcess->exec('/usr/local/bin/php', ['/var/www/project/yii-best-practice/cli/yii', 't/index', '-m=123', 'abc', 'xyz']); // exec 系统调用});$process->start(); // 启动子进程
父进程与exec子进程利用管道进行通信:
// exec - 与exec进程进行管道通信use Swoole\Process;$process = new Process(function (Process $worker) { $worker->exec('/bin/echo', ['hello']); $worker->write('hello');}, true); // 须要启用标准输入输出重定向$process->start();echo "from exec: ". $process->read(). "\n";
<?php# 案例 use \Swoole\Process;echo '当提高程ID:'.posix_getpid().PHP_EOL;cli_set_process_title('swoole-main');$process = new Process(function (Process $p){ echo '123'; //该代码段将会被新程序更换,不会实行 $p->exec('/usr/bin/php',[__DIR__.'/run.php','name']); echo 'abc'; //该代码段将会被新程序更换,不会实行},true,0,true); // 父子进程进行管道通信,须要启用标准输入输出重定向$process->start();while (true){ $str = $process->read(); echo $str;}Process::signal(SIGCHLD, function($sig) { //必须为false,非壅塞模式 while($ret = Process::wait(false)) { // var_dump($ret); }});
<?php# run.phpcli_set_process_title('swoole-exec');while (true){ echo date('Y-m-d H:i:s').PHP_EOL; sleep(3);}
[root@localhost ~]# ps -ef | grep swoole root 3494 3064 0 01:00 pts/0 00:00:00 swoole-main root 3495 3494 0 01:00 pts/0 00:00:00 swoole-exec
11(附加课)调用外部程序成为子进程之调用Go写的程序、设置进程名称$process = new Process(function (Process $p){ echo '123'; //该代码段将会被新程序更换,不会实行 $p->exec('/usr/run.go',[]); echo 'abc'; //该代码段将会被新程序更换,不会实行},true,0,true); // 父子进程进行管道通信,须要启用标准输入输出重定向$process->start();
12易进程管理器(1)读取配置、启动多个子进程
#pm.conf[child]send = /usr/bin/env php send.phpmove = /usr/bin/env php move.php
<?php//function.phpuse \Swoole\Process;function init(){ $config = parse_ini_file('pm.conf',true); $child = $config['child']; foreach ($child as $name => $item){ $params = explode(' ',$item); $process = new Process(function (Process $p) use($params){ $p->exec($params[0],array_splice($params,1)); }); $process->start(); } }//查看linux下的旗子暗记列表 kill -l
<?php//move.phpcli_set_process_title('swoole-move');while (true){ echo 'move-data'.PHP_EOL; sleep(3);}
<?php//pm.php 进程入口use \Swoole\Process;echo '当提高程ID:'.posix_getpid().PHP_EOL;cli_set_process_title('swoole-main');//包含公共函数require 'function.php';//初始化进程init();//在异步旗子暗记回调中实行waitProcess::signal(SIGCHLD, function($sig) { //必须为false,非壅塞模式 while($ret = Process::wait(false)) { //实行回收后的处理逻辑,比如拉起一个新的进程 var_dump($ret); }});//回收结束运行的子进程。Process::wait(true);
<?php//send.phpcli_set_process_title('swoole-send');while (true){ echo 'send-data'.PHP_EOL; sleep(3);}
13大略单纯进程管理器(2)监控配置文件、支持动态添加子进程
<?php//pm.phpuse \Swoole\Process;echo '当提高程ID:'.posix_getpid().PHP_EOL;cli_set_process_title('swoole-main');//包含公共函数require 'function.php';//核心进程intCore();//启动进程init();//主进程拦截子进程发送旗子暗记Process::signal(SIGUSR1, function() { echo '进程正在重新配置'.date('Y-m-d H:i:s').PHP_EOL; init();});//在异步旗子暗记回调中实行wait//查看linux下的旗子暗记列表 kill -lProcess::signal(SIGCHLD, function() { //必须为false,非壅塞模式 while($ret = Process::wait(false)) { //实行回收后的处理逻辑,比如拉起一个新的进程 var_dump($ret); }});//回收结束运行的子进程。//Process::wait(true);
<?php//function.phpuse \Swoole\Process;//创建进程$processList = []; //主进程变量,管理进程变量function init(){ global $processList; // $config = parse_ini_file('pm.conf',true); $child = $config['child']; foreach ($child as $name => $item){ $params = explode(' ',$item); $process = new Process(function (Process $p) use($params){ $p->exec($params[0],array_splice($params,1)); }); $pid = $process->start();//用户自定义进程 $processList[$name] = [ 'pid' =>$pid, 'date' =>date('Y-m-d H:i:s'), ]; ; }}//运行 核心子进程function intCore(){ $process = new Process(function (Process $p) { cli_set_process_title('swoole-watch'); watchConfig(); //运行子进程,子父进程通信发送旗子暗记 }); $process->start();}//监听文件function watchConfig(){ $child = new Process(function() { $file = __DIR__.'/pm.conf'; $md5 = md5_file($file); while (true){ $md5_check = md5_file($file); if(strcmp($md5,$md5_check)){ //子进程和父进程通信,发送旗子暗记 Process::kill(posix_getppid(),SIGUSR1); //向父进程发送账户 echo posix_getppid().'文件被修正'.date('Y-m-d H:i:s').PHP_EOL; } sleep(3); } }); $child->start();}
14大略单纯进程管理器(2)监控配置文件、支持动态删除子进程
<?php//function.phpuse \Swoole\Process;//创建进程$processList = []; //主进程变量,管理进程变量function init(){ global $processList; // $config = parse_ini_file('pm.conf',true); $child = $config['child']; foreach ($child as $name => $item){ $params = explode(' ',$item); $process = new Process(function (Process $p) use($params){ $p->exec($params[0],array_splice($params,1)); }); $pid = $process->start();//用户自定义进程 $processList[$name] = [ 'pid' =>$pid, 'date' =>date('Y-m-d H:i:s'), ]; ; } //删除进程 rmProcess($child);}//运行 核心子进程function intCore(){ $process = new Process(function (Process $p) { cli_set_process_title('swoole-watch'); watchConfig(); //运行子进程,子父进程通信发送旗子暗记 }); $process->start();}//监听文件function watchConfig(){ $child = new Process(function() { $file = __DIR__.'/pm.conf'; $md5 = md5_file($file); while (true){ $md5_check = md5_file($file); if(strcmp($md5,$md5_check)){ //子进程和父进程通信,发送旗子暗记 Process::kill(posix_getppid(),SIGUSR1); //向父进程发送旗子暗记 echo posix_getppid().'文件被修正'.date('Y-m-d H:i:s').PHP_EOL; } sleep(3); } }); $child->start();}function rmProcess($child){ global $processList; $process = array_diff_key($processList,$child); foreach ($process as $pkey => $pvalue){ Process::kill($pkey['pid'],SIGTERM); unset($processList[$pkey]); }}
15大略单纯进程管理器支持API查看子进程运行情形
<?php//利用文件共享进程状态$http = new Swoole\Http\Server('0.0.0.0', 9501);$http->set([ 'worker_num' => 1]);$http->on('Request', function ($request, $response) { $response->header('Content-Type', 'text/html; charset=utf-8'); $content = file_get_contents('pm.status'); $response->end($content);});$http->start();
function httpWeb(){ $process = new Process(function (Process $p){ $p->exec('/usr/bin/env',['php',__DIR__.'/http.php']); }); $pid = $process->start();//用户自定义进程}
16大略单纯进程管理器 子进程退出时修正状态、API状态同步17大略单纯定时任务(1)crontab表达式解析、定时实行函数20补充课时swoole 4.4.x之后子进程回收代码的问题处理
Process主进程自动退出 https://github.com/swoole/swoole-src/issues/2731
从4.4版本开始底层将不再将旗子暗记监听作为 EventLoop Exit 的 block 条件。有两个办理方案:
添加一个 tick 定时器改为壅塞等待进程退出use Swoole\Process;cli_set_process_title("master");$manager=new Process(function(Process $process){ cli_set_process_title("manager"); while(true) { echo "a".PHP_EOL; sleep(1); }}); $ret = Process::wait(); echo "PID={$ret['pid']}\n";