PHP Advanced Programming daemon for graceful reboots

Source: Internet
Author: User
Tags ack

The daemon of PHP advanced programming http://netkiller.github.io/journal/php.daemon.html Mr.Neo Chen (Chen Jingfeng),Netkiller, Bg7nyt


China Guangdong province Shenzhen Khe Sanh Street, Longhua District, civil Administration
518131
+86 13113668890
+86 755 29812080
<[email protected]>

Copyright? Http://netkiller.github.io

Copyright Notice

Reprint please contact the author, please be sure to indicate the original source of the article and the author's information and this statement.

Document Source:
Http://netkiller.github.io
Http://netkiller.sourceforge.net

Scan QR code to enter Netkiller subscription number

QQ Group: 128659835 Please specify "reader"

2014-09-01

Summary

Published 2014-09-01

2015-08-31 Update

2015-10-20 Update, add graceful restart

My series of documents
Netkiller Architect Codex Netkiller Developer Codex Netkiller PHP Codex Netkiller Python Codex Netkiller Testing Codex
Netkiller Cryptography Codex Netkiller Linux Codex Netkiller Debian Codex Netkiller CentOS Codex Netkiller FreeBSD Codex
Netkiller Shell Codex Netkiller Security Codex Netkiller Web Codex Netkiller Monitoring Codex Netkiller Storage Codex
Netkiller Mail Codex Netkiller Docbook Codex Netkiller Project Codex Netkiller Database Codex Netkiller PostgreSQL Codex
Netkiller MySQL Codex Netkiller NoSQL Codex Netkiller LDAP Codex Netkiller Network Codex Netkiller Cisco IOS Codex
Netkiller H3C Codex Netkiller Multimedia Codex Netkiller Perl Codex Netkiller Amateur Radio Codex Netkiller DevOps Codex

You can use ibook to read the current document

Directory
    • 1. What is daemon process
    • 2. Why the daemon is being developed
    • 3. When to use Daemons to develop applications
    • 4. Security issues with Daemons
    • 5. How to Develop Daemons
      • 5.1. Program Startup
      • 5.2. Program Stop
      • 5.3. Single-Case mode
      • 5.4. Achieve Graceful restart
    • 6. The process exits the solution unexpectedly
1. What is daemon process

Daemons are processes that are detached from the terminal and run in the background. The daemon is detached from the terminal to prevent the information in the process from being displayed on any terminal and the process will not be interrupted by terminal information generated by any terminal.

Like Apache, Nginx, and MySQL are daemons.

2. Why the daemon is being developed

Many programs exist in the form of services, and he has no terminal or UI interaction, and it may interact with other programs in other ways, such as TCP/UDP sockets, UNIX sockets, FIFO. Once the program is started, it goes backstage until the condition is met and he begins to process the task.

3. When to use daemons to develop applications

Take my current needs as an example, I need to run a program, and then listen to a port, continue to accept data initiated by the server, and then analyze the data processing, and then write the results into the database; I use ZEROMQ to achieve data transmission and dispatch.

If I do not adopt the Daemon way to develop the program, the program once run will occupy the current terminal window frame, and is affected by the current terminal keyboard input, there may be a program mistakenly quit.

4. Security issues with Daemons

We want the program to run in a non-superuser, so that once the program is compromised by hackers, the attacker can only inherit the permissions of the operation and not gain superuser privileges.

We want the program to run only one instance, not to run a colleague to open more than two programs, because there will be port conflicts and so on.

