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 &