资讯专栏INFORMATION COLUMN

PHP定时器那点事

tyheist / 3260人阅读

摘要:如果把改成,还能实现微秒纳秒级定时器,岂不美哉实践中应当尽量避免用这种方式实现定时器,不仅因为低效,还略有危险。接下来看看中使用定时器的正确姿势。在这种形式下,实现定时器就有多种玩法。

转载请注明文章来源:https://tlanyan.me/timer-in-php/

常见的定时器有两种:一种周期性定时执行,例如每天的凌晨三点出报表;另一种在指定时间后执行(一次),例如会员登录系统五分钟后发放每日登录奖励。两种情况对应shell中的cronat命令,与JavaScript中的setIntervalsetTimeout函数类似(严格来说setInterval是周期性执行,指定时间点执行需要自行处理)。

做web开发的PHP程序员对JavaScript中的两个定时器函数应该都还熟悉,回到PHP层面就有点傻眼:PHP中有sleep,但是没有(内置)定时器函数可用。sleep函数勉强可以做到,但会导致进程阻塞,期间不能做其他事(或无响应)。为什么PHP没能提供定时器(Timer)这个功能呢?

原因

个人认为,web开发中PHP不能使用定时器的本质原因是可控 常驻内存运行环境的缺失。两个要点:第一常驻内存,第二可控。CGI模式下,进程执行完脚本后直接退出,不能指望其到指定时间运行任务;PHP-FPM模式下,进程(绝大多数)常驻内存,但不可控。

不可控的意思是执行PHP的进程不受PHP代码影响,进程的入口点和退出时机由额外的程序控制。例如FPM模式下,PHP脚本中的exitdie函数只中断脚本的执行,不会对执行脚本的进程产生特别的影响(内存泄露除外)。PHP开发人员编写的脚本是进程的执行体,执行完毕后就从进程的执行上下文中卸载出去。这种情况下,执行PHP脚本的时机仍然由外部驱动,没有外部请求PHP代码就安详的躺在硬盘上,什么都不做,也就定时任务。

由于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") . "
", FILE_APPEND);
   sleep(5);
}while(true);

请求http://localhost:8080/test.php文件后,监测/tmp/out.dat文件,会发现不断有内容输出,无论客户端是否断开连接、关闭浏览器或者重启电脑(不能重启服务器)。这说明程序一直在执行,并且也实现了我们想要的定时器功能。如果把sleep改成usleeptime_nanosleep,还能实现微秒、纳秒级定时器,岂不美哉?

实践中应当尽量避免用这种方式实现定时器,不仅因为低效,还略有危险。原因之一是每次请求会占用一个进程,请求十万次需要十万个进程,基本上会导致系统崩溃或后续请求无响应;另外如果打开了session,但是忘记调用session_write_close,会导致同一个用户的后续请求被hang住(session活跃时处于加锁状态,不关闭session会导致后续进程无法打开session)。

web开发应当越快响应用户的请求越好,在web开发中用这种方式强行实现定时器,会让整个web应用处于不稳定、不可靠或不可预测状态。孟子曰:知而慎行,君子不立于危墙之下。不靠谱的做法要尽量避免,顺带也避免背锅和甩锅。

接下来看看PHP中使用定时器的正确姿势。

正确的姿势

PHP实现定时器的做法可简单归结为如下几种:

使用cron、Jenkins等调度工具做周期性定时任务(既可以是执行脚本,也可以是请求某个网址);

一次性执行任务通过消息队列、数据库等方式投递给第三方程序执行;

像WordPress一样模拟定时任务,但要记住这种方式依赖于客户端请求,并需自行处理好进程并发问题;

使用常驻内存型方式运行PHP程序,即CLI模式。

除了第三种做法,其他方式都是推荐的,具体方案请结合实际需求。作为PHP程序员,当然还是首选用PHP来做,也就是CLI模式。

CLI模式