5. How to develop Daemon Example 1. Multithreading Daemon Example
<?phpclass Exampleworker extends Worker {#public function __construct (Logging $logger) {# $this->logger = $logger; # } #protected $logger;p rotected static $dbh;p ublic function __construct () {}public function run () {$dbhost = ' 192.168.2.1 ';    Database server $dbport = 3306;        $dbuser = ' www ';             Database user Name $dbpass = ' qwer123 '; Database Password $dbname = ' example ';//Database name self:: $DBH = new PDO ("mysql:host= $dbhost;p ort= $dbport;d bname= $dbname", $dbuser, $ Dbpass, Array (/* Pdo::mysql_attr_init_command = ' SET NAMES \ ' utf8\ ', */pdo::mysql_attr_compress = True,pdo:: Attr_persistent = True));}    protected function getinstance () {return self:: $DBH; }}/* the collectable class implements machinery for Pool::collect */class Fee extends Stackable {public Function __constru CT ($msg) {$trades = Explode (",", $msg); $this->data = $trades;p rint_r ($trades);} Public Function Run () {# $this->worker->logger->log ('%s executing in Thread #%lu ', __class__, $this->worker- >getThreadId ()); try {$dbh = $this->worker->getinstance (); $insert = "INSERT into fee (ticket, Login, volume, ' status ') V Alues (: Ticket,: Login,: Volume, ' N ') "; $sth = $dbh->prepare ($insert); $sth->bindvalue (': Ticket ', $this->data[ 0]); $sth->bindvalue (': Login ', $this->data[1]), $sth->bindvalue (': Volume ', $this->data[2]); $sth  Execute (); $sth = null;/* ... */$update = "Update fee SET ' status ' = ' Y ' WHERE ticket =: Ticket and ' status ' = ' N '"; $sth = $dbh->prepare ($update); $sth->bindvalue (': Ticket ', $this->data[0]); $sth->execute ();//echo $sth querystring;//$DBH = null;} catch (Pdoexception $e) {$error = sprintf ("%s,%s\n", $mobile, $id) file_put_contents ("Mobile_error.log", $error, File_ APPEND);}}} Class Example {/* config */const LISTEN = "tcp://192.168.2.15:5555"; const MAXCONN = 100;const Pidfile = __class__;const UI d= 80;const gid= 80;protected $pool = null;protected $zmq = null;public function __construct () {$this->pidfile = '/var/ Run/'. Self::p idfilE. '. pid ';} Private Function Daemon () {if (file_exists ($this->pidfile)) {echo "The file $this->pidfile exists.\n"; exit ();}  $pid = Pcntl_fork (), if ($pid = =-1) {die (' could not fork '),} else if ($pid) {//We are the parent//pcntl_wait ($status); Protect against Zombie Childrenexit ($pid);} else {//We is the childfile_put_contents ($this->pidfile, Getmypid ());p Osix_setuid (self::uid);p osix_setgid (self: : GID); return (Getmypid ());}} Private Function Start () {$pid = $this->daemon (); $this->pool = new Pool (self::maxconn, \exampleworker::class, []); $this->zmq = new Zmqsocket (new Zmqcontext (), zmq::socket_rep); $this->zmq->bind (Self::listen);/* Loop Receiving and echoing back */while ($message = $this->zmq->recv ()) {//print_r ($message);//if ($trades) {$this  Pool->submit (New Fee ($message)); $this->zmq->send (' TRUE ');  }else{//$this->zmq->send (' FALSE '); }} $pool->shutdown ();} Private Function Stop () {if (file_exists ($this->pidfile)) {$pid = File_get_cOntents ($this->pidfile);p Osix_kill ($pid, 9); Unlink ($this->pidfile);}} Private function Help ($proc) {printf ("%s Start | Stop | Help \ n ", $proc);} Public Function Main ($ARGV) {if (count ($ARGV) < 2) {printf ("parameter\n"); exit (); if ($argv [1] = = = ' Stop ') {$this->stop ();} else if ($argv [1] = = = ' Start ') {$this->start ();} else{$this->help ($argv [0]);}} $cgse = new Example (); $cgse->main ($ARGV);

Example 2. Message Queuing and Daemons
<?phpdeclare (ticks = 1); require_once (__dir__. ' /autoload.class.php '); Umask (077); class EDM {protected $queue;p ublic function __construct () {global $ARGC, $argv; $this- &GT;ARGC = $ARGC; $this->argv = $argv; $this->pidfile = $this->argv[0]. ". pid "; $this->config = new config (' MQ '); $this->logging = new Logging (__dir__. ') /log/'. $this->argv[0]. '. '. Date (' y-m-d '). Log '); //. H:i:s//print_r ($this->config->getarray (' MQ ')),//pcntl_signal (SIGHUP, Array (& $this, "restart"));} protected function Msgqueue () {$exchangeName = ' email ';//switch name $queuename = ' email ';//queue name $routekey = ' email ';//route key//Create connection Connect and channel$connection = new Amqpconnection ($this->config->getarray (' MQ ')), if (! $connection->connect ()) {die ("Cannot connect to the broker!\n");} $this->channel = new Amqpchannel ($connection), $this->exchange = new Amqpexchange ($this->channel); $this Exchange->setname ($exchangeName); $this->exchange->settype (Amqp_ex_type_direct); Direct Type $this->exchange->setflags (amqp_durable); Persistent $this->exchange->declare ();//echo "Exchange Status:". $this->exchange->declare (). " \ n ";//Create queue $this->queue = new Amqpqueue ($this->channel); $this->queue->setname ($queueName); $this Queue->setflags (amqp_durable); Persistent $this->queue->declare ();//echo "Message total:". $this->queue->declare (). " \ n ";//Bind the switch with the queue and specify the routing key $bind = $this->queue->bind ($exchangeName, $routeKey);//echo ' queue bind: '. $bind." \ n ";//block mode Receive message while (TRUE) {//$this->queue->consume (' ProcessMessage ', amqp_autoack);//Auto Ack answer $this-> Queue->consume (function ($envelope, $queue) {$msg = $envelope->getbody (); $queue->ack ($envelope Getdeliverytag ()); Manually Send ACK response $this->logging->info (' ('. ') + '. ') '. $MSG);//$this->logging->debug ("Message total:". $this->queue->declare ());}); $this->channel->qos (0,1);//echo "Message total:". $this->queue->declare (). " \ n ";} $conn->disconnect ();} protected function Start () {if (File_exists ($This->pidfile) {printf ("%s already running\n", $this->argv[0]); exit (0);} $this->logging->warning ("Start"), $pid = Pcntl_fork (), if ($pid = =-1) {die (' could not fork '),} else if ($pid) {//pcnt L_wait ($status); Wait for the child process to break to prevent the child process from becoming a zombie process. Exit (0);} else {posix_setsid ();//printf ("pid:%s\n", Posix_getpid ()); File_put_contents ($this->pidfile, Posix_getpid ());// Posix_kill (Posix_getpid (), SIGHUP); $this->msgqueue ();}} protected function Stop () {if (file_exists ($this->pidfile)) {$pid = file_get_contents ($this->pidfile);p osix_ Kill ($pid, SIGTERM);//posix_kill ($pid, SIGKILL); unlink ($this->pidfile); $this->logging->warning ("Stop");} else{printf ("%s haven ' t running\n", $this->argv[0]);}} protected function Restart () {$this->stop (); $this->start ();} protected function Status () {if (file_exists ($this->pidfile)) {$pid = file_get_contents ($this->pidfile);p rintf ( "%s already running, PID =%s\n", $this->argv[0], $pid);} else{printf ("%s haven ' t running\n", $this->argv[0]);}} protected function usage () {printf ("Usage:%s {start | Stop | Restart | Status}\n ", $this->argv[0]);} Public Function Main () {//print_r ($this->argv), if ($this->argc! = 2) {$this->usage ();} Else{if ($this->argv[1] = = ' start ') {$this->start ();} else if ($this->argv[1] = = ' Stop ') {$this->stop ();} else if ($this->argv[1] = = ' restart ') {$this->restart ();} else if ($this->argv[1] = = ' status ') {$this->status ();} else{$this->usage ();}}} $EDM = New EDM (); $edm->main ();

5.1. Program Startup

Here's the code that goes backstage after the program starts

The process ID file to determine the current process state, if the process ID file exists to indicate that the program is running, through code file_exists ($this->pidfile) implementation, but then the process is killed need to manually delete the file to run

Private Function Daemon () {if (file_exists ($this->pidfile)) {echo "The file $this->pidfile exists.\n"; exit ();} $pid = Pcntl_fork (), if ($pid = =-1) {die (' could not fork '),} else if ($pid) {//We are the parent//pcntl_wait ($status); /protect against Zombie Childrenexit ($pid);} else {//We is the childfile_put_contents ($this->pidfile, Getmypid ());p Osix_setuid (self::uid);p osix_setgid (self: : GID); return (Getmypid ());}}

After the program starts, the parent process is rolled out, the child process runs in the background, the child process permissions are switched from root to the specified user, and the PID is written to the process ID file.

5.2. Program Stop

The program stops, just read the PID file and then call Posix_kill ($pid, 9); Finally, the file is deleted.

Private Function Stop () {if (file_exists ($this->pidfile)) {$pid = file_get_contents ($this->pidfile);p Osix_kill ( $pid, 9); Unlink ($this->pidfile);}}
5.3. Single-Case mode

All threads share the database connection, which is very important in multi-threading, and if each thread is established with this database connection closed, this is a huge cost to the database.

protected function getinstance () {return self:: $DBH;}
5.4. Achieve Graceful restart

The so-called graceful restart is the case that the process does not quit and the implementation reload contains reset variables, refreshes the configuration file, resets the log, and so on

Stop/start or restart will exit the process and reboot, causing the process ID to change, while an instant exit causes the business to flash. So many daemons will provide a reload function, which is called graceful restart.

Reload implementation principle is to send SIGHUP signal to the process, can be sent through the KILL command Kill-s SIGHUP 64881, can also be implemented through the library function Posix_kill (Posix_getpid (), SIGUSR1);

<?phppcntl_signal (SIGTERM,  function ($signo) {    echo] \ n This signal is called. [$signo] \ n ";    Status:: $state =-1;}); Pcntl_signal (SIGHUP,  function ($signo) {    echo] \ n This signal is called. [$signo] \ n ";    Status:: $state = 1; Status:: $ini = Parse_ini_file (' Test.ini ');}); Class status{public    static $state = 0;public static $ini = null;} $pid = Pcntl_fork (); if ($pid = =-1) {die    (' could not fork ');} if ($pid) {    //parent} else {$loop = true; Status:: $ini = Parse_ini_file (' Test.ini ');    while ($loop) {Print_r (Status:: $ini);        while (true) {//dispatching ... pcntl_signal_dispatch (); if (Status:: $state = =-1) {//do something and end loop. $loop = Fals E;break;} if (Status:: $state = = 1) {printf ("This is reload.\r\n"); Status:: $state = 0;break;}            echo '. ';            Sleep (1);        }        echo "\ n";    }        echo "Finish \ n";    Exit ();}

Creating a configuration file

[email protected] pcntl]# cat Test.ini [db]host=192.168.0.1port=3306

Test method, run the daemon first

# php signal.reload.php Array (    [Host] = 192.168.0.1    [port] = 3306)

Now modify the configuration file to add user=test configuration items

[email protected] pcntl]# cat Test.ini [db]host=192.168.0.1port=3306user=test

Send a signal, in another terminal window, through the PS command to find the PID of the process, and then use the KILL command to send the SIGHUP signal, and then through the PS viewing process, you will find that the process PID has not changed

[[Email protected] pcntl]# PS Ax | grep reload64881 pts/0    S      0:00 php-c/srv/php/etc/php-cli.ini signal.reload.php65073 pts/1    s+     0:00 grep--color=auto reload[[email protected] pcntl]# kill-s SIGHUP 64881[[email protected] pcntl]# PS Ax | grep reload64881 pts/0    S      0:00 php-c/srv/php/etc/php-cli.ini signal.reload.php65093 pts/1    s+     0:00 grep--color=auto Reload

Configuration file is re-loaded

This signal is called. [1] This program is reload. Array (    [Host] = 192.168.0.1    [port] = 3306    [user] = test)

Graceful reboot complete.

6. The process exits the solution unexpectedly

If it is a very important process, it is necessary to ensure that the program is working properly, and if any exception exits, you need to do the immediate processing. The following program may check if the process exits unexpectedly and starts immediately if it exits.

#!/bin/shlogfile=/var/log/$ (basename. sh). logpattern= "my.php" recovery= "/path/to/my.php start" while Truedo        timepoint=$ (date-d "Today" + "%y-%m-%d_%h:%m:%s")        proc=$ (Pgrep-o-F ${pattern})        #echo ${proc}        if [-Z] ${ PROC} "]; Then${recovery} >> $LOGFILE                echo "[${timepoint}] ${pattern} ${recovery}" >> $LOGFILE                        #else                # echo "[${timepoint}] ${pattern} ${proc}" >> $LOGFILE        fisleep 5done &

PHP Advanced Programming daemon for graceful restart

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.