PHP進階編程執行個體:編寫守護進程,編程執行個體守護進程
1.什麼是守護進程
守護進程是脫離於終端並且在後台啟動並執行進程。守護進程脫離於終端是為了避免進程在執行過程中的資訊在任何終端上顯示並且進程也不會被任何終端所產生的終端資訊所打斷。
例如 apache, nginx, mysql 都是守護進程
2.為什麼開發守護進程
很多程式以服務形式存在,他沒有終端或UI互動,它可能採用其他方式與其他程式互動,如TCP/UDP Socket, UNIX Socket, fifo。程式一旦啟動便進入後台,直到滿足條件他便開始處理任務。
3.何時採用守護進程開發應用程式
以我當前的需求為例,我需要運行一個程式,然後監聽某連接埠,持續接受服務端發起的資料,然後對資料分析處理,再將結果寫入到資料庫中; 我採用ZeroMQ實現資料收發。
如果我不採用守護進程方式開發該程式,程式一旦運行就會佔用當前終端窗框,還有受到當前終端鍵盤輸入影響,有可能程式誤退出。
4.守護進程的安全問題
我們希望程式在非超級使用者運行,這樣一旦由於程式出現漏洞被駭客控制,攻擊者只能繼承運行許可權,而無法獲得超級使用者權限。
我們希望程式只能運行一個執行個體,不運行同事開啟兩個以上的程式,因為會出現連接埠衝突等等問題。
5.怎樣開發守護進程
例 1. 守護進程例示
<?phpclass 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,%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 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 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);
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); 最後將該檔案刪除。
private function stop(){ if (file_exists($this->pidfile)) { $pid = file_get_contents($this->pidfile); posix_kill($pid, 9); unlink($this->pidfile); } }
php進階編程會不會涉及到java
很進階的會涉及。因為Java能做的事太多了。
PHP守護進程的製作思路及應用 急需 團隊需要 有可可以長期合作 不過沒錢拿 只有分
建議使用ignore_user_abort 具體思路 私聊 長期合作也可以 不過時間有點緊
http://www.bkjia.com/PHPjc/873340.htmlwww.bkjia.comtruehttp://www.bkjia.com/PHPjc/873340.htmlTechArticlePHP進階編程執行個體:編寫守護進程,編程執行個體守護進程 1.什麼是守護進程 守護進程是脫離於終端並且在後台啟動並執行進程。守護進程脫離於終端...