摸着良心说,CLI模式让PHP发挥的空间拓展不少。在CLI模式下,程序的入口点就是脚本,且代码可以常驻内存,进程完全由PHP代码控制。在这种形式下,实现定时器就有多种玩法。本文列出几种做法,抛砖引玉:

使用swooleworkerman等框架,内置(高精度)定时器;

使用多进程(池)/多线程(池)技术(pcntlpthreads拓展在CLI模式下才可用);

处理tick或者alarm等信号;

使用libeventlibev等事件驱动库;

sleep加循环或自己实现事件循环。

想折腾的话自己用2-5方案,不想折腾swooleworkerman等框架是首选,稳定可靠。

总结

区分HTTP请求和任务的关系,实现定时任务就简单了。至于用不用PHP来实现,那是另外一回事。当然作为web开发的首选语言,PHP实现定时任务也是轻而易举的。

本文感谢“微通广州”的赞助。

参考

http://php.net/manual/en/func...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/28883.html

相关文章

  • 关于性能优化的点事——函数节流

    摘要:函数节流背景中的函数大多数情况下都是由用户主动调用触发的除非是函数本身的实现不合理否则一般不会遇到跟性能相关的问题但在少数情况下函数的触发不是由用户直接控制的在这些场景下函数可能被非常频繁调用而造成大的性能问题场景事件事件滚动事件共同的特征 函数节流 背景 javascript中的函数大多数情况下都是由用户主动调用触发的, 除非是函数本身的实现不合理, 否则一般不会遇到跟性能相关的问题...

    khlbat 评论0 收藏0
  • 邮件发送点事

    摘要:配置在中,修改为,这样的函数就可以发出正常的邮件了。没找到哪儿改,所以我就直接用发送了。同时,邮件发送是一个耗时操作,不应该让进程长时间等待。相对复杂一些,它会对邮件内容进行签名,然后收件服务器通过获取公钥,核对签名是否正确。 搭建Postfix 操作系统我个人比较喜欢CentOs,原生的系统都装有,没有的话 可以自己谷歌,这个很简单 ,跳过。需要注意的是,Postfix默认只对本地I...

    acrazing 评论0 收藏0
  • 聊聊加密点事——PHP加密最佳实践

    摘要:所谓对称加密,就是加密和解密使用同一秘钥,这也是这种加密算法最显著的缺点之一。非对称加密算法由于对称加密在通信加密领域的缺陷,年和提出了非对称加密的概念。非对称加密,其主要缺点之一就是慢,适合加密少量数据。 1. 加密的目的 加密不同于密码,加密是一个动作或者过程,其目的就是将一段明文信息(人类或机器可以直接读懂的信息)变为一段看上去没有任何意义的字符,必须通过事先约定的解密规则才能将...

    lcodecorex 评论0 收藏0
  • 聊聊加密点事——PHP加密最佳实践

    摘要:所谓对称加密,就是加密和解密使用同一秘钥,这也是这种加密算法最显著的缺点之一。非对称加密算法由于对称加密在通信加密领域的缺陷,年和提出了非对称加密的概念。非对称加密,其主要缺点之一就是慢,适合加密少量数据。 1. 加密的目的 加密不同于密码,加密是一个动作或者过程,其目的就是将一段明文信息(人类或机器可以直接读懂的信息)变为一段看上去没有任何意义的字符,必须通过事先约定的解密规则才能将...

    Mr_zhang 评论0 收藏0
  • php 学习笔记之关于时区的点事

    摘要:科普一下什么是时区众所周知地球绕着太阳转的同时也会自转因此同一时刻不同地区所接收到太阳照射的情况不同所以有的地区是日出有的地区是日落还有的地区可能是黑夜既然地球上的不同地区时间不同那总要有统一的时间刻度才能方便文化科技交流吧不然大家说的都是 科普一下什么是时区 众所周知,地球绕着太阳转的同时也会自转,因此同一时刻不同地区所接收到太阳照射的情况不同,所以有的地区是日出,有的地区是日落,还...

    xzavier 评论0 收藏0

发表评论

0条评论

tyheist

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<