Run php script as daemon process

Viewed 164346

I need to run a php script as daemon process (wait for instructions and do stuff). cron job will not do it for me because actions need to be taken as soon as instruction arrives. I know PHP is not really the best option for daemon processes due to memory management issues, but due to various reasons I have to use PHP in this case. I came across a tool by libslack called Daemon (http://libslack.org/daemon) it seems to help me manage daemon processes, but there hasn't been any updates in the last 5 years, so I wonder if you know some other alternatives suitable for my case. Any information will be really appreciated.

15 Answers

You could start your php script from the command line (i.e. bash) by using

nohup php myscript.php &

the & puts your process in the background.

Edit:
Yes, there are some drawbacks, but not possible to control? That's just wrong.
A simple kill processid will stop it. And it's still the best and simplest solution.

If you can - grab a copy of Advanced Programming in the UNIX Environment. The entire chapter 13 is devoted to daemon programming. Examples are in C, but all the function you need have wrappers in PHP (basically the pcntl and posix extensions).

In a few words - writing a daemon (this is posible only on *nix based OS-es - Windows uses services) is like this:

  1. Call umask(0) to prevent permission issues.
  2. fork() and have the parent exit.
  3. Call setsid().
  4. Setup signal processing of SIGHUP (usually this is ignored or used to signal the daemon to reload its configuration) and SIGTERM (to tell the process to exit gracefully).
  5. fork() again and have the parent exit.
  6. Change the current working dir with chdir().
  7. fclose() stdin, stdout and stderr and don't write to them. The corrrect way is to redirect those to either /dev/null or a file, but I couldn't find a way to do it in PHP. It is possible when you launch the daemon to redirect them using the shell (you'll have to find out yourself how to do that, I don't know :).
  8. Do your work!

Also, since you are using PHP, be careful for cyclic references, since the PHP garbage collector, prior to PHP 5.3, has no way of collecting those references and the process will memory leak, until it eventually crashes.

I run a large number of PHP daemons.

I agree with you that PHP is not the best (or even a good) language for doing this, but the daemons share code with the web-facing components so overall it is a good solution for us.

We use daemontools for this. It is smart, clean and reliable. In fact we use it for running all of our daemons.

You can check this out at http://cr.yp.to/daemontools.html.

EDIT: A quick list of features.

  • Automatically starts the daemon on reboot
  • Automatically restart dameon on failure
  • Logging is handled for you, including rollover and pruning
  • Management interface: 'svc' and 'svstat'
  • UNIX friendly (not a plus for everyone perhaps)

You can

  1. Use nohup as Henrik suggested.
  2. Use screen and run your PHP program as a regular process inside that. This gives you more control than using nohup.
  3. Use a daemoniser like http://supervisord.org/ (it's written in Python but can daemonise any command line program and give you a remote control to manage it).
  4. Write your own daemonise wrapper like Emil suggested but it's overkill IMO.

I'd recommend the simplest method (screen in my opinion) and then if you want more features or functionality, move to more complex methods.

There is more than one way to solve this problem.

I do not know the specifics but perhaps there is another way to trigger the PHP process. For instance if you need the code to run based on events in a SQL database you could setup a trigger to execute your script. This is really easy to do under PostgreSQL: http://www.postgresql.org/docs/current/static/external-pl.html .

Honestly I think your best bet is to create a Damon process using nohup. nohup allows the command to continue to execute even after the user has logged out:

nohup php myscript.php &

There is however a very serious problem. As you said PHP's memory manager is complete garbage, it was built with the assumption that a script is only executing for a few seconds and then exists. Your PHP script will start to use GIGABYTES of memory after only a few days. You MUST ALSO create a cron script that runs every 12 or maybe 24 hours that kills and re-spawns your php script like this:

killall -3 php
nohup php myscript.php &

But what if the script was in the middle of a job? Well kill -3 is an interrupt, its the same as doing a ctrl+c on the CLI. Your php script can catch this interrupt and exit gracefully using the PHP pcntl library: http://php.oregonstate.edu/manual/en/function.pcntl-signal.php

Here is an example:

function clean_up() {
  GLOBAL $lock;
  mysql_close();
  fclose($lock)
  exit();
}
pcntl_signal(SIGINT, 'clean_up');

The idea behind the $lock is that the PHP script can open a file with a fopen("file","w");. Only one process can have a write lock on a file, so using this you can make sure that only one copy of your PHP script is running.

Good Luck!

you can check pm2 here is, http://pm2.keymetrics.io/

create a ssh file, such as worker.sh put into your php script that you will deal with.

worker.sh

php /path/myscript.php

daemon start

pm2 start worker.sh

Cheers, that is it.

I was looking for a simple solution without having to install additional stuff and compatible with common hostings that allow SSH access.

I've ended up with this setup for my chat server:

-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

And the scripts:

startChatServer.sh

#!/bin/bash
nohup /opt/plesk/php/5.6/bin/php -q /var/www/vhosts/crazypoems.org/httpdocs/chat/php-socket.php > /dev/null &

stopChatServer.sh

#!/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

restartChatServer.sh

#!/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

And last but not least, I have put this last script on a cron job, to check if the "chat server is running", and if not, to start it:

Cron job every minute

-bash-4.1$ crontab -l
*       *       *       *       * /var/www/vhosts/crazypoems.org/httpdocs/chat/checkChatServerRunning.sh > /var/www/vhosts/crazypoems.org/httpdocs/chat/cron.log

checkChatServerRunning.sh

#!/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

So with this setup:

  • I manually can control the service with the scripts when needed (eg: maintenance)
  • The cron job will start the server on reboot, or on crash

Extending Emil Ivaov answer, You can do the following to close STDIN, STDOUT AND STDERROR in php

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');

Basically you close the standard streams so that PHP has no place to write. The following fopen calls will set the standard IO to /dev/null.

I have read this from book of Rob Aley - PHP beyond the web

Related