在我之前的文章中,介绍过Gearman的使用。在我的项目中,我使用了PHP来编写一直运行的Worker。如果按照Gearman官方推荐的例子,只是简单的一个循环来等待任务,会有一些问题,包括:1、当代码进行过修改之后,如何让代码的修改生效;2、重启Worker的时候,如何保证当前的任务处理完成才重启。
针对这个问题,我考虑了以下的解决方法:
1、每次修改完代码后,Worker需要手工重启(先杀死然后启动)。这个只能解决重新加载配置文件的问题。
2、在Worker中设置,单次任务循环完成后,就对Worker进行重启。这个方案的问题在于消耗比较大。
3、在Worker中添加一个退出函数,如果需要Worker退出的时候,在Client端发送一个优先级比较高的退出调用。这个需要客户端配合,在使用后台类任务时,不太适合。
4、在Worker中检查文件是否发生变化,如果发生了变化,退出并重启自身。
5、为Worker编写信号控制,接受重启指令,类似于 http restart graceful 指令。
最后,结合4和5两种方法,可以实现这样一个Daemon,如果配置文件发生了变化,他就会自动重启;如果接受到了用户的 kill -1 pid 信号,也会重新启动。
代码如下:
<?php declare( ticks = 1 ); // This case will check the config file regularly, if the config file changed, it will restart it self // If you want to restart the daemon gracefully, give it a HUP signal // by shiqiang<cocowool@gmail.com> at 2011-12-04 $init_md5 = md5_file( 'config.php'); // register signal handler pcntl_signal( SIGALRM, "signal_handler", true ); pcntl_signal( SIGHUP, 'signal_handler', TRUE ); $job_flag = FALSE; //Job status flag, to justify if the job has been finished $signal_flag = FALSE; //Signal status flag, to justify whether we received the kill -1 signal while( 1 ){ $job_flag = FALSE; //Job status flag print "Worker start running ... "; sleep(5); print "Worker's task done ... "; $flag = TRUE; //Job status flag AutoStart( $signal_flag ); } function signal_handler( $signal ) { global $job_flag; global $signal_flag; switch( $signal ){ case SIGQUIT: print date('y-m-d H:i:s', time() ) . " Caught Signal : SIGQUIT - No : $signal "; exit(0); break; case SIGSTOP: print date('y-m-d H:i:s', time() ) . " Caught Signal : SIGSTOP - No : $signal "; break; case SIGHUP: print date('y-m-d H:i:s', time() ) . " Caught Signal : SIGHUP - No : $signal "; if( $flag === TRUE ){ AutoStart( TRUE ); }else{ $signal_flag = TRUE; } break; case SIGALRM: print date('y-m-d H:i:s', time() ) . " Caught Signal : SIGALRM - No : $signal "; //pcntl_exec( '/bin/ls' ); pcntl_alarm( 5 ); break; default: break; } } function AutoStart( $signal = FALSE, $filename = 'config.php' ){ global $init_md5; if( $signal || md5_file( $filename ) != $init_md5 ){ print "The config file has been changed, we are going to restart. "; $pid = pcntl_fork(); if( $pid == -1 ){ print "Fork error "; }else if( $pid > 0 ){ print "Parent exit "; exit(0); }else{ $init_md5 = md5_file( $filename ); print "Child continue to run "; } } }
参考资料:
再参考一下下面的片段:
<?php function handle_http_request($address, $port){ $max_backlog = 16; $res_content = "HTTP/1.1 200 OK ". "Content-Length: 15 ". "Content-Type: text/plain; charset=UTF-8 ". "PHP HTTP Server Hello World!!"; $res_len = strlen($res_content); //Create, bind and listen to socket if(($socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === FALSE){ echo "Create socket failed! "; exit; } if((socket_bind($socket, $address, $port)) === FALSE) { echo "Bind socket failed! "; exit; } if((socket_listen($socket, $max_backlog)) === FALSE) { echo "Listen to socket failed! "; exit; } //loop while (true) { if( ($accept_socket = socket_accept($socket)) === FALSE ){ continue; }else{ socket_write($accept_socket, $res_content, $res_len); socket_close($accept_socket); } } } //Run as daemon process. function run(){ if(($pid1 = pcntl_fork()) === 0){ posix_setsid();//Set first child process as the session leader. if(($pid2 = pcntl_fork()) === 0){ handle_http_request('192.168.255.131', 10101); }else{ exit; } }else{ pcntl_wait($status); } } run(); ?>