PHP高級編程實(shí)例:編寫守護(hù)進(jìn)程
1.什么是守護(hù)進(jìn)程
守護(hù)進(jìn)程是脫離于終端并且在后臺運(yùn)行的進(jìn)程。守護(hù)進(jìn)程脫離于終端是為了避免進(jìn)程在執(zhí)行過程中的信息在任何終端上顯示并且進(jìn)程也不會被任何終端所產(chǎn)生的終端信息所打斷。
例如 apache, nginx, mysql 都是守護(hù)進(jìn)程
2.為什么開發(fā)守護(hù)進(jìn)程
很多程序以服務(wù)形式存在,他沒有終端或UI交互,它可能采用其他方式與其他程序交互,如TCP/UDP Socket, UNIX Socket, fifo。程序一旦啟動便進(jìn)入后臺,直到滿足條件他便開始處理任務(wù)。
3.何時(shí)采用守護(hù)進(jìn)程開發(fā)應(yīng)用程序
以我當(dāng)前的需求為例,我需要運(yùn)行一個(gè)程序,然后監(jiān)聽某端口,持續(xù)接受服務(wù)端發(fā)起的數(shù)據(jù),然后對數(shù)據(jù)分析處理,再將結(jié)果寫入到數(shù)據(jù)庫中; 我采用ZeroMQ實(shí)現(xiàn)數(shù)據(jù)收發(fā)。
如果我不采用守護(hù)進(jìn)程方式開發(fā)該程序,程序一旦運(yùn)行就會占用當(dāng)前終端窗框,還有受到當(dāng)前終端鍵盤輸入影響,有可能程序誤退出。
4.守護(hù)進(jìn)程的安全問題
我們希望程序在非超級用戶運(yùn)行,這樣一旦由于程序出現(xiàn)漏洞被駭客控制,攻擊者只能繼承運(yùn)行權(quán)限,而無法獲得超級用戶權(quán)限。
我們希望程序只能運(yùn)行一個(gè)實(shí)例,不運(yùn)行同事開啟兩個(gè)以上的程序,因?yàn)闀霈F(xiàn)端口沖突等等問題。
5.怎樣開發(fā)守護(hù)進(jìn)程
例 1. 守護(hù)進(jìn)程例示
<?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'; // 數(shù)據(jù)庫服務(wù)器
$dbport = 3306;
$dbuser = 'www'; // 數(shù)據(jù)庫用戶名
$dbpass = 'qwer123'; // 數(shù)據(jù)庫密碼
$dbname = 'example'; // 數(shù)據(jù)庫名
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. 程序啟動
下面是程序啟動后進(jìn)入后臺的代碼
通過進(jìn)程ID文件來判斷,當(dāng)前進(jìn)程狀態(tài),如果進(jìn)程ID文件存在表示程序在運(yùn)行中,通過代碼file_exists($this->pidfile)實(shí)現(xiàn),但而后進(jìn)程被kill需要手工刪除該文件才能運(yùn)行
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());
}
}
程序啟動后,父進(jìn)程會推出,子進(jìn)程會在后臺運(yùn)行,子進(jìn)程權(quán)限從root切換到指定用戶,同時(shí)將pid寫入進(jìn)程ID文件。
5.2. 程序停止
程序停止,只需讀取pid文件,然后調(diào)用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擴(kuò)展程序?qū)崿F(xiàn)守護(hù)進(jìn)程
- PHP將進(jìn)程作為守護(hù)進(jìn)程的方法
- PHP守護(hù)進(jìn)程實(shí)例
- shell腳本作為保證PHP腳本不掛掉的守護(hù)進(jìn)程實(shí)例分享
- PHP實(shí)現(xiàn)多進(jìn)程并行操作的詳解(可做守護(hù)進(jìn)程)
- PHP程序級守護(hù)進(jìn)程的實(shí)現(xiàn)與優(yōu)化的使用概述
- php守護(hù)進(jìn)程 加linux命令nohup實(shí)現(xiàn)任務(wù)每秒執(zhí)行一次
- 詳解PHP解決守護(hù)進(jìn)程Redis假死
相關(guān)文章
PHP實(shí)現(xiàn)微信掃碼登錄功能的兩種方式總結(jié)
這篇文章主要為大家介紹了利用PHP實(shí)現(xiàn)微信掃碼登錄功能的兩種方式,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)有一定借鑒價(jià)值,需要的可以參考一下2022-08-08
PHP中批量生成靜態(tài)html(命令行下運(yùn)行PHP)
這篇文章主要介紹了如何通過命令行下運(yùn)行PHP命令,減少web請求,讓網(wǎng)站運(yùn)行的更穩(wěn)定,生成速度也更快2014-04-04
PHP實(shí)現(xiàn)對xml的增刪改查操作案例分析
這篇文章主要介紹了PHP實(shí)現(xiàn)對xml的增刪改查操作,結(jié)合具體案例形式分析了php針對xml格式文件的增刪改查操作相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-05-05
PHP判斷文件是否被引入的方法get_included_files用法示例
這篇文章主要介紹了PHP判斷文件是否被引入的方法get_included_files用法,結(jié)合實(shí)例形式分析了get_included_files函數(shù)獲取引入文件及遍歷輸出的操作技巧,需要的朋友可以參考下2016-11-11
利用ThinkPHP內(nèi)置的ThinkAjax實(shí)現(xiàn)異步傳輸技術(shù)的實(shí)現(xiàn)方法
ThinkPHP的官方文檔沒有給出ThinkAjax的使用方法,令很多初學(xué)者使用起來有些不便,今天學(xué)到這里,也碰到了很多問題,花時(shí)間深究下,做個(gè)學(xué)習(xí)筆記,希望能對初學(xué)者有幫助2011-12-12

