• CentOS 下管理自定义 PHP 计划任务代码的守护进程脚本


    有需求想实现比 crontab 更频繁一点的计划任务,折腾一通之后,写下这个脚本,目前在 CentOS 6 下测试没有问题:

    Shell 代码:

    #!/bin/bash
    #
    # chkconfig: 35 90 12
    # description: Queue Daemon
    #
     
    # Get function from functions library
    . /etc/init.d/functions
     
    # PHP 访问路径,如果 PHP 是编译安装则需要修改
    php_path="/usr/bin/php"
     
    # PHP 执行用户名
    # Nginx 环境下常见为 nginx 或 www,
    # Apapche 环境下常见为 php 或 apache
    # 用在某些时候需要在计划任务中生成文件时文件权限不正确的问题
    php_user=nginx
     
    # 要管理的 PHP 计划任务脚本
    # 该目录下必须都是 PHP 可执行代码文件
    queue_path="/www/micate.dev/crontab/"
     
    # PHP 计划任务执行日志保存路径
    # 默认以 脚本名.log 的方式存储
    log_path="/var/log/queue/"
     
    # 守护进程 PID 路径
    pid_path="/var/run/queued.pid"
     
    # 守护进程在启动退出时的提示名称
    prog="Queued"
     
    # Start the service
    start() {
    	echo -n $"Starting $prog: "
     
    	if [ -f $pid_path ]; then
    		echo -n "pid "$pid_path" exists."
    		failure
    		echo
    		return 0
    	fi
     
    	if [ ! -d $log_path ]; then
    		mkdir -p $log_path
    	fi
     
    	queues=$(ls $queue_path)
    	for queue in $queues
    	do
    		# TODO sudo -u $php_user 
    		nohup $php_path $queue_path$queue 1>>$log_path$queue.log 2>/dev/null &
    		retval=$?
    		if [ ! $retval -eq 0 ]; then
    			echo -n $queue" init failed."
    			failure
    			echo
    			return 0
    		fi
    		echo $! >> $pid_path
    	done
     
    	success
    	echo
    	return $retval
    }
     
    # Stop the service
    stop() {
    	echo -n $"Stopping $prog: "
     
    	if [ ! -f $pid_path ]; then
                    echo -n "pid "$pid_path" not exists."
                    failure
                    echo
                    return 0
            fi
     
    	queues=$(cat $pid_path)
    	for queue in $queues
    	do
    		kill -9 $queue 1>/dev/null 2>&1
    		retval=$?
                    if [ ! $retval -eq 0 ]; then
                            echo -n $queue" stop failed."
                            failure
                            echo
                            return 0
                    fi
    	done
     
    	rm -f $pid_path
     
    	success
    	echo
    	return $retval
    }
     
    ### main logic ###
    case "$1" in
      start)
    	start
    	;;
      stop)
    	stop
    	;;
      restart)
    	stop
    	start
    	;;
      *)
    	echo $"Usage: $0 {start|stop|restart}"
    	exit 1
    esac
     
    exit 0

    使用方法:

    根据实际情况,按照上面的配置提示,修改相应配置,并保存为 /etc/init.d/queued(建议以这样的方式命名,注意路径必须是 /etc/init.d/)

    在 Shell 里面执行:

    # 为守护进程脚本增加执行权限
    chmod +x /etc/init.d/queued
    # 设置开机启动
    chkconfig queued on

    守护进程设置完成。

    在上面配置的目录中添加测试 PHP 计划任务脚本,如 hello.php:

    <?php
    // 注意,推荐使用死循环的方式
    // 这个守护进程脚本也是以这个思路来写的
    while (true)
    {
        // 做点事情
     
        // 注意,这里的输出会记录到日志文件里面,可以输出些有用的信息来帮助排除问题
        echo "hello from queued.\n";
     
        // 延迟时间,建议用秒级别的,usleep ... 你确定么?
        sleep(20);
    }

    好了,来运行看看:

    /etc/init.d/queued start

    查看下日志目录:

    less /var/log/queue/hello.php.log

    收工。

    其他的,比如:

    # 停止所有计划任务
    /etc/init.d/queued stop
     
    # 重新启动守护进程脚本
    /etc/init.d/queued restart

    注意事项

    由于是基于死循环的方式实现,如果代码里面有引入基础文件后的死循环操作,那依赖基础文件中的变量的代码部分,要注意调整,举例来说:

    <?php
    define('START_TIME', microtime(true));
     
    // 假如这个文件里面定义了 TIME 常量,
    // 以备后面的代码获取当前时间用
    //(避免一次请求中多次调用 time() 函数带来的性能损失)
    require '../init.php';
     
    $interval = value(config('mail'), 'interval', 20);
    $interval_size = value(config('mail'), 'interval_size', 50);
    while (true)
    {
        $queue = & factory::queue('mail');
     
        // 这里的定时循环,用到了 TIME 常量
        // 比如记录执行时间,甚至用 TIME 常量做查询条件
        echo $queue->interval($interval_size);
        sleep($interval);
    }

    2. 看了上面的注释,问题就比较明显了。当以普通用户请求过去时,由于是正常流程,每次请求发起后,TIME 都会重新赋予正确的当前时间;但当使用死循环后,以后的每次 interval 用到的时间都会是 该计划任务脚本启动时间。这就是问题,需要注意和调整。

    3. 修改和添加计划任务后,需要重新启动守护进程,方法上面已经介绍了,restart 就可以。

    4. 担心日志过多?设置下日志自动滚动切割吧:

    # 在 /etc/logrotate.d/ 目录下建立 queued 文件
    vim /etc/logrotate.d/queued
     
    # 输入如下的信息,路径之类的要和你自己设置的保持一致
    /var/log/queue/*log {
        daily
        rotate 10
        missingok
        notifempty
        sharedscripts
        compress
        postrotate
            /etc/init.d/queued restart > /dev/null 2>/dev/null || true
        endscript
    }

    保存文件,完成。这样,每天日志会自动滚动切割,最多保留 10 个日志文件(未验证这个步骤... 如有问题请反馈)。

    4. 进程崩溃、服务器断电...导致守护进程起不来或停不了了?

    直接 ps -ef | grep *** 关键词,看是否有在运行的 PHP 计划任务代码,kill 掉,然后删除 pid 文件,重新启动即可。 

    更多...

    其实 crontab 也有对应的解决方法:

    # 打开 crontab 编辑界面
    crontab -e
     
    # 输入类似的设置
    * * * * * /bin/date
    * * * * * sleep 20; /bin/date
    * * * * * sleep 40; /bin/date

    思想就是... 同时添加多个任务,然后每个之前以适当的延时隔离开,就是说每分钟会有 N 个程序被触发,但会逐个延迟执行,也是个不错的解决思路。

  • 相关阅读:
    推荐系统相关算法
    特征的生命周期
    数学知识索引
    蓄水池(Reservoir_sampling)抽样算法简记
    数赛刷题代码学习及课程学习链接
    逻辑回归(LR)总结复习
    我的面试问题记录
    开发中遇到的一些问题
    K-Means聚类和EM算法复习总结
    常见概率分布图表总结
  • 原文地址:https://www.cnblogs.com/ninelands/p/2746645.html
Copyright © 2020-2023  润新知