具有自定义信号处理程序的PHP SIGINT无法终止CURL
我有一个带有自定义关机处理程序的PHP命令行应用程序:具有自定义信号处理程序的PHP SIGINT无法终止CURL,php,curl,sigint,Php,Curl,Sigint,我有一个带有自定义关机处理程序的PHP命令行应用程序: <?php declare(ticks=1); $shutdownHandler = function () { echo 'Exiting'; exit(); }; pcntl_signal(SIGINT, $shutdownHandler); while (true) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'http://bla
<?php
declare(ticks=1);
$shutdownHandler = function () {
echo 'Exiting';
exit();
};
pcntl_signal(SIGINT, $shutdownHandler);
while (true) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://blackhole.webpagetest.org');
curl_exec($ch);
curl_close($ch);
}
<代码> > p>我已经用了一段时间来搔过头,没有用PCNTL找到任何最佳的解决方案,但是根据您的命令的预期用途,您可以考虑:
- 使用超时:
curl_setopt($ch,CURLOPT_CONNECTTIMEOUT,15)代码>
您可能已经知道,有很多方法可以控制卷曲的行为,包括设置卷曲 - 使用
而不是curlfile\u get\u contents()
$w = new EvSignal(SIGINT, function (){
exit("SIGINT received\n");
});
Ev::run();
电动汽车分机安装的快速总结:sudo pecl安装电动汽车
您需要通过添加到php cli的php.ini文件来启用扩展名,该文件可能是/etc/php/7.0/cli/php.ini
extension=“ev.so”
如果pecl抱怨缺少phpize,请安装php7.0-dev.什么工作? 真正起作用的是给整个系统一些空间来发挥它的信号处理魔力。这样的空间似乎是通过启用cURL的进度处理来提供的,同时还设置了一个“userland”进度回调: 解决方案1 似乎进度回调函数中需要“某些东西”。空主体似乎不起作用,因为它可能只是没有给PHP太多时间进行信号处理(核心推测)
解决方案2 将
pcntl\u signal\u dispatch()
...
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, function() {
pcntl_signal_dispatch();
});
...
解决方案3❤ (菲律宾比索7.1+)
使用pcntl\u async\u信号(true)
而不是declare(ticks=1)代码>即使在进程回调函数体为空的情况下也能工作
这可能是我个人会使用的,所以我将把完整的代码放在这里:
<?php
pcntl_async_signals(true);
$shutdownHandler = function() {
die("Exiting\n");
};
pcntl_signal(SIGINT, $shutdownHandler);
while (true) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_NOPROGRESS, false);
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, function() {});
curl_setopt($ch, CURLOPT_URL, 'http://blackhole.webpagetest.org');
curl_exec($ch);
curl_close($ch);
}
发生了什么事?
发送Ctrl+C
命令时,PHP
尝试在退出之前完成当前操作
为什么我的(OP)代码不退出?
有关更详细的解释,请参见最后的想法
您的代码不会退出,因为cURL
没有完成,因此PHP
在完成当前操作之前无法退出
您为本练习选择的网站从未加载
如何修复
要修复此问题,请将URL替换为确实加载的内容,例如
证明
我编写了自己的代码示例,以准确显示PHP
决定退出的时间/地点:
<?php
declare(ticks=1);
$t = 1;
$shutdownHandler = function () {
exit("EXITING NOW\n");
};
pcntl_signal(SIGINT, $shutdownHandler);
while (true) {
print "$t\n";
$t++;
}
然后运行:
中提琴正如预期的那样,脚本在当前进程完成后终止,就像其预期的那样
最后的想法
您试图卷曲的站点实际上是在无止境地发送代码。唯一能够停止进程的操作是CTRL+X
、设置或cURL选项。当您取出pcntl_信号(SIGINT,$shutdownHandler)时,CTRL+C
的原因起作用
是因为PHP不再承担由内部函数正常关闭的负担。由于PHP不是并发的,当处理程序进入时,它必须等待轮到它执行,因为cURL
任务永远不会完成,因此会给您留下永无止境的结果。
我希望这有帮助如果可以升级到PHP7.1.X
或更高版本,我会使用@smuf发布的解决方案3
。那是干净的
但是,如果您无法升级,则需要使用变通方法。问题的发生如以下SO线程中所述
PHP在阻塞IO中很忙,无法处理您已自定义处理程序的挂起信号。每当一个信号被触发,并且您有与之相关联的处理程序时,PHP都会对其进行排队。PHP完成当前操作后,将执行处理程序
但不幸的是,在这种情况下,它永远不会有这样的机会,您没有为CURL指定超时,它只是一个PHP无法逃脱的黑洞
因此,如果您可以让一个php进程负责处理SIGTERM
,而一个子进程必须处理该工作,那么它就可以工作了,因为父进程将能够捕获信号并处理回调,即使子进程被同一个进程占用
下面是一段代码,它演示了相同的功能
<?php
$pid = pcntl_fork();
if ($pid == -1) {
die('could not fork');
} else if ($pid) {
// we are the parent
declare (ticks = 1);
//pcntl_async_signals(true);
$shutdownHandler = function()
{
echo 'Exiting' . PHP_EOL;
};
pcntl_signal(SIGINT, $shutdownHandler);
pcntl_wait($status); //Protect against Zombie children
} else {
// we are the child
echo "hanging now" . PHP_EOL;
while (true) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://blackhole.webpagetest.org');
curl_exec($ch);
curl_close($ch);
}
}
为什么你认为CURL是对信号做出反应,而不是完全忽略它?@IMSoP对不起,我的问题可能有点误导(我已经更新了)。问题的关键是为什么卷曲在这个场景中是不可杀死的。CURL是一个外部的库,所以在操作过程中杀死它是不容易的/可能的。你必须弄清楚如何连接到默认的信号处理程序中才能正确地终止它。我认为答案在于,问题是你的curl调用被阻塞io卡住了,php根本无法连接到你的信号处理程序,而设置处理程序会禁用原来的处理程序together@Jonathan,如果不在7.0.X
中重新编译代码,则无法执行此操作。看看这个要点是否是您可以接受的解决方案,如果是这样的话,我会发布一个回复谢谢你的详细回复,但是选择一个缓慢加载的URL是问题的关键。如果未定义信号处理程序,则请求将始终被终止,即使它正在进行。当定义了信号处理程序时,这种行为会发生变化,这正是我感兴趣的地方。@Jonathan我详细介绍了为什么它不适用于您的URL。请阅读整个答案。您的代码不会退出,因为cURL没有完成,所以PHP在完成当前操作之前无法退出。从字面上来说就是答案,唯一的解决办法就是更改URL。当你去掉pcntl_信号(SIGINT,$s)时,CTRL+C工作的原因是
<?php
declare(ticks=1);
$t = 1;
$shutdownHandler = function () {
exit("\nEXITING\n");
};
pcntl_signal(SIGINT, $shutdownHandler);
while (true) {
echo "CURL$t\n";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://google.com');
curl_exec($ch);
curl_close($ch);
}
<?php
$pid = pcntl_fork();
if ($pid == -1) {
die('could not fork');
} else if ($pid) {
// we are the parent
declare (ticks = 1);
//pcntl_async_signals(true);
$shutdownHandler = function()
{
echo 'Exiting' . PHP_EOL;
};
pcntl_signal(SIGINT, $shutdownHandler);
pcntl_wait($status); //Protect against Zombie children
} else {
// we are the child
echo "hanging now" . PHP_EOL;
while (true) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://blackhole.webpagetest.org');
curl_exec($ch);
curl_close($ch);
}
}