A-A+

关于PHP的守护进程与实例

2018年01月23日 PHP技术文章 暂无评论 阅读 0 views 次

在php中,如果需要在某一段时间之内执行某一段逻辑代码的话,我们直接写定时脚本就可以了,但如果需要时时刻刻同步数据的话,这个时候再使用php定时脚本就显得有些吃力了,需要我们设置定时的脚本极期的短,短到秒扫表去执行,其实像这种实时同步数据的问题,我们还可以使用php的守护进程,也是我最近刚刚接触到的,说得直白一些,php守护进程就是一个死循环,比如我们要扫一个表,只要不触发停止命令,它是从不停歇的一直在执行某个逻辑段。

也就是我们可以在linux端后台一直执行某个程序逻辑段,守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程是一种很有用的进程。php也可以实现守护进程的功能,具体的可以上搜索引擎查找一下php守护进程。

下面来看一段php守护进程实例:

  1. <?php   
  2. * 后台脚本控制类   
  3. */   
  4. class DaemonCommand{   
  5.      
  6.   private $info_dir="/tmp";   
  7.   private $pid_file="";   
  8.   private $terminate=false; //是否中断   
  9.   private $workers_count=0;   
  10.   private $gc_enabled=null;   
  11.   private $workers_max=8; //最多运行8个进程   
  12.      
  13.   public function __construct($is_sington=false,$user='nobody',$output="/dev/null"){   
  14.      
  15.       $this->is_sington=$is_sington//是否单例运行,单例运行会在tmp目录下建立一个唯一的PID   
  16.       $this->user=$user;//设置运行的用户 默认情况下nobody   
  17.       $this->output=$output//设置输出的地方   
  18.       $this->checkPcntl();   
  19.   }   
  20.   //检查环境是否支持pcntl支持   
  21.   public function checkPcntl(){   
  22.     if ( ! function_exists('pcntl_signal_dispatch')) {   
  23.       // PHP < 5.3 uses ticks to handle signals instead of pcntl_signal_dispatch   
  24.       // call sighandler only every 10 ticks   
  25.       declare(ticks = 10);   
  26.     }   
  27.      
  28.     // Make sure PHP has support for pcntl   
  29.     if ( ! function_exists('pcntl_signal')) {   
  30.       $message = 'PHP does not appear to be compiled with the PCNTL extension. This is neccesary for daemonization';   
  31.       $this->_log($message);   
  32.       throw new Exception($message);   
  33.     }   
  34.     //信号处理   
  35.     pcntl_signal(SIGTERM, array(__CLASS__"signalHandler"),false);   
  36.     pcntl_signal(SIGINT, array(__CLASS__"signalHandler"),false);   
  37.     pcntl_signal(SIGQUIT, array(__CLASS__"signalHandler"),false);   
  38.      
  39.     // Enable PHP 5.3 garbage collection   
  40.     if (function_exists('gc_enable'))   
  41.     {   
  42.       gc_enable();   
  43.       $this->gc_enabled = gc_enabled();   
  44.     }   
  45.   }   
  46.      
  47.   // daemon化程序   
  48.   public function daemonize(){   
  49.      
  50.     global $stdin$stdout$stderr;   
  51.     global $argv;   
  52.      
  53.     set_time_limit(0);   
  54.      
  55.     // 只允许在cli下面运行   
  56.     if (php_sapi_name() != "cli"){   
  57.       die("only run in command line mode\n");   
  58.     }   
  59.      
  60.     // 只能单例运行   
  61.     if ($this->is_sington==true){   
  62.      
  63.       $this->pid_file = $this->info_dir . "/" .__CLASS__ . "_" . substr(basename($argv[0]), 0, -4) . ".pid";   
  64.       $this->checkPidfile();   
  65.     }   
  66.      
  67.     umask(0); //把文件掩码清0   
  68.      
  69.     if (pcntl_fork() != 0){ //是父进程,父进程退出   
  70.       exit();   
  71.     }   
  72.   
  73.     posix_setsid();//设置新会话组长,脱离终端   
  74.     if (pcntl_fork() != 0){ //是第一子进程,结束第一子进程     
  75.       exit();   
  76.     }   
  77.      
  78.     chdir("/"); //改变工作目录   
  79.      
  80.     $this->setUser($this->user) or die("cannot change owner");   
  81.      
  82.     //关闭打开的文件描述符   
  83.     fclose(STDIN);   
  84.     fclose(STDOUT);   
  85.     fclose(STDERR);   
  86.      
  87.     $stdin = fopen($this->output, 'r');   
  88.     $stdout = fopen($this->output, 'a');   
  89.     $stderr = fopen($this->output, 'a');   
  90.      
  91.     if ($this->is_sington==true){   
  92.       $this->createPidfile();   
  93.     }   
  94.      
  95.   }   
  96.   //--检测pid是否已经存在   
  97.   public function checkPidfile(){   
  98.      
  99.     if (!file_exists($this->pid_file)){   
  100.       return true;   
  101.     }   
  102.     $pid = file_get_contents($this->pid_file);   
  103.     $pid = intval($pid);   
  104.     if ($pid > 0 && posix_kill($pid, 0)){   
  105.       $this->_log("the daemon process is already started");   
  106.     }   
  107.     else {   
  108.       $this->_log("the daemon proces end abnormally, please check pidfile " . $this->pid_file);   
  109.     }   
  110.     exit(1);   
  111.      
  112.   }   
  113.   //----创建pid   
  114.   public function createPidfile(){   
  115.      
  116.     if (!is_dir($this->info_dir)){   
  117.       mkdir($this->info_dir);   
  118.     }   
  119.     $fp = fopen($this->pid_file, 'w') or die("cannot create pid file");   
  120.     fwrite($fp, posix_getpid());   
  121.     fclose($fp);   
  122.     $this->_log("create pid file " . $this->pid_file);   
  123.   }   
  124.      
  125.   //设置运行的用户   
  126.   public function setUser($name){   
  127.      
  128.     $result = false;   
  129.     if (emptyempty($name)){   
  130.       return true;   
  131.     }   
  132.     $user = posix_getpwnam($name);   
  133.     if ($user) {   
  134.       $uid = $user['uid'];   
  135.       $gid = $user['gid'];   
  136.       $result = posix_setuid($uid);   
  137.       posix_setgid($gid);   
  138.     }   
  139.     return $result;   
  140.   }   
  141.   //信号处理函数   
  142.   public function signalHandler($signo){   
  143.      
  144.     switch($signo){   
  145.       //用户自定义信号   
  146.       case SIGUSR1: //busy   
  147.       if ($this->workers_count < $this->workers_max){   
  148.         $pid = pcntl_fork();   
  149.         if ($pid > 0){   
  150.           $this->workers_count ++;   
  151.         }   
  152.       }   
  153.       break;   
  154.       //子进程结束信号   
  155.       case SIGCHLD:   
  156.         while(($pid=pcntl_waitpid(-1, $status, WNOHANG)) > 0){   
  157.           $this->workers_count --;   
  158.         }   
  159.       break;   
  160.       //中断进程   
  161.       case SIGTERM:   
  162.       case SIGHUP:   
  163.       case SIGQUIT:   
  164.         $this->terminate = true;   
  165.       break;   
  166.       default:   
  167.       return false;   
  168.     }   
  169.      
  170.   }   
  171.   /**  
  172.   *开始开启进程  
  173.   *$count 准备开启的进程数  
  174.   */  
  175.   public function start($count=1){   
  176.      
  177.     $this->_log("daemon process is running now");   
  178.     pcntl_signal(SIGCHLD, array(__CLASS__"signalHandler"),false); // if worker die, minus children num   
  179.     while (true) {   
  180.       if (function_exists('pcntl_signal_dispatch')){  
  181.         pcntl_signal_dispatch();   
  182.       }   
  183.   
  184.       if ($this->terminate){   
  185.         break;   
  186.       }   
  187.       $pid=-1;   
  188.       if($this->workers_count<$count){   
  189.         $pid=pcntl_fork();   
  190.       }   
  191.      
  192.       if($pid>0){  
  193.         $this->workers_count++;   
  194.       }elseif($pid==0){   
  195.      
  196.         // 这个符号表示恢复系统对信号的默认处理   
  197.         pcntl_signal(SIGTERM, SIG_DFL);   
  198.         pcntl_signal(SIGCHLD, SIG_DFL);   
  199.         if(!emptyempty($this->jobs)){   
  200.           while($this->jobs['runtime']){   
  201.             if(emptyempty($this->jobs['argv'])){   
  202.               call_user_func($this->jobs['function'],$this->jobs['argv']);   
  203.             }else{   
  204.               call_user_func($this->jobs['function']);   
  205.             }   
  206.             $this->jobs['runtime']--;   
  207.             sleep(2);   
  208.           }   
  209.           exit();   
  210.         }   
  211.         return;   
  212.       }else{  
  213.         sleep(2);   
  214.       }   
  215.     }  
  216.     $this->mainQuit();   
  217.     exit(0);  
  218.   }   
  219.      
  220.   //整个进程退出   
  221.   public function mainQuit(){   
  222.      
  223.     if (file_exists($this->pid_file)){   
  224.       unlink($this->pid_file);   
  225.       $this->_log("delete pid file " . $this->pid_file);   
  226.     }   
  227.     $this->_log("daemon process exit now");   
  228.     posix_kill(0, SIGKILL);   
  229.     exit(0);   
  230.   }   
  231.   
  232.   // 添加工作实例,目前只支持单个job工作   
  233.   public function setJobs($jobs=array()){   
  234.      
  235.     if(!isset($jobs['argv'])||emptyempty($jobs['argv'])){  
  236.       $jobs['argv']="";  
  237.     }   
  238.     if(!isset($jobs['runtime'])||emptyempty($jobs['runtime'])){  
  239.       $jobs['runtime']=1;   
  240.     }   
  241.      
  242.     if(!isset($jobs['function'])||emptyempty($jobs['function'])){  
  243.       $this->log("你必须添加运行的函数!");   
  244.     }   
  245.     $this->jobs=$jobs;   
  246.   }   
  247.   //日志处理   
  248.   private function _log($message){   
  249.     printf("%s\t%d\t%d\t%s\n"date("c"), posix_getpid(), posix_getppid(), $message);   
  250.   }   
  251.      
  252. }   
  253.      
  254. //调用方法1   
  255. $daemon=new DaemonCommand(true);   
  256. $daemon->daemonize();   
  257. $daemon->start(2);//开启2个子进程工作   
  258. work();   
  259.   
  260. //调用方法2   
  261. $daemon=new DaemonCommand(true);   
  262. $daemon->daemonize();   
  263. $daemon->addJobs(array('function'=>'work','argv'=>'','runtime'=>1000));//function 要运行的函数,argv运行函数的参数,runtime运行的次数   
  264. $daemon->start(2);//开启2个子进程工作   
  265. //www.xiariboke.net  
  266. //具体功能的实现   
  267. function work(){   
  268.    echo "测试1";   
  269. }   
  270. ?>  

php守护进程在处理同步数据方面很方便,或者也可应用在有需要循环执行的程序上,目前对于php守护进程夏日博客也是一知半解,随着深入了解,会把笔记慢慢记上来。

标签:

给我留言