PHP高级编程实例:编写守护进程
《PHP高级编程实例:编写守护进程》要点: PHP应用1.什么是守护过程 守护进程是脱离于终端而且在后台运行的进程.守护进程脱离于终端是为了避免进程在执行过程中的信息在任何终端上显示而且进程也不会被任何终端所产生的终端信息所打断. 例如 apache,nginx,mysql 都是守护过程 2.为什么开发守护过程 很多程序以服务形式存在,他没有终端或UI交互,它可能采纳其他方式与其他程序交互,如TCP/UDP Socket,UNIX Socket,fifo.程序一旦启动便进入后台,直到满足条件他便开始处理任务. 3.何时采纳守护进程开发应用程序 以我当前的需求为例,我必要运行一个程序,然后监听某端口,持续接受服务端发起的数据,然后对数据分析处理,再将结果写入到数据库中; 我采用ZeroMQ实现数据收发. 如果我不采纳守护进程方式开发该程序,程序一旦运行就会占用当前终端窗框,还有受到当前终端键盘输入影响,有可能程序误退出. 4.守护进程的平安问题 我们希望程序在非超等用户运行,这样一旦由于程序出现漏洞被骇客控制,攻击者只能继承运行权限,而无法获得超等用户权限. 我们希望程序只能运行一个实例,不运行同事开启两个以上的程序,因为会呈现端口冲突等等问题. 5.怎样开发守护过程 例 1. 守护过程例示 <?php class ExampleWorker extends Worker { #public function __construct(Logging $logger) { # $this->logger = $logger; #} #protected $logger; protected static $dbh; public function __construct() { } public function run(){ $dbhost = '192.168.2.1'; // 数据库服务器 $dbport = 3306; $dbuser = 'www'; // 数据库用户名 $dbpass = 'qwer123'; // 数据库暗码 $dbname = 'example'; // 数据库名 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 Fee 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 fee(ticket,login,volume,`status`) VALUES(: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,%sn",$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 children exit($pid); } else { // we are the child file_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 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); 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 parametern"); 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); 5.1. 程序启动 下面是法式启动后进入后台的代码 通过进程ID文件来判断,当前进程状态,如果进程ID文件存在表示程序在运行中,通过代码file_exists($this->pidfile)实现,但而后进程被kill必要手工删除该文件才能运行 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 children exit($pid); } else { // we are the child file_put_contents($this->pidfile,getmypid()); posix_setuid(self::uid); posix_setgid(self::gid); return(getmypid()); } } 法式启动后,父进程会推出,子进程会在后台运行,子进程权限从root切换到指定用户,同时将pid写入进程ID文件. 5.2. 法式停止 法式停止,只需读取pid文件,然后调用posix_kill($pid,9); 最后将该文件删除. PHP利用
private function stop(){
if (file_exists($this->pidfile)) {
$pid = file_get_contents($this->pidfile);
posix_kill($pid,9);
unlink($this->pidfile);
}
}
《PHP高级编程实例:编写守护进程》是否对您有启发,欢迎查看更多与《PHP高级编程实例:编写守护进程》相关教程,学精学透。编程之家 52php.cn为您提供精彩教程。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |