前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >PHP平滑关闭/重启的实现代码

PHP平滑关闭/重启的实现代码

作者头像
很酷的站长
发布于 2022-12-02 09:09:43
发布于 2022-12-02 09:09:43
1K00
代码可运行
举报
运行总次数:0
代码可运行

本文为小伙伴们带来了关于PHP平滑关闭/重启的实现代码,

前言

写过 CLI 常驻进程的老司机肯定遇到过这么一个问题:在需要更新程序的时候,我要怎样才能安全关闭老进程?你可能会想到 NGINX、php-fpm 之类的平滑重启是给进程发送 USR2 信号,然后它就会将当前请求处理完再退出。

但进程是怎样接收信号、处理信号,估计就不是很多人能说清楚了。

原理

要实现平滑关闭/重启不难,这里先讲解两个知识点:

阻塞信号

当我们的程序正在处理一个任务的时候,你肯定不希望它中途被终止,比如说你在执行一个数据库事务,肯定不希望事务还没被提交进程就被终止了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!--?php
echo "开始执行事务" . PHP_EOL;
// 模拟一些耗时的操作
$finish_time = time() + 5;
while (time() < $finish_time) {
}
echo "事务执行完毕" . PHP_EOL;</pre-->

上面这段代码,如果你在第二个 echo 之前用 kill 命令去杀死这个进程,那么第二个 echo 就不会被执行了。那能不能做到在事务过程中暂时先忽略 kill 信号呢?

能。我们可以使用 pcntl_sigprocmask() 来阻塞信号,让事务完成之后再响应 kill 信号。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!--?php
 
// 阻塞信号
$sig_set = array(SIGINT, SIGTERM); // 要阻塞的信号集合
pcntl_sigprocmask(SIG_BLOCK, $sig_set); // SIG_BLOCK: 把信号加入到当前阻塞信号中
 
echo date("[Y-m-d H:i:s]") . " 开始执行事务" . PHP_EOL;
 
$finish_time = time() + 5;
while (time() < $finish_time) {
}
 
echo date("[Y-m-d H:i:s]") . "事务执行完毕" . PHP_EOL;
 
pcntl_sigprocmask(SIG_UNBLOCK, $sig_set); // SIG_UNBLOCK: 从当前阻塞信号中移出信号</pre-->

同样的,在第二个 echo 之前按下 Ctrl + C 或者用 kill 命令去杀这个进程,你会发现第二个 echo 正常执行了,并且两条输出的时间间隔是 5 秒。

我们的常驻进程通常是在一个 while(true) 循环中去执行重复的任务,如果这么写的话:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!--?php
while (true) {
    pcntl_sigprocmask(SIG_BLOCK, $sig_set);
    // ...
    pcntl_sigprocmask(SIG_UNBLOCK, $sig_set);
}</pre-->

我们是可以保证一个事务不会被打断,但是我们的程序还不知道是不是已经接收到信号了,并且把阻塞信号移除之后进程立刻就退出了,没办法去做一些收尾工作(比如关闭文件)。

处理信号

为了解决上面提到的问题,我们需要在信号发生的时候去做收尾工作,然后再退出进程。

pcntl 扩展提供了一些信号相关的函数,我们可以使用 pcntl_signal() 和 pcntl_signal_dispatch() 来注册信号处理器和分发信号。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!--?php
$sig_handler = function ($signo) {
    echo "收到信号 {$signo}" . PHP_EOL;
};
pcntl_signal(SIGINT, $sig_handler); // 给 SIGINT 信号注册一个处理器
 
// 模拟耗时操作
echo "开始执行事务" . PHP_EOL;
$finish_time = time() + 5;
while(true) {
    if (time() --> $finish_time) {
        echo "事务执行完毕" . PHP_EOL;
        break;
    }
}
pcntl_signal_dispatch(); // 分发信号

执行上面这段代码并在 5 秒内按下 Ctrl + C,你会看到 sig_handler 被执行了;而如果不按下 Ctrl + C,那么 sig_handler 就不会被执行。

到这里你应该已经理解了 pcntl_signal() 和 pcntl_signal_dispatch() 的用法了,把它放到到刚刚的代码试试

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!--?php
 
$sig_handler = function ($signo) {
    echo "收到信号 {$signo}" . PHP_EOL;
};
$sig_set = array(SIGINT, SIGTERM);
foreach ($sig_set as $sig) {
    pcntl_signal($sig, $sig_handler); // 注册多个信号
}
 
// [1]
 
while (true) {
    // [2-1]
    pcntl_sigprocmask(SIG_BLOCK, $sig_set);
    // [2-2]
 
    // ...
 
    // [2-3]
    pcntl_sigprocmask(SIG_UNBLOCK, $sig_set);
    // [2-4]
}
 
// [3]</pre-->

pcntl_signal_dispatch() 该放哪里呢?是 [1] [2] 还是 [3]?先动手试一下

然后你会发现,只有放在 [2] 才能让信号处理器执行。同时这个实验也告诉我们 pcntl_signal_dispatch() 要在信号发生后才会使处理器执行:放在 [1] 时,除非你手速足够快,不然在你按下 Ctrl + C 或者是 kill 之前就已经执行过了;而放在 [3] 它就永远没机会执行。

至于放在 [2] 的哪个位置,我建议是放在 [2-4],因为这个时候已经处理完任务了。

拼起来

到这里你已经了解平滑关闭/重启的原理了,我们把上面的半成品代码(因为在收到信号后可能还会进入下一层循环)整理一下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!--?php
 
$running = true;
 
$sig_handler = function ($signo) use (&$running) {
    echo "收到信号 {$signo}" . PHP_EOL;
    // 做收尾工作
    $running = false;
};
$sig_set = array(SIGINT, SIGTERM, SIGUSR2 /* 熟悉的 USR2 信号不能漏 */);
foreach ($sig_set as $sig) {
    pcntl_signal($sig, $sig_handler); // 注册多个信号
}
 
 
while ($running) {
    pcntl_sigprocmask(SIG_BLOCK, $sig_set);
 
    // ... 业务逻辑
 
    pcntl_sigprocmask(SIG_UNBLOCK, $sig_set);
    pcntl_signal_dispatch();
}</pre-->

我们就得到了一个可以平滑程序的常驻进程框架,你也可以把它封装成一个类。

以上就是关于PHP平滑关闭/重启的实现代码的全部内容了,感兴趣的小伙伴记得点击关注哦。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
PHP中pcntl_sigprocmask的作用是什么
当你编写程序,想要屏蔽某个或多个信号的时候,那么我们就会用到PHP扩展中的pcntl_sigprocmask函数,这个函数就是用于设置信号屏蔽字的函数,第一个参数就是可以是否设置屏蔽信号字,SIG_BLOCK 是设置,SIG_UNBLOCK是移除不设置 第二个参数就是你要设置屏蔽的信号集合数组,第三个参数是返回之前设置屏蔽的信号集数组
北溟有鱼QAQ
2021/04/14
7400
信号初相识:Linux 内核的 “隐形使者”
在 Linux 系统的广袤世界里,信号(Signal)宛如一位神秘的隐形使者,默默地在后台发挥着至关重要的作用。它是一种异步通信机制,如同古代的烽火台,当特定事件发生时,便会燃起 “烽火”,通知进程做出相应的反应。信号可以来自硬件异常,如内存访问错误、除零错误;也能由软件条件触发,像是用户按下特定的按键组合,或者程序主动调用系统函数发送信号 。
用户11396661
2025/02/28
730
【Linux探索学习】第二十八弹——信号(下):信号在内核中的处理及信号捕捉详解
https://blog.csdn.net/2301_80220607/category_12805278.html?spm=1001.2014.3001.5482
GG Bond1
2025/02/05
1030
【Linux探索学习】第二十八弹——信号(下):信号在内核中的处理及信号捕捉详解
进程间通信的信号艺术:机制、技术与实战应用深度剖析
alarm() 函数的返回值是上一次 alarm() 设置的定时器剩余的时间(以秒为单位)。具体行为如下:
绝活蛋炒饭
2024/12/16
1200
进程间通信的信号艺术:机制、技术与实战应用深度剖析
这次让我们真的读一下Workerman源码(六)
在经过了一个如沐春风、令人神清气爽而又愉悦的工作周后(具体发生了什么你们心里应该有数),总算可以回到以往周六日的节奏了。实际上对于我来说,没有严格意义上的周六日,一直在做事情,只不过所做事情的贡献对象不同而已。WM我已经叨叨了五个章节了,今天我想聊聊关于Workerman进程管理部分的相关源码,如果前五个章节你们都已经仔细研究过了,那么现在阅读Workerman进程管理部分的源码应该会是易如反掌了。
老李秀
2019/12/26
1.7K0
一文搞懂Linux信号【下】
在观看本博客之前,建议大家先看一文搞懂Linux信号【上】。由于上一篇博客篇幅太长,为了更好的阅读体验,我拆成了两篇博客。那么接下来,在上一篇的基础上,我们继续学习Linux信号部分。本篇我们主要谈论信号保存和信号处理。
破晓的历程
2024/06/24
1310
一文搞懂Linux信号【下】
老李在搞Workerman的日子里(五)
首先是我这两天看了一些三国演义,我决定学习罗贯中大爷那种给章节起名的方式,你们先xue微感受一下,如果觉得不好恶心想吐,那么你们都给我忍着!!!
老李秀
2019/12/11
1K0
PHP进程间通信-信号
(一)PHP进程间通信-信号 信号是一种系统调用。通常我们用的kill命令就是发送某个信号给某个进程的。具体有哪些信号可以在liunx/mac中运行kill -l查看。下面这个例子中,父进程等待5秒钟,向子进程发送sigint信号。子进程捕获信号,调信号处理函数处理。
码农编程进阶笔记
2021/07/20
1.8K0
初识Linux · 信号保存
前文我们已经介绍了信号产生,在时间的学习线上,信号的学习分为预备知识,信号产生,信号保存,信号处理,本文我们学习信号保存,在前言部分,我们介绍几个信号保存中的概念。
_lazy
2024/11/19
660
初识Linux · 信号保存
如何防止PHP进程异常退出(进程被杀)?
通常,在cli下运行的常驻后台PHP进程,可能异常退出,比如php执行过程中出现的致命错误,或被 kill 命令手动杀死等。如下面的php代码:
猿哥
2019/07/25
2.5K0
wokerman启动分析
根据上面存储的static::<pre>不能识别此Latex公式: _workers和static::</pre>_pidMap 循环fork进程
用户2825413
2019/07/15
6780
如何用PHP编写一个信号中断处理程序
当我们使用kill命令发送或者在终端按下ctrl+c时,我们编写的中断处理函数就会收到中断信号
北溟有鱼QAQ
2021/04/13
8350
如何用PHP编写一个信号中断处理程序
sigterm信号_一文吃透 PHP 进程信号处理
前两周老大给安排了一个任务,写一个监听信号的包。因为我司的项目是运行在容器里边的,每次上线,需要重新打包镜像,然后启动。在重新打包之前,Dokcer会先给容器发送一个信号,然后等待一段超时时间(默认10s)后,再发送SIGKILL信号来终止容器
全栈程序员站长
2022/11/01
1.2K0
sigterm信号_一文吃透 PHP 进程信号处理
CVE-2018-9206渗透实战
jQuery上传组件,这个组建的漏洞还是蛮多的,详情请戳:https://xz.aliyun.com/t/3819
鸿鹄实验室
2021/04/15
1.2K0
CVE-2018-9206渗透实战
php进程通信-进程信号
快一个月没发博文了,之前都在深入研究php多进程tcp服务器,结果到现在也没搞出一个完美的解决方案,所以还是先发下这个月学到的东西吧
仙士可
2019/12/18
1.5K0
linux系统编程之信号(三):信号的阻塞与未决
一、信号在内核中的表示 实际执行信号的处理动作称为信号递达(Delivery),信号从产生到递达之间的状态,称为信号未决(Pending)。进程可以选择阻塞(Block)某个信号,SIGKILL 和
s1mba
2017/12/28
2.3K0
linux系统编程之信号(三):信号的阻塞与未决
初探PHP多进程
枕边书
2018/01/04
1.5K0
初探PHP多进程
10(信号)
func的值可以是: (1)SIG_IGN–忽略 (2)SIG_DFL–系统默认动作 (3)调用的函数地址–信号处理程序
提莫队长
2019/02/21
6880
PHP进程管理
这篇文章是对之前一篇文章的补充和改进, 创建一个主(master)进程,主进程安装定时器,每隔5分钟检测一次队列长度,根据队列长度计算需要的worker进程,
用户3094376
2018/12/06
1.6K0
一种绕过php disablefunc的方法复现
无意中刷到一个文章,关于disablefunction的方法绕过的,依然是脚本小子式的复现环境。
用户5878089
2019/07/23
1.7K0
相关推荐
PHP中pcntl_sigprocmask的作用是什么
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验