Run php script as daemon process

2018-12-31 14:48发布

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.

14条回答
忆尘夕之涩
2楼-- · 2018-12-31 15:04

With new systemd you can create a service (on rhel based linux).

You must create a file or a symlink in /etc/systemd/system/, eg. myphpdaemon.service and place a content like this one, myphpdaemon will be the name of the service:

[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

You will be able to start, get status, restart and stop the services using the command

systemctl <start|status|restart|stop|enable> myphpdaemon

The PHP script should have a kind of "loop" to continue running.

<?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
        }
    }
}

Working example:

[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

If your PHP should be executed once in a cycle (like a diggest) you may should use a shell or bash script to be invoked into systemd service file instead of PHP directly, for example:

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

If you chose these option you should change the KillMode to mixed to processes, bash(main) and php(child) be killed.

ExecStart=/app/phpservice/runner.sh phpfile parameter  > /dev/null 2>/dev/null
KillMode=process

Note: Every time that you change your "myphpdaemon.service" you must run `systemctl daemon-reload', but do worry if you not do, it will be prompted when is needed.

查看更多
闭嘴吧你
3楼-- · 2018-12-31 15:11

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.

查看更多
看风景的人
4楼-- · 2018-12-31 15:12

Check out https://github.com/shaneharter/PHP-Daemon

This is an object-oriented daemon library. It has built-in support for things like logging and error recovery, and it has support for creating background workers.

查看更多
零度萤火
5楼-- · 2018-12-31 15:12

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.

查看更多
与风俱净
6楼-- · 2018-12-31 15:15

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.

查看更多
临风纵饮
7楼-- · 2018-12-31 15:17

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

查看更多
登录 后发表回答