如何用纯PHP实现定时器
计时器任务在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 (1,array ('class ',' func '),array(),true)),说明:1438156396时间戳数组(1,array ('class ',' func '),array()。True)参数表示:执行时间间隔、回调函数、传递给回调函数的参数、是否持久(true始终保存在数据中,否则一次执行就删除)。这些任务可以是任何类的方法。由于这是一个定时任务,我们需要类似定时的东西。该方案利用信号量来完成,每秒向当前进程发送SIGALRM信号,捕捉信号,触发信号处理功能,循环遍历数据,判断当前时刻是否有任务需要执行。如果是,则由回调触发,并将参数传递给方法。
?Php /** * timer */class Timer {//保存所有计划任务public static $ task=array();//计时间隔为公共静态$ time=1;/* * * open service * @ param $ time int */public static函数run($ time=null){ if($ time){ self :3360 $ time=$ time;} SelF : InstallHandler();pcntl _ alarm(1);}/* * *寄存器信号处理函数*/公共静态函数install handler(){ pcntl _ signal(sigal RM,array ('timer ',' signal handler '));}/* * *信号处理函数*/公共静态函数信号处理程序(){ self :3360 task();//信号事件执行完成后,下次触发pcntl _ alarm(self :3360 $ time);}/* * *执行回调*/public static function task(){ if(empty(self :3360 $ task)){//无任务,返回;} 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 '];/* Persistence */if($ current==$ time){//当前有一个执行任务//调用回调函数并传递参数call _ user _ func _ array ($ func,$ argv);//删除此任务取消设置(self : $ task[$ time][$ k]);} if($ persist){//if persist,写入数组,等待self :3360 $ task[$ current $ interval][]=$ job的下一次唤醒;} } if(空(self : $ task[$ time]){ unset(self : $ task[$ time]);} } }/* * * add task */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);}/* * *删除所有计时器任务*/公共函数delall(){ self :3360 $ task=array();}}这是定时器类的核心部分。有一个静态变量保存所有要执行的任务。为什么这里是静止的?自己想想。当进程接收到SIGALRM信号时,它会触发signalHandler函数,然后顺序遍历数组,看看当前是否有任务要执行。如果是,它会回调,传递参数,删除当前作业,然后检查是否执行持久任务。如果是,则继续将当前作业写入事件数组,等待下一个触发器,最后为当前进程设置一个闹钟信号。可以看到,这个计时器会从内部再次触发一次,从而达到自循环的目的。
?php类DoJob { public function job($ param=array()){ $ time=time();回显“Time: {$time},Func:”。get_class()。':'.__FUNCTION__。'('.json_encode($param)。)\ n ';} }
这是回调类和函数。为了便于解释,增加了很多调试信息。定时器类和回调是可用的。让我们看看使用场景是什么样的。
?php需要_once(__DIR__)。/timer . PHP ');需要_一次(__DIR__)。/DoJob . PHP ');timer : delall();Timer:add(1,数组(' DoJob ',' Job '),数组(),true);Timer:add(3,数组(' DoJob ',' Job '),数组(' a'=1),false);回声“时间开始:”。时间()。\ n ';timer : run();而(1) {睡眠(1);pcntl _ signal _ dispatch();}代码很短。这里注册了两个作业,然后运行计时器,在无限循环中捕获触发动作的信号。如果没有捕捉到,则不会触发预注册处理功能。这种自循环计时器的开发已经完成。运行结果如下:
就像我们的场景类添加的任务一样,两个任务在90执行,一个是不带参数的持久化作业,另一个是带参数的非持久化作业,然后不再执行非持久化作业。
摘要
1.收到信号前,当前进程无法退出。在这里,我使用了conditIOn始终为真的循环。在我们的实际生产环境中,我们需要创造这样一个前提条件。例如,我们有一组服务,它们总是在运行。无论是io访问、等待socket链接等。当前服务不会终止,即使进程被阻塞,也不会有问题。2
以上就是本文的全部内容,希望对大家的学习有所帮助。
版权声明:如何用纯PHP实现定时器是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。