PHP advanced programming daemon for elegant restart

Source: Internet
Author: User
PHP advanced programming daemon to enable elegant restart of PHP advanced programming daemon

Http://netkiller.github.io/journal/php.daemon.html

Mr. Neo Chen (Chen Jingfeng), netkiller, BG7NYT


Xishan Meidi, Minzhi Street, Longhua New District, Shenzhen city, Guangdong province, China
518131
+ 86 13113668890
+ 86 755 29812080

Copyright©2014 http://netkiller.github.io

Copyright notice

For reprinting, contact the author. during reprinting, be sure to indicate the original source, author information, and this statement.

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

Scan the QR code to enter the Netkiller subscription number.

QQ group: 128659835 please indicate "readers"

2014-09

Summary

Published on February 11

Update

Updated and restarted elegantly

My Documents

Netkiller impact ECT shouzha Netkiller Developer notebook Netkiller PHP notebook Netkiller Python notebook Netkiller Testing shouzha
Netkiller Cryptography notebook Netkiller Linux shouzha Netkiller Debian shouzha Netkiller CentOS notebook Netkiller FreeBSD notebook
Netkiller Shell shouzha Netkiller Security statement Netkiller Web shouzha Netkiller Monitoring shouzha Netkiller Storage shouzha
Netkiller Mail Shouzhi Netkiller Docbook notebook Netkiller Project notebook Netkiller Database Shouzhi Netkiller PostgreSQL notebook
Netkiller MySQL notebook Netkiller NoSQL notebook Netkiller LDAP notebook Netkiller Network shouzha Netkiller Cisco IOS notebook
Netkiller H3C notebook Netkiller Multimedia Notebook Netkiller Perl notebook Netkiller Amateur Radio shouzha Netkiller DevOps notebook

You can use iBook to read the current document

Directory

  • 1. what is a daemon?
  • 2. why the development daemon process?
  • 3. when to use a daemon process to develop applications
  • 4. daemon security issues
  • 5. how to develop daemon
    • 5.1. program startup
    • 5.2. program stopped
    • 5.3. Singleton mode
    • 5.4. achieve elegant restart
  • 6. unexpected process exit solution

1. what is a daemon?

A Daemon is a process that runs in the background and is out of the terminal. The daemon is separated from the terminal to prevent the information in the execution process from being displayed on any terminal and the process is not interrupted by any terminal information generated by any terminal.

For example, apache, nginx, and mysql are all daemon processes.

2. why the development daemon process?

Many programs exist in the form of services. they have no terminal or UI interaction. they may interact with other programs in other ways, such as TCP/UDP Socket, UNIX Socket, and fifo. Once started, the program enters the background until it meets the conditions and starts to process the task.

3. when to use a daemon process to develop applications

Taking my current needs as an example, I need to run a program, listen to a port, continue to accept data initiated by the server, analyze and process the data, and then write the results to the database; I use ZeroMQ to send and receive data.

If I do not use a daemon to develop the program, the program will occupy the current terminal window once it runs, and may be affected by the keyboard input of the current terminal. The program may exit by mistake.

4. daemon security issues

We want the program to run on non-super users. Once the program is controlled by hackers due to a vulnerability, attackers can only inherit the operation permission, but cannot obtain the super user permission.

We hope that the program can run only one instance without running more than two programs, because there may be port conflicts and other problems.

5. how to develop daemon

