我需要将php脚本作为守护进程运行(等待说明并执行操作)。cron工作不会为我做这件事,因为指令一到达就需要立即采取措施。我知道由于内存管理问题,PHP并不是守护进程的最佳选择,但是由于种种原因,在这种情况下我必须使用PHP。我遇到了libslack的一个名为Daemon的工具(http://libslack.org/daemon),它似乎可以帮助我管理守护程序进程,但是最近5年没有任何更新,所以我想知道您是否知道一些其他适合我情况的选择。任何信息将不胜感激。
我需要将php脚本作为守护进程运行(等待说明并执行操作)。cron工作不会为我做这件事,因为指令一到达就需要立即采取措施。我知道由于内存管理问题,PHP并不是守护进程的最佳选择,但是由于种种原因,在这种情况下我必须使用PHP。我遇到了libslack的一个名为Daemon的工具(http://libslack.org/daemon),它似乎可以帮助我管理守护程序进程,但是最近5年没有任何更新,所以我想知道您是否知道一些其他适合我情况的选择。任何信息将不胜感激。
Answers:
您可以使用以下命令从命令行(即bash)启动php脚本
nohup php myscript.php &
在&
把你的程序在后台运行。
编辑:
是的,有一些缺点,但无法控制吗?那是错误的。
一个简单的kill processid
将停止它。而且它仍然是最好和最简单的解决方案。
nohup
也&
做同样的事情:将启动的进程与shell的当前实例分离。为什么我都需要它们?我可以不只是做php myscript.php &
还是nohup myscript.php
?? 谢谢
nohup php myscript.php > myscript.log &
另一种选择是使用Upstart。它最初是为Ubuntu开发的(默认情况下随附),但旨在适合所有Linux发行版。
这种方法类似于Supervisord和daemontools,因为它在系统启动时自动启动守护程序,并在脚本完成时重新生成。
在创建新的脚本文件/etc/init/myphpworker.conf
。这是一个例子:
# Info
description "My PHP Worker"
author "Jonathan"
# Events
start on startup
stop on shutdown
# Automatically respawn
respawn
respawn limit 20 5
# Run the script!
# Note, in this example, if your PHP script returns
# the string "ERROR", the daemon will stop itself.
script
[ $(exec /usr/bin/php -f /path/to/your/script.php) = 'ERROR' ] && ( stop; exit 1; )
end script
sudo service myphpworker start
sudo service myphpworker stop
sudo service myphpworker status
非常感谢Kevin Van Zonneveld,我从那里学习了这项技术。
sudo service myphpworker start/stop/status
仅适用于/etc/init.d
非新贵服务中的服务。@ matt-sich似乎发现了正确的语法。另一种选择是使用Gearman或Resque,它可以进行后台处理和除氨。
使用新的 systemd,您可以创建服务。
你必须创建一个文件或符号连接的 /etc/systemd/system/
,如。myphpdaemon.service并放置诸如此类的内容,myphpdaemon将成为服务的名称:
[Unit]
Description=My PHP Daemon Service
#May your script needs MySQL or other services to run, eg. MySQL Memcached
Requires=mysqld.service memcached.service
After=mysqld.service memcached.service
[Service]
User=root
Type=simple
TimeoutSec=0
PIDFile=/var/run/myphpdaemon.pid
ExecStart=/usr/bin/php -f /srv/www/myphpdaemon.php arg1 arg2> /dev/null 2>/dev/null
#ExecStop=/bin/kill -HUP $MAINPID #It's the default you can change whats happens on stop command
#ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartSec=42s
StandardOutput=null #If you don't want to make toms of logs you can set it null if you sent a file or some other options it will send all php output to this one.
StandardError=/var/log/myphpdaemon.log
[Install]
WantedBy=default.target
您将可以使用以下命令启动,获取状态,重新启动和停止服务
systemctl <start|status|restart|stop|enable> myphpdaemon
PHP脚本应具有某种“循环”以继续运行。
<?php
gc_enable();//
while (!connection_aborted() || PHP_SAPI == "cli") {
//Code Logic
//sleep and usleep could be useful
if (PHP_SAPI == "cli") {
if (rand(5, 100) % 5 == 0) {
gc_collect_cycles(); //Forces collection of any existing garbage cycles
}
}
}
工作示例:
[Unit]
Description=PHP APP Sync Service
Requires=mysqld.service memcached.service
After=mysqld.service memcached.service
[Service]
User=root
Type=simple
TimeoutSec=0
PIDFile=/var/run/php_app_sync.pid
ExecStart=/bin/sh -c '/usr/bin/php -f /var/www/app/private/server/cron/app_sync.php 2>&1 > /var/log/app_sync.log'
KillMode=mixed
Restart=on-failure
RestartSec=42s
[Install]
WantedBy=default.target
如果您的PHP例程应每个周期执行一次(例如diggest),则应使用shell或bash脚本而不是直接调用PHP来将其调用到systemd服务文件中,例如:
#!/usr/bin/env bash
script_path="/app/services/"
while [ : ]
do
# clear
php -f "$script_path"${1}".php" fixedparameter ${2} > /dev/null 2>/dev/null
sleep 1
done
如果你选择了这些选项,你应该改变KillMode到mixed
以流程时,bash(主)和PHP(儿童)被杀害。
ExecStart=/app/phpservice/runner.sh phpfile parameter > /dev/null 2>/dev/null
KillMode=process
This method also is effective if you're facing a memory leak.
注意:每次更改“ myphpdaemon.service”时,都必须运行“ systemctl daemon-reload”,但是请放心,如果不这样做,将在需要时向您发出警报。
systemctl status <your_service_name> -l
输出,它将为您提供线索。
如果可以,请在UNIX环境中获取Advanced Programming的副本。整个第13章致力于守护程序编程。示例在C中,但是您需要的所有功能在PHP中都有包装器(基本上是pcntl和posix扩展名)。
简而言之-编写守护程序(仅在基于* nix的OS-es上才可能使用-Windows使用服务)是这样的:
umask(0)
以防止权限问题。fork()
并有父母出口。setsid()
。SIGHUP
(通常会被忽略或用于向守护程序发送信号以重新加载其配置)和SIGTERM
(以告知进程正常退出)。fork()
再次并有父母出口。chdir()
。fclose()
stdin
,stdout
并且stderr
不给他们写信。正确的方法是将那些重定向到/dev/null
文件或文件,但是我找不到在PHP中执行此操作的方法。当您启动守护程序以使用shell重定向它们时,这是可能的(您必须了解如何做到这一点,我不知道:)。另外,由于您使用的是PHP,因此请小心使用循环引用,因为PHP 5.3之前的PHP垃圾收集器无法收集这些引用,并且该过程将内存泄漏,直到最终崩溃。
我运行了大量的PHP守护程序。
我同意您的看法,PHP并不是实现此目的的最佳(甚至不是一种好的)语言,但是守护程序与面向Web的组件共享代码,因此总体而言,这对我们来说是一个很好的解决方案。
我们为此使用daemontools。它是智能,清洁和可靠的。实际上,我们使用它来运行所有守护程序。
您可以在http://cr.yp.to/daemontools.html上进行检查。
编辑:功能的快速列表。
您可以
nohup
按照Henrik的建议使用。screen
和运行PHP程序作为常规过程。这给您比使用更多的控制权nohup
。我建议最简单的方法(我认为是屏幕),然后,如果您想要更多的功能,请转到更复杂的方法。
解决此问题的方法不止一种。
我不知道具体细节,但是也许还有另一种触发PHP进程的方法。例如,如果您需要代码基于SQL数据库中的事件运行,则可以设置触发器以执行脚本。在PostgreSQL下,这确实很容易做到:http : //www.postgresql.org/docs/current/static/external-pl.html。
老实说,我认为您最好的选择是使用nohup创建Damon进程。nohup允许命令即使在用户注销后也可以继续执行:
nohup php myscript.php &
但是,存在一个非常严重的问题。正如您所说的,PHP的内存管理器是完全垃圾,它是在脚本仅执行几秒钟然后存在的前提下构建的。几天后,您的PHP脚本将开始使用GIGABYTES内存。您还必须创建一个cron脚本,该脚本每12或24小时运行一次,它会杀死并重新生成php脚本,如下所示:
killall -3 php
nohup php myscript.php &
但是,如果脚本处于工作中间该怎么办?好kill -3是一个中断,与在CLI上执行ctrl + c相同。您的php脚本可以捕获此中断并使用PHP pcntl库正常退出:http ://php.oregonstate.edu/manual/en/function.pcntl-signal.php
这是一个例子:
function clean_up() {
GLOBAL $lock;
mysql_close();
fclose($lock)
exit();
}
pcntl_signal(SIGINT, 'clean_up');
$ lock背后的想法是PHP脚本可以使用fopen(“ file”,“ w”);打开文件。只有一个进程可以对文件进行写锁定,因此使用此过程可以确保仅运行PHP脚本的一个副本。
祝好运!
Kevin van Zonneveld 在这方面写了一篇非常不错的详细文章,在他的示例中,他使用了System_Daemon
PEAR包(最新发布日期为2009-09-02)。
查看https://github.com/shaneharter/PHP-Daemon
这是一个面向对象的守护程序库。它具有对日志记录和错误恢复之类的内置支持,并且具有创建后台工作程序的支持。
最近,我需要跨平台的解决方案(Windows,Mac和Linux)来解决将PHP脚本作为守护程序运行的问题。我通过编写自己的基于C ++的解决方案并编写二进制文件解决了该问题:
https://github.com/cubiclesoft/service-manager/
全面支持Linux(通过sysvinit),以及Windows NT服务和Mac OSX。
如果您只需要Linux,那么这里介绍的其他两个解决方案也可以很好地工作,并且取决于风格。这些天还出现了Upstart和systemd,它们回退到sysvinit脚本。但是使用PHP的一半是,它本质上是跨平台的,因此用该语言编写的代码有很大的机会按原样工作。当某些外部本机OS方面输入图片(例如系统服务)时,就会开始出现缺陷,但是大多数脚本语言都会遇到这个问题。
试图按照PHP用户国的建议在这里捕获信号不是一个好主意。pcntl_signal()
仔细阅读文档,您将很快了解到PHP使用一些相当不愉快的方法(特别是“滴答声”)来处理信号,这些方法会消耗大量周期来处理一些进程(例如信号)很少见的事情。PHP中的信号处理也仅在POSIX平台上才可用,并且支持因PHP版本而异。最初听起来像是一个不错的解决方案,但还远远不够真正有用。
随着时间的流逝,PHP在内存泄漏问题上也变得越来越好。您仍然必须小心(DOM XML解析器仍会泄漏),但是这些天我很少看到失控的过程,与过去相比,PHP Bug跟踪器非常安静。
正如其他人已经提到的那样,将PHP作为守护程序运行非常容易,并且可以使用单行命令来完成。但是实际的问题是保持它的运行和管理。我很早以前就遇到过同样的问题,尽管已经有很多解决方案可用,但是大多数解决方案都具有很多依赖性,或者很难使用并且不适合基本用法。我写了一个Shell脚本,可以管理任何进程/应用程序,包括PHP cli脚本。可以将其设置为cronjob以启动应用程序,并将包含该应用程序并对其进行管理。如果再次执行(例如通过同一cronjob再次执行),它将检查该应用程序是否正在运行,如果运行,则直接退出并让其先前的实例继续管理该应用程序。
我将其上传到github,随时使用它:https : //github.com/sinasalek/EasyDeamonizer
EasyDeamonizer
只需监视您的应用程序(启动,重新启动,日志,监视等)。一个通用脚本,以确保您的应用程序正常运行。故意使用pid / lock文件的进程名instread来防止其所有副作用,并使脚本尽可能简单和合理,因此,即使重新启动EasyDaemonizer本身,它也始终有效。特征
扩展Emil Ivaov答案,您可以执行以下操作以关闭php中的STDIN,STDOUT和STDERROR
if (!fclose(STDIN)) {
exit("Could not close STDIN");
}
if (!fclose(STDOUT)) {
exit("Could not close STDOUT");
}
if (!fclose(STDERR)) {
exit("Could not close STDERR");
}
$STDIN = fopen('/dev/null', 'r');
$STDOUT = fopen('/dev/null', 'w');
$STDERR = fopen('/var/log/our_error.log', 'wb');
基本上,您关闭了标准流,因此PHP没有写地方。以下fopen
调用会将标准IO设置为/dev/null
。
我已经从Rob Aley的书中阅读了此书-Web之外的PHP
我编写并部署了一个简单的php-daemon,代码在这里在线
https://github.com/jmullee/PhpUnixDaemon
功能:特权下降,信号处理,记录
我在队列处理程序中使用了它(用例:从网页触发冗长的操作,而无需等待页面生成的php等待,即启动异步操作) https://github.com/jmullee/PhpIPCMessageQueue
您可以在这里查看pm2,http: //pm2.keymetrics.io/
创建一个ssh文件,例如worker.sh放入要处理的php脚本中。
工人
php /path/myscript.php
守护程序启动
pm2 start worker.sh
干杯,就是这样。