守护进程

php 守护进程

2017-12-15  本文已影响86人  aoshi
php扩展需求
php-pcntl
php-posix
守护进程实现代码
Class Daemon{

    /**
     * 初始化一个守护进程
     * @throws Exception
     */
    public function init(){
        //创建一个子进程
        $pid = pcntl_fork();

        if ($pid == -1) {
            throw new Exception('fork子进程失败');
        } elseif ($pid > 0) {
            //父进程退出,子进程变成孤儿进程被1号进程收养,进程脱离终端
            exit(0);
        }

        //创建一个新的会话,脱离终端控制,更改子进程为组长进程
        $sid = posix_setsid();
        if ($sid == -1) {
            throw new Exception('setsid fail');
        }

        //修改当前进程的工作目录,由于子进程会继承父进程的工作目录,修改工作目录以释放对父进程工作目录的占用。
        chdir('/');

        /**
         * 通过上一步,我们创建了一个新的会话组长,进程组长,且脱离了终端,但是会话组长可以申请重新打开一个终端,为了避免
         * 这种情况,我们再次创建一个子进程,并退出当前进程,这样运行的进程就不再是会话组长。
         */
        $pid = pcntl_fork();
        if ($pid == -1) {
            throw new Exception('fork子进程失败');
        } elseif ($pid > 0) {
            //再一次退出父进程,子进程成为最终的守护进程
            exit(0);
        }
        //由于守护进程用不到标准输入输出,关闭标准输入,输出,错误输出描述符
        fclose(STDIN);
        fclose(STDOUT);
        fclose(STDERR);
    }
}

实例化一个守护进程
$daemon = new Daemon();
$daemon->init();
针对需求进行分析:

需求:有一个常驻队列messageQueue(假设在redis内存中),这个队列会有可能有请求不定期的往队列中增加元素。同时我们要求在队列中有元素的时候,按照队列顺序将元素pop出来,并进行处理(假设这个处理只是echo ‘test’);

解决方法:

现在假设已经有了两个函数

function oPopMessageQueue(){ …} //获取队列最后一个元素;

function vDealElement($element) { …} 处理元素;

要求写出一个守护程序,完成上面的需求。

好了,这个程序很容易想到,可以使用while循环来做

程序:
while(true)
{
    if( $element  = oPopMessageQueue())
    {
        vDealElement($element);
    }   
}
考虑1 : 这个程序如果一直跑的话已经可以满足上面的需求了.

但是考虑到:1 用php进程跑有可能会由于各种情况(比如运行时间过长),Cpu使用率达到100%,进程挂了,这样程序就无法自动重连了.

方法:使用cron

我们在定时脚本中每10分钟起一个进程跑这个程序。

然后设置这个程序的运行时间为10分钟,10分钟后自动取消,于是代码变成

$timeStart = time();        //设置开始值
while(true) {
    //有设置开始值
    if(empty($timeStart)) {
        $timeStart = time();
    }

    $diffTime = time() - $timeStart;      //计算进程时间差
    if($diffTime > 600) {     //时间超过5秒
        exit;       //终止运行
    }
}
考虑2,可能会有这种需求: 需要有随时让脚本暂停的功能:
于是考虑使用文件或者缓存来增加暂停功能

方法1:  让程序终止执行,有需要的时候再次启动进程
while(true) {
    if(file_exists("/home/daemon/stop"))    //判断终止文件是否存在
    {
        exit;
    }
}

方法2:让程序睡眠指定秒数
while(true) {
    if(file_exists("/home/daemon/sleep"))    //判断终止文件是否存在
    {
        $second = file_get_contents("/home/daemon/sleep");
        $second = (int)$second;
        if($second) {        //让程序睡眠指定秒数
              sleep($second);
        }
    }
}
考虑3, 是否可以改成多线程的程序,让运行的效率更高?
这个只要把cron的10分钟起一个进程的限制改成每1分钟起一个进程就好了
这样能保证有10个线程在运行程序
但是有一个基本要求是:oPopMessageQueue()是一个原子操作(整个逻辑处理成功才算成功,有一个失败就需要回滚<事务>)

出处:http://www.cnblogs.com/yjf512/

本文版权归yjf512和cnBlog共有,欢迎转载,但未经作者同意必须保留此段声明

上一篇 下一篇

猜你喜欢

热点阅读