获取当前进程ID
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、回收子进程
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
<?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.html
echo $child->read().'jackin.chen';
\Swoole\Process::wait();
//主进程代码
while (1){
sleep(10);
}
array Process::wait(bool $blocking = true);
$result = array('code' => 0, 'pid' => 15001, 'signal' => 15);
子进程结束必须要执行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信号入门
<?php
cli_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(); //回收子进程
<?php
cli_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(); //等待回收子进程
}*/
//第二种方案 :在异步信号回调中执行wait
Process::signal(SIGCHLD, function($sig) {
//必须为false,非阻塞模式
while($ret = Process::wait(false)) {
var_dump($ret);
}
});
//信号发生时可能同时有多个子进程退出
//必须循环执行wait直到返回false
Linux信号完整对照表[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
信号 | 取值 | 默认动作 | 含义(发出信号的原因) |
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的计时器导师 |
构建Http\Server对象Server只能用于php-cli环境,在其他环境下会抛出致命错误
$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遍历所有连接
运行流程图进程/线程结构图onStart
启动后在主进程(master)的主线程回调此函数,函数原型
function onStart(Server $server);
在此事件之前Server已进行了如下操作
接下来要执行
onStart回调中,仅允许echo、打印Log、修改进程名称。不得执行其他操作。onWorkerStart和onStart回调是在不同进程中并行执行的,不存在先后顺序。
可以在onStart回调中,将$serv->master_pid和$serv->manager_pid的值保存到一个文件中。这样可以编写脚本,向这两个PID发送信号来实现关闭和重启的操作。
onStart事件在Master进程的主线程中被调用。
在onStart中创建的全局资源对象不能在Worker进程中被使用,因为发生onStart调用时,worker进程已经创建好了 新创建的对象在主进程内,Worker进程无法访问到此内存区域 因此全局对象创建的代码需要放置在Server::start之前
<?php
use \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函数。
异常捕获onStart/onManagerStart/onWorkerStart 3个事件的执行顺序是不确定的
$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、这种功能是很适合用多进程监控来做的
<?php
use \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.processlist
select * 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
进程里面运行协程代码
<?php
use \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;
测试数据
<?php
use \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);
}
});
*/
案例代码:父子进程通信,子进程之间通信
<?php
use \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场景练习 多进程监控订单表状态、队列通信
使用队列通信
<?php
use \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写的程序、设置进程名称
执行一个外部程序,此函数是exec系统调用的封装。
bool Process->exec(string $execfile, array $args)
执行成功后,当前进程的代码段将会被新程序替换。子进程蜕变成另外一套程序。父进程与当前进程仍然是父子进程关系。
父进程与新进程之间可以通过标准输入输出进行通信,必须启用标准输入输出重定向。
$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.php
cli_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.php
move = /usr/bin/env php move.php
<?php
//function.php
use \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.php
cli_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();
//在异步信号回调中执行wait
Process::signal(SIGCHLD, function($sig) {
//必须为false,非阻塞模式
while($ret = Process::wait(false)) {
//执行回收后的处理逻辑,比如拉起一个新的进程
var_dump($ret);
}
});
//回收结束运行的子进程。
Process::wait(true);
<?php
//send.php
cli_set_process_title('swoole-send');
while (true){
echo 'send-data'.PHP_EOL;
sleep(3);
}
13简易进程管理器(2)监控配置文件、支持动态添加子进程
<?php
//pm.php
use \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 -l
Process::signal(SIGCHLD, function() {
//必须为false,非阻塞模式
while($ret = Process::wait(false)) {
//执行回收后的处理逻辑,比如拉起一个新的进程
var_dump($ret);
}
});
//回收结束运行的子进程。
//Process::wait(true);
<?php
//function.php
use \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.php
use \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 条件。有两个解决方案:
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";
Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved