信号处理
需要linux环境并开启pcntl
<?php
declare(ticks=1);
namespace app\common\library;
/**
* 信号处理, 比如重新队列文件的while (true)增加信号管理
Signal::init();
while (true) {
Signal::checkExit();
// code ...
}
* @throws \Exception
* @copyright lxx
* @author lxx
*/
class Signal
{
// 强制停止时间
public const TIMEOUT = 60 * 3;
/**
* 注册信号
* 请在while(true)中运行 pcntl_signal_dispatch();
* @return void
* @throws \Exception
* @copyright lxx
* @author lxx
*/
public static function init()
{
if (PHP_OS == 'Linux' && IS_CLI) {
// go on
} else {
return;
}
$GLOBALS['signal_stop'] = 0;
pcntl_signal(SIGINT, function ($s) {
// CTRL+c
(new Signal())->tagStop();
});
pcntl_signal(SIGTERM, function ($s) {
// docker默认发送这个终止SIGTERM
// https://docs.docker.com/compose/compose-file/05-services/#stop_signal
// 超时时间 https://docs.docker.com/compose/compose-file/05-services/#stop_grace_period
(new Signal())->tagStop();
});
pcntl_signal(SIGHUP, function ($s) {
// 重启信号
(new Signal())->tagStop();
});
pcntl_signal(SIGALRM, function ($s) {
// pcntl_alarm信号
echo 'TIMEOUT EXIT.';
exit();
});
}
/**
* 标记全进程的stop, 超时时候直接发送停止信号
* ! 闭包限制,虽然是public也仅限当前类使用
* @return void
* @throws \Exception
* @copyright lxx
* @author lxx
*/
public function tagStop()
{
$GLOBALS['signal_stop'] = 1;
// 使用这个不会阻塞进程
echo '收到停止信号';
pcntl_alarm(self::TIMEOUT);
}
/**
* 判断是否存在停止信号
*
* @return boolean
* @throws \Exception
* @copyright lxx
* @author lxx
*/
private static function isSignalStop()
{
return !empty($GLOBALS['signal_stop']);
}
/**
* 检查是否需要退出
* while中,且在代码之前
* @return void
* @throws \Exception
* @copyright lxx
* @author lxx
*/
public static function checkExit()
{
if (PHP_OS == 'Linux' && IS_CLI) {
// go on
} else {
return;
}
pcntl_signal_dispatch();
if (self::isSignalStop()) {
// 直接停止
echo '不再运行,直接停止' . PHP_EOL;
exit();
}
}
}