转载请注明文章来源: https://tlanyan.me/timer-in-php/
常见的定时器有两种:一种周期性定时执行,例如天天的凌晨三点出报表;另外一种在指定时间后执行(一次),例如会员登陆系统五分钟后发放每日登陆奖励。两种状况对应shell中的cron
和at
命令,与JavaScript
中的setInterval
和setTimeout
函数相似(严格来讲setInterval
是周期性执行,指定时间点执行须要自行处理)。php
作web开发的PHP程序员对JavaScript中的两个定时器函数应该都还熟悉,回到PHP层面就有点傻眼:PHP中有sleep
,可是没有(内置)定时器函数可用。sleep
函数勉强能够作到,但会致使进程阻塞,期间不能作其余事(或无响应)。为何PHP没能提供定时器(Timer)这个功能呢?程序员
我的认为,web开发中PHP不能使用定时器的本质缘由是可控 常驻内存运行环境的缺失。两个要点:第一常驻内存,第二可控。CGI模式下,进程执行完脚本后直接退出,不能期望其到指定时间运行任务;PHP-FPM模式下,进程(绝大多数)常驻内存,但不可控。web
不可控的意思是执行PHP的进程不受PHP代码影响,进程的入口点和退出时机由额外的程序控制。例如FPM模式下,PHP脚本中的exit
、die
函数只中断脚本的执行,不会对执行脚本的进程产生特别的影响(内存泄露除外)。PHP开发人员编写的脚本是进程的执行体,执行完毕后就从进程的执行上下文中卸载出去。这种状况下,执行PHP脚本的时机仍然由外部驱动,没有外部请求PHP代码就安详的躺在硬盘上,什么都不作,也就定时任务。shell
因为PHP主要面向web开发,PHP这种执行模式稳定可靠,开发效率快。好比省去资源释放这一步,就避免了开发中不少工做量和坑。想一想某些第三方库代码中改时区、字符编码等还不还原,在常驻内存运行环境下几乎确定会致使后续请求有问题。但在FPM模式下,这种坑无心中直接趟平,省去许多调试时间,为程序员保住发际线作出了不小的贡献。数据库
问题已经了解,那么PHP中如何使用定时器执行定时任务?浏览器
在web环境下,PHP脚本默认有超时时间。去掉超时设置,就可让程序一直在后台运行(若是进程不退出的话)。例如如下代码在响应请求后继续后台运行,而且每五秒钟输出一次时间到文件:服务器
# test.php set_time_limit(0); # 取消超时设置,让脚本可一直运行 echo 'This is a background run forever script. Now you can leave me alone.'; fastcgi_finish_request(); # 结束当前请求 do{ file_put_contents("/tmp/out.dat", "test script, now:" . date("Y-m-d H:i:s") . "\n", FILE_APPEND); sleep(5); }while(true);
请求http://localhost:8080/test.php
文件后,监测/tmp/out.dat
文件,会发现不断有内容输出,不管客户端是否断开链接、关闭浏览器或者重启电脑(不能重启服务器)。这说明程序一直在执行,而且也实现了咱们想要的定时器功能。若是把sleep
改为usleep
、time_nanosleep
,还能实现微秒、纳秒级定时器,岂不美哉?swoole
实践中应当尽可能避免用这种方式实现定时器,不只由于低效,还略有危险。缘由之一是每次请求会占用一个进程,请求十万次须要十万个进程,基本上会致使系统崩溃或后续请求无响应;另外若是打开了session,可是忘记调用session_write_close
,会致使同一个用户的后续请求被hang住(session活跃时处于加锁状态,不关闭session会致使后续进程没法打开session)。session
web开发应当越快响应用户的请求越好,在web开发中用这种方式强行实现定时器,会让整个web应用处于不稳定、不可靠或不可预测状态。孟子曰:知而慎行,君子不立于危墙之下。不靠谱的作法要尽可能避免,顺带也避免背锅和甩锅。多线程
接下来看看PHP中使用定时器的正确姿式。
PHP实现定时器的作法可简单归结为以下几种:
除了第三种作法,其余方式都是推荐的,具体方案请结合实际需求。做为PHP程序员,固然仍是首选用PHP来作,也就是CLI模式。
摸着良心说,CLI模式让PHP发挥的空间拓展很多。在CLI模式下,程序的入口点就是脚本,且代码能够常驻内存,进程彻底由PHP代码控制。在这种形式下,实现定时器就有多种玩法。本文列出几种作法,抛砖引玉:
swoole
、workerman
等框架,内置(高精度)定时器;pcntl
、pthreads
拓展在CLI模式下才可用);libevent
、libev
等事件驱动库;sleep
加循环或本身实现事件循环。想折腾的话本身用2-5方案,不想折腾swoole
、workerman
等框架是首选,稳定可靠。
区分HTTP请求和任务的关系,实现定时任务就简单了。至于用不用PHP来实现,那是另一回事。固然做为web开发的首选语言,PHP实现定时任务也是垂手可得的。
本文感谢“微通广州”的赞助。