我需要运行一个php脚本作为守护程序进程(等待指令并做一些事情)。 cron job不会为我做,因为一旦指令到达就需要采取行动。我知道由于内存管理问题,PHP实际上不是守护进程的最佳选择,但由于各种原因,我必须在这种情况下使用PHP。我遇到了一个名为守护进程(http://libslack.org/daemon)的libslack工具,它似乎可以帮助我管理守护程序进程,但是在过去的5年中没有任何更新,所以我想知道你是否知道其他一些适用于我的情况。任何信息都将非常感激。
答案 0 :(得分:159)
您可以使用
从命令行(即bash)启动PHP脚本 nohup php myscript.php &
&
将您的流程置于后台。
编辑:
是的,有一些缺点,但无法控制?那是错的。
一个简单的kill processid
会阻止它。它仍然是最好,最简单的解决方案。
答案 1 :(得分:154)
另一种选择是使用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,我从中学习了这项技术。
答案 2 :(得分:49)
使用新的systemd,您可以创建服务。
您必须在/etc/systemd/system/
中创建文件或symlink,例如。 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脚本应该有一种&#34;循环&#34;继续跑步。
<?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脚本直接调用systemd服务文件而不是PHP,例如:
#!/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(main)和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.
注意:每次更改&#34; myphpdaemon.service&#34;你必须 运行`systemctl daemon-reload&#39;,但是如果你不这样做就会担心,它会 在需要时发出警报。
答案 3 :(得分:47)
如果可以 - 抓取Advanced Programming in the UNIX Environment的副本。整个第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垃圾收集器无法收集这些引用,并且该进程将内存泄漏,直到它最终崩溃。
答案 4 :(得分:24)
我运行了大量的PHP守护进程。
我同意你的看法,PHP并不是最好的(甚至是好的)语言,但守护进程与面向Web的组件共享代码,所以总体来说它对我们来说是一个很好的解决方案。
我们为此使用daemontools。它聪明,干净,可靠。事实上,我们使用它来运行所有守护进程。
您可以在http://cr.yp.to/daemontools.html处查看。
编辑:快速的功能列表。
答案 5 :(得分:14)
你可以
nohup
作为Henrik的建议。screen
并将PHP程序作为常规进程运行。与使用nohup
相比,这可以提供更多控制。 我建议使用最简单的方法(我认为是屏幕)然后如果你想要更多的功能或功能,请转向更复杂的方法。
答案 6 :(得分:11)
解决此问题的方法不止一种。
我不知道具体细节,但也许还有另一种触发PHP过程的方法。例如,如果您需要基于SQL数据库中的事件运行代码,则可以设置触发器来执行脚本。这在PostgreSQL下很容易做到:http://www.postgresql.org/docs/current/static/external-pl.html。
老实说,我认为最好的办法是使用nohup创建一个Damon进程。 nohup允许命令在用户注销后继续执行:
nohup php myscript.php &
然而,有一个非常严重的问题。正如你所说,PHP的内存管理器是完全垃圾,它是在假设脚本只执行几秒然后存在的情况下构建的。您的PHP脚本将在几天后开始使用GIGABYTES内存。您还必须创建一个每12或24小时运行一次的cron脚本,这样可以杀死并重新生成您的php脚本,如下所示:
killall -3 php
nohup php myscript.php &
但是如果脚本正在工作中呢?杀-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脚本的一个副本。
祝你好运!
答案 7 :(得分:10)
Kevin van Zonneveld wrote a very nice detailed article on this,在他的例子中,他使用System_Daemon
PEAR package(2009-09-02上次发布日期)。
答案 8 :(得分:6)
查看https://github.com/shaneharter/PHP-Daemon
这是一个面向对象的守护程序库。它内置了对日志记录和错误恢复等内容的支持,并且支持创建后台工作程序。
答案 9 :(得分:3)
我最近需要一个跨平台解决方案(Windows,Mac和Linux)来解决将PHP脚本作为守护进程运行的问题。我通过编写自己的基于C ++的解决方案并制作二进制文件解决了这个问题:
https://github.com/cubiclesoft/service-manager/
完全支持Linux(通过sysvinit),但也启动了Windows NT服务和Mac OSX。
如果您只需要Linux,那么此处介绍的其他几种解决方案效果不错,具体取决于风格。现在还有Upstart和systemd,它们都有回溯到sysvinit脚本。但使用PHP的一半原因是它本质上是跨平台的,因此用该语言编写的代码很有可能在任何地方工作。当某些外部本机操作系统级别的方面进入图片(例如系统服务)时会出现缺陷,但是您会遇到大多数脚本语言的问题。
尝试捕获信号,因为这里有人在PHP用户区建议不是一个好主意。仔细阅读pcntl_signal()
上的文档,您将很快了解到PHP使用一些相当令人不愉快的方法处理信号(特别是,&#39; ticks&#39;),这些方法为进程中很少见到的东西咀嚼了一堆循环(即信号)。 PHP中的信号处理在POSIX平台上也几乎不可用,并且支持因PHP的版本而异。它最初听起来像是一个不错的解决方案,但它确实没有真正有用。
随着时间的推移,PHP在内存泄漏问题上的表现也越来越好。你仍然需要小心(DOM XML解析器往往会泄漏)但我现在很少看到失控的进程,而且与以前的日子相比,PHP bug跟踪器相当安静。
答案 10 :(得分:2)
我一直在寻找一种简单的解决方案,而无需安装额外的东西,并且与允许 SSH 访问的常见主机兼容。
我已经为我的聊天服务器完成了这个设置:
-rwxr-xr-x 1 crazypoems psacln 309 ene 30 14:01 checkChatServerRunning.sh
-rw-r--r-- 1 crazypoems psacln 3018 ene 30 13:12 class.chathandler.php
-rw-r--r-- 1 crazypoems psacln 29 ene 30 14:05 cron.log
-rw-r--r-- 1 crazypoems psacln 2560 ene 29 08:04 index.php
-rw-r--r-- 1 crazypoems psacln 2672 ene 30 13:29 php-socket.php
-rwxr-xr-x 1 crazypoems psacln 310 ene 30 14:04 restartChatServer.sh
-rwxr-xr-x 1 crazypoems psacln 122 ene 30 13:28 startChatServer.sh
-rwxr-xr-x 1 crazypoems psacln 224 ene 30 13:56 stopChatServer.sh
和脚本:
#!/bin/bash
nohup /opt/plesk/php/5.6/bin/php -q /var/www/vhosts/crazypoems.org/httpdocs/chat/php-socket.php > /dev/null &
#!/bin/bash
PID=`ps -eaf | grep '/var/www/vhosts/crazypoems.org/httpdocs/chat/php-socket.php' | grep -v grep | awk '{print $2}'`
if [[ "" != "$PID" ]]; then
echo "killing $PID"
kill -9 $PID
else
echo "not running"
fi
#!/bin/bash
PID=`ps -eaf | grep '/var/www/vhosts/crazypoems.org/httpdocs/chat/php-socket.php' | grep -v grep | awk '{print $2}'`
if [[ "" != "$PID" ]]; then
echo "killing $PID"
kill -9 $PID
else
echo "not running"
fi
echo "Starting again"
/var/www/vhosts/crazypoems.org/httpdocs/chat/startChatServer.sh
最后但并非最不重要的一点是,我已将最后一个脚本放在 cron 作业中,以检查“聊天服务器是否正在运行”,如果没有,则启动它:
-bash-4.1$ crontab -l
* * * * * /var/www/vhosts/crazypoems.org/httpdocs/chat/checkChatServerRunning.sh > /var/www/vhosts/crazypoems.org/httpdocs/chat/cron.log
#!/bin/bash
PID=`ps -eaf | grep '/var/www/vhosts/crazypoems.org/httpdocs/chat/php-socket.php' | grep -v grep | awk '{print $2}'`
if [[ "" != "$PID" ]]; then
echo "Chat server running on $PID"
else
echo "Not running, going to start it"
/var/www/vhosts/crazypoems.org/httpdocs/chat/startChatServer.sh
fi
所以有了这个设置:
答案 11 :(得分:1)
正如其他人已经提到的,将PHP作为守护进程运行非常简单,并且可以使用单行命令来完成。但实际问题是保持运行和管理它。我很久以前就遇到过同样的问题,尽管已经有很多解决方案,但是大多数解决方案都存在很多依赖关系或难以使用,不适合基本用法。我编写了一个shell脚本,可以管理任何进程/应用程序,包括PHP cli脚本。它可以设置为cronjob来启动应用程序,并将包含应用程序并对其进行管理。如果它再次执行,例如通过相同的cronjob,它会检查应用程序是否正在运行,如果确实如此,则只需退出并让其先前的实例继续管理应用程序。
我将它上传到github,随意使用它:https://github.com/sinasalek/EasyDeamonizer
<强> EasyDeamonizer 强>
只需查看您的应用程序(启动,重启,登录,监控等)。一个通用脚本,以确保您的应用程序保持正常运行。故意它使用pid / lock文件的进程名称instread来防止其所有副作用并使脚本保持尽可能简单和尽头,因此即使EasyDaemonizer本身重新启动它也总是有效。 特征
答案 12 :(得分:1)
扩展 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-PHP超越网络
的书中阅读了此书答案 13 :(得分:0)
我编写并部署了一个简单的php-daemon,代码在这里
https://github.com/jmullee/PhpUnixDaemon
功能:权限下降,信号处理,记录
我在队列处理程序中使用它(用例:从网页触发一个冗长的操作,而不会使页面生成php等待,即启动异步操作) https://github.com/jmullee/PhpIPCMessageQueue
答案 14 :(得分:0)
您可以在这里查看pm2,http://pm2.keymetrics.io/
在要处理的php脚本中创建一个ssh文件,例如worker.sh。
worker.sh
php /path/myscript.php
守护进程启动
pm2 start worker.sh
干杯,就是这样。