Example 1. multi-thread daemon example

 Logger = $ logger; #}# protected $ logger; protected static $ dbh; public function _ construct () {} public function run () {$ dbhost = '2017. 168.2.1 '; // database server $ dbport = 3306; $ dbuser = 'WWW'; // database username $ dbpass = 'qwer123 '; // database password $ dbname = 'example '; // database name self: $ dbh = new PDO ("mysql: host = $ dbhost; port = $ dbport; dbname = $ 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 extends Stackable {public function _ construct ($ msg) {$ trades = explode (",", $ msg); $ this-> data = $ trades; print_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 ignore (ticket, login, volume, 'status') VALUES (: ticket,: login,: volume, 'n') "; $ Something = $ dbh-> prepare ($ insert ); $ Something-> bindValue (': ticket', $ this-> data [0]); $ Something-> bindValue (': login ', $ this-> data [1]); $ Something-> bindValue (': volume', $ this-> data [2]); $ Something-> execute (); $…… = null ;/*...... */$ update = "UPDATE into SET 'status' = 'y' WHERE ticket =: ticket and 'status' = 'n '"; $ Something = $ dbh-> prepare ($ update); $ Something-> bindValue (': ticket', $ this-> data [0]); $ Something-> execute (); // echo $ Something-> 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 uid = 80; const gid = 80; protected $ pool = NULL; protected $ zmq = NULL; public function _ construct () {$ this-> pidfile = '/var/run /'. self: pidfile. '. 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 are the childfile_put_contents ($ this-> pidfile, getmypid (); posix_setuid (self: uid); posix_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 processing and echoing back */while ($ message = $ this-> zmq-> recv ()) {// print_r ($ message); // if ($ trades) {$ this-> pool-> submit (new transfer ($ 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); posix_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 ("please input help 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 queue and Daemon

 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 a connection 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); // persistence $ this-> exchange-> declare (); // echo "Exchange Status :". $ this-> exchange-> declare (). "\ n"; // Create a queue $ this-> queue = new AMQPQueue ($ this-> channel); $ this-> queue-> setNa Me ($ queueName); $ this-> queue-> setFlags (AMQP_DURABLE); // persistence $ this-> queue-> declare (); // echo "Message Total: ". $ this-> queue-> declare (). "\ n"; // bind the vSwitch and queue, and specify the route key $ bind = $ this-> queue-> bind ($ exchangeName, $ routeKey ); // echo 'queue Bind :'. $ bind. "\ n"; // when receiving messages in blocking mode while (true) {// $ this-> queue-> consume ('processmessage', AMQP_AUTOACK ); // automatic ACK response $ this-> queue-> consume (function ($ envelope, $ queue) {$ msg = $ envelope-> getBo Dy (); $ queue-> ack ($ envelope-> getDeliveryTag (); // manually send an 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-> w Arning ("start"); $ pid = pcntl_fork (); if ($ pid =-1) {die ('could not fork');} else if ($ pid) {// pcntl_wait ($ status); // wait until the sub-process is interrupted to prevent the sub-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); posix_kill ($ pid, SIGTERM); // posix_kill ($ pid, SIGKILL); unlink ($ this-> pidfile); $ this-> logging-> warning ("stop");} else {printf ("% S haven 'trunning \ 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 ); printf ("% 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

The following code enters the background after the program is started:

The current process status is determined by the process ID File. if the process ID file exists, it indicates that the program is running and implemented through the code file_exists ($ this-> pidfile, however, if the process is killed, you need to manually delete the file before it can 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 are the childfile_put_contents($this->pidfile, getmypid());posix_setuid(self::uid);posix_setgid(self::gid);return(getmypid());}}

After the program starts, the parent process will be released, and the child process will run in the background. the sub-process permission will be changed from root to the specified user, and the pid will be written to the process ID file.

5.2. program stopped

When the program stops, you only need to read the pid file and then call posix_kill ($ pid, 9) to delete the file.

private function stop(){if (file_exists($this->pidfile)) {$pid = file_get_contents($this->pidfile);posix_kill($pid, 9); unlink($this->pidfile);}}

5.3. Singleton mode

All threads share database connections, which is very important in multithreading. if each thread is established and the database connection is closed, the database overhead is huge.

protected function getInstance(){return self::$dbh;}

5.4. achieve elegant restart

The so-called "elegant restart" means that the process does not exit and is re-loaded to include resetting variables, refreshing the configuration file, resetting logs, and so on.

Both stop, start, and restart will exit the process and restart the process. as a result, the process ID changes and the business is terminated instantly. Therefore, many daemon provide a reload function, which is called an elegant restart.

The reload implementation principle is to send a SIGHUP signal to the process. you can use the kill command to send the kill-s SIGHUP 64881, or use the library function to implement posix_kill (posix_getpid (), SIGUSR1 );

     

Create a configuration file

[root@netkiller pcntl]# cat test.ini [db]host=192.168.0.1port=3306

Test Method: first run the daemon

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

Modify the configuration file and add the user = test configuration item.

[root@netkiller pcntl]# cat test.ini [db]host=192.168.0.1port=3306user=test

Send a signal. in another terminal window, run the ps command to find the PID of the process, run the kill command to send the SIGHUP signal, and then run the ps command to view the process. you will find that the PID of the process has not changed.

[root@netkiller 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[root@netkiller pcntl]# kill -s SIGHUP 64881[root@netkiller 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

The configuration file is reloaded.

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

Elegant restart is complete.

6. unexpected process exit solution

If it is a very important process, you must ensure that the program runs normally. once any exceptions exit, you must perform immediate processing. The following program may check whether the process exits abnormally. if it exits, it will be started immediately.

#!/bin/shLOGFILE=/var/log/$(basename $0 .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 &

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.