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