首页 > 开发 > PHP > 正文

如何使用纯PHP实现定时器任务(Timer)

2024-05-04 22:35:27
字体:
来源:转载
供稿:网友

定时器任务,在WEB应用比较常见,如何使用PHP实现定时器任务,大致有两种方案:1)使用Crontab命令,写一个shell脚本,在脚本中调用PHP文件,然后定期执行该脚本;2)配合使用ignore_user_abort()和set_time_limit(),使脚本脱离浏览器运行。前者是利用Linux的特性,和PHP本身没有多大关系,后者使用场景有限,且只能由一次HTTP请求触发该脚本,执行完后退出。那么我们如何使用纯PHP实现纯粹的定时器任务,且能适应认识任务业务需求?

基础知识

此程序在Linux下开发,以cli模式运行,一下是基本知识的简要介绍。

CLI:PHP的命令行模式,常见的WEB应用使用的是fpm;
进程:进程是程序运行的基本单元,进程之间是独立运行且互不干扰的,有独立的运行空间,每个进程都有一个进程控制块;
进程间通信:既然进程是独立运行,我们需要一种机制保证不同进程信息的交换,进程间通信主要包括:管道,IPC(共享内存,信号,消息队列),套接字;
PCNTL扩展:PHP的一个进程扩展,主要用到pcntl_alarm()函数,详细介绍请查阅官网.

实现原理    

用一个三维数组保存所有需要执行的任务,一级索引为时间戳,值为执行任务的方法、回调参数等,具体数组形式如下:

array(  '1438156396' => array(    array(1,array('Class','Func'), array(), true),   ))说明:1438156396 时间戳array(1,array('Class','Func'), array(), true) 参数依次表示: 执行时间间隔,回调函数,传递给回调函数的参数,是否持久化(ture则一直保存在数据中,否则执行一次后删除)

这些任务可以是任意类的方法。既然是定时任务,我们需要一个类似计时的东东,此方案采用信号量去做,每一秒向当前进程发送SIGALRM信号,并捕获该信号,触发信号处理函数,循环遍历数据,判断是否有当前时间需要执行的任务。如果有则采用回调方式触发,并把参数传递给该方法。

<?php /** *定时器 */ class Timer {  //保存所有定时任务  public static $task = array();    //定时间隔   public static $time = 1;    /**  *开启服务   *@param $time int   */  public static function run($time = null)   {   if($time)    {      self::$time = $time;    }    self::installHandler();    pcntl_alarm(1);   }   /**   *注册信号处理函数   */   public static function installHandler()   {    pcntl_signal(SIGALRM, array('Timer','signalHandler'));   }    /**   *信号处理函数   */   public static function signalHandler()   {    self::task();   //一次信号事件执行完成后,再触发下一次   pcntl_alarm(self::$time);   }    /**   *执行回调   */   public static function task()   {    if(empty(self::$task))    {//没有任务,返回      return ;    }    foreach(self::$task as $time => $arr)   {      $current = time();        foreach($arr as $k => $job)    {//遍历每一个任务       $func = $job['func']; /*回调函数*/       $argv = $job['argv']; /*回调函数参数*/     $interval = $job['interval']; /*时间间隔*/       $persist = $job['persist']; /*持久化*/        if($current == $time)       {//当前时间有执行任务       //调用回调函数,并传递参数         call_user_func_array($func, $argv);            //删除该任务        unset(self::$task[$time][$k]);       }       if($persist)       {//如果做持久化,则写入数组,等待下次唤醒         self::$task[$current+$interval][] = $job;       }    }    if(empty(self::$task[$time]))    {     unset(self::$task[$time]);    }    }   }    /**   *添加任务   */   public static function add($interval, $func, $argv = array(), $persist = false)   {    if(is_null($interval))    {     return;    }    $time = time()+$interval;   //写入定时任务   self::$task[$time][] = array('func'=>$func, 'argv'=>$argv, 'interval'=>$interval, 'persist'=>$persist);   }    /**   *删除所有定时器任务   */   public function dellAll()   {    self::$task = array();   } }            
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表