PHP本身是没有定时功能的,这主要是因为PHP无法实现多线程。所谓多线程,就是你写一个php文件,可以在访问它时,同时完成多个任务,实际上,php是从上往下顺序执行的,执行过程中没有办法分支执行。PHP的定时任务功能必须通过和其他工具结合才能实现,例如WordPress内置了wp-cron的功能,很厉害。本文,我们就来深入的解析几种常见的php定时任务的思路。
Linux服务器上使用CronTab定时执行php ↑
我们先从相对比较复杂的服务器执行php谈起。服务器上安装了php,就可以执行php文件,无论是否安装了nginx或Apache这样的服务器环境软件。而Linux中,使用命令行,用CronTab来定时任务,又是绝佳的选择,而且也是效率最高的选择。
首先,进入命令行模式。作为服务器的linux一般都默认进入命令行模式的,当然,我们管理服务器也一般通过putty等工具远程连接到服务器,为了方便,我们用root用户登录。在命令行中键入:
之后就会打开一个文件,并且是非编辑状态,则是vi的编辑界面,通过敲键盘上的i,进入编辑模式,就可以编辑内容。这个文件中的每一行就是一个定时任务,我们新建一行,就是新建一条定时任务(当然是指这一行内按照一定的格式进行书写)。我们现在来举个例子,增加一行,内容如下:
这是什么意思呢?实际上上面这一行由两部分组成,前面一部分是时间,后面一部分是操作内容。例如上面这个,
就是指当当前时间的分钟数为00时,执行该定时任务。时间部分由5个时间参数组成,分别是:
第2列表示小时1~23(0表示0点)
第3列表示日期1~31
第4列表示月份1~12
第5列标识号星期0~6(0表示星期天)
整个句子的后面部分就是操作的具体内容。
意思就是说通过lynx访问这个url。我们在使用中主要用到lynx、curl、wget来实现对url的远程访问,而如果要提高效率,直接用php去执行本地php文件是最佳选择,例如:
这条语句就可以在每2小时的0分钟,通过linux内部php环境执行script.php,注意,这里可不是通过url访问,通过服务器环境来执行哦,而是直接执行,因为绕过了服务器环境,所以效率当然要高很多。
好了,已经添加了几条需要的定时任务了吧。点击键盘上的Esc键,输入“:wq”回车,这样就保存了设置的定时任务,屏幕上也能看到提示创建了新的定时任务。接下来就是好好写你的script.php了。
关于CronTab的更多用法这里就不介绍了,如果你想更灵活的使用这个定时任务功能,应该自己再去深入学习一下crontab。
Windows服务器上使用bat定时执行php ↑
windows上和linux上有一个类似的cmd和bat文件,bat文件类似于shell文件,执行这个bat文件,就相当于依次执行里面的命令(当然,还可以通过逻辑来实现编程),所以,我们可以利用bat命令文件在windows服务器上面实现PHP定时任务。实际上在windows上定时任务,和linux上道理是一样的,只不过方法和途径不同。好了下面开始。
首先,在一个你觉得比较适当的位置创建一个cron.bat文件,然后用文本编辑器打开它(记事本都可以),在里面写上这样的内容:
这句话的意思就是,使用php.exe去执行test.php这个php文件,和上面的contab一样,绕过了服务器环境,执行效率也比较高。写好之后,点击保存,关闭编辑器。
接下来就是设置定时任务来运行cron.bat。依次打开:“开始–>控制面板–>任务计划–>添加任务计划”,在打开的界面中设置定时任务的时间、密码,通过选择,把cron.bat挂载进去。确定,这样一个定时任务就建立好了,在这个定时任务上右键,运行,这个定时任务就开始执行了,到点时,就会运行cron.bat处理,cron.bat再去执行php。
非自有服务器(虚拟主机)上实现php定时任务 ↑
如果站长没有自己的服务器,而是租用虚拟主机,就无法进入服务器系统进行上述操作。这个时候应该如何进行php定时任务呢?其实方法又有多个。
使用ignore_user_abort(true)和sleep死循环 ↑
在一个php文档的开头直接来一句:
这时,通过url访问这个php的时候,即使用户把浏览器关掉(断开连接),php也会在服务器上继续执行。利用这个特性,我们可以实现非常牛的功能,也就是通过它来实现定时任务的激活,激活之后就随便它自己怎么办了,实际上就有点类似于后台任务,php的多线程也就有了一点眉目。
而sleep(n)则是指当程序执行到这里时,暂时不往下执行,而是休息n秒钟。如果你访问这个php,就会发现页面起码要加载n秒钟。实际上,这种长时间等待的行为是比较消耗资源的,不能大量使用。
那么定时任务到底怎么实现呢?使用下面的代码即可实现:
通过执行上面这段php代码,即可实现定时任务,直到你删除cron-switch文件,这个任务才会停止。
但是有一个问题,也就是如果用户直接访问这个php,实际上没有任何作用,页面也会停在这个地方,一直处于加载状态,有没有一种办法可以消除这种影响呢?fsockopen帮我们解决了这个问题。
fsockopen可以实现在请求访问某个文件时,不必获得返回结果就继续往下执行程序,这是和curl通常用法不一样的地方,我们在使用curl访问网页时,一定要等curl加载完网页后,才会执行curl后面的代码,虽然实际上curl也可以实现“非阻塞式”的请求,但是比fsockopen复杂的多,所以我们优先选择fsockopen,fsockopen可以在规定的时间内,比如1秒钟以内,完成对访问路径发出请求,完成之后就不管这个路径是否返回内容了,它的任务就到这里结束,可以继续往下执行程序了。利用这个特性,我们在正常的程序流中加入fsockopen,对上面我们创建的这个定时任务php的地址发出请求,即可让定时任务在后台执行。如果上面这个php的url地址是http://www.yourdomain.com/script.php,那么我们在编程中,可以这样:
把这段代码加入到某个定时任务提交结果程序中,在设置好时间后,提交,然后执行上面这个代码,就可以激活该定时任务,而且对于提交的这个用户而言,没有任何页面上的堵塞感。
借用用户的访问行为来执行某些延迟任务 ↑
但是上面使用sleep来实现定时任务,是效率很低的一种方案。我们希望不要使用这种方式来执行,这样的话就可以解决效率问题。我们借用用户访问行为来执行任务。用户对网站的访问其实是一个非常丰富的行为资源,包括搜索引擎蜘蛛对网站的访问,都可以算作这个类型。在用户访问网站时,内部加一个动作,去检查任务列表中是否存在没有被执行的任务,如果存在,就将这个任务执行。对于用户而言,利用上面所说的fsockopen,根本感觉不到自己的访问竟然还做出了这样的贡献。但是这种访问的缺点就是访问很不规律,比如你希望在凌晨2点执行某项任务,但是这个时间段非常倒霉,没有用户或任何行为到达你的网站,直到早上6点才有一个新访问。这就导致你原本打算2点执行的任务,到6点才被执行。
这里涉及到一个定时任务列表,也就是说你需要有一个列表来记录所有任务的时间、执行什么内容。一般来说,很多系统会采用数据库来记录这些任务列表,比如wordpress就是这样做的。我则利用文件读写特性,提供了托管在github上的开源项目php-cron,你可以去看看。总之,如果你想要管理多个定时任务,靠上面的单个php是无法合理布局的,必须想办法构建一个schedules列表。由于这里面的逻辑比较复杂,就不再详细阐述,我们仅停留在思路层面上。
借用第三方定时任务跳板 ↑
很好玩的是,一些服务商提供了各种类型的定时任务,例如阿里云的ACE提供了单独的定时任务,你可以填写自己应用下的某个uri。百度云BCE提供了服务器监测功能,每天会按照一定的时间规律访问应用下的固定uri。类似的第三方平台上还有很多定时任务可以用。你完全可以用这些第三方定时任务作为跳板,为你的网站定时任务服务。比如说,你可以在阿里云ACE上建立一个每天凌晨2点的定时任务,执行的uri是/cron.php。然后你创建一个cron.php,里面则采用fsockopen去访问你真正要执行某些任务的网站的url,例如上面的http://www.yourdomain.com/script.php,而且在cron.php中还可以访问多个url。然后把cron.php上传到你的ACE上面去,让ACE的定时任务去访问/cron.php,然后让cron.php去远程请求目标网站的定时任务脚本。
PHP定时任务是一个非常有意思的东西,虽然说实话,用系统的php.exe去直接执行php文件的效率更高,但是对于很多普通站长而言,虚拟主机是无法做到直接php执行原生程序的。本文仅提供一些解决的思路,你可以通过本文的思路,开发出自己的一种解决方案,届时,希望你能将方案发布,并与我们一起探讨。
来自 http://www.tangshuang.net/dev/70.html
Moodle的Cron进程是必须在后台定期运行的PHP脚本(标准安装过程的一部分)。Cron脚本可以在不同的计划时间间隔运行不同的任务。
重要:设置服务器时不要跳过设置Cron进程。如若跳过,你的Moodle站点可能不会正常运行。
特 殊的程序(一般称作——这并不奇怪——Cron)用来在固定时间间隔运行Cron脚本。Cron脚本可以运行很多任务,包括邮件发送,报告更新,RSS源 更新,活动完成情况及发布论坛消息等。因为不同的任务有不同的计划时间,所以运行Cron脚本后不是每一个任务都会被执行。
Cron程序是 基于Unix系统(包括Linux和OSX)的核心部分,用来运行所有与时间相关的服务。在Windows系统上,最简单的的解决方案是用Windows 任务计划创建一个按照固定时间间隔运行的任务。如若是虚拟主机,需要找到如何在虚拟主机上配置Cron的相关文档(或直接寻求支持)。
从本质上讲,任务涉及到向系统中的Cron活动列表添加一行独立的命令。在基于Unix的系统上,此列表名为“crontab”,为所有用户共有。
目录
[隐藏]综合讨论
根据服务器的类型查阅后面的章节;这一章节包含一些基础背景信息。
实质上部署Cron需要两步:
- 确认将要执行的命令是否正确
- 找到执行命令的正确的地方
制定出Moodle的Cron命令
Moodle可以在安装过程中使用2种不同的脚本来部署Cron。他们是:
- CLI (命令行解析器) 脚本。这将会在路径:
- /path/to/moodle/admin/cli/cron.php
- 如若没有疑问,这会是正确的可用脚本。但执行它还需要在电脑上安装“PHP CLI”的程序。最终的命令类似于下面这条命令:
- /usr/bin/php /path/to/moodle/admin/cli/cron.php
- 尝试着执行这条命令,查验它是否能正常运行
- 基于Web的脚本。需要在Web浏览器上运行脚本,并能访问类似于http://your.moodle.site/admin/cron.php的地址。最终能够找到的基于Web浏览器(如,wget)的命令行可能会类似于如下代码:
-
/usr/bin/wget http://your.moodle.site/admin/cron.php
- 这样做的优势是可以在“任何地方”执行脚本。如果你电脑不能正常运行,那么你就可以去其他地方执行脚本。
找到执行命令的正确的地方
命 令放在什么地方确实取决于正在使用的系统,找到并阅读系统平台或服务商的相关文档以谋求解决方案。大多数情况下,Cron执行的是包含有建立的正确命令 (如上所示)并把它添加到计划任务中去,到预定时间之时即可执行命令并对文件进行排序。这也许通过一个特定的用户界面既可做到,或者也可直接通过编辑文件 目录。
如果使用CLI来执行Cron的话,首先要确保执行Cron进程的用户是正确的。Web版本则不会有这种问题。
例如,在Ubuntu/Debian等Linux系统上安装Cron后,假设以root身份登录系统:
使用crontab命令为用户www-data打开crontab编辑器。下面命令为在基于Debian系统上的Apache执行cron
$ crontab -u www-data -e
上述命令会打开一个编辑窗口。如需每一分钟运行一次CLI Cron脚本,添加下面这行命令:
*/1 * * * * /usr/bin/php /path/to/moodle/admin/cli/cron.php >/dev/null
注意:命令最后的 >/dev/null 是把所有输出都发送到“bin”并阻止每一分钟就接受一封邮件。
在系统上设置Cron
根据服务器类型选择如下信息:
- 在Unix或Linux上设置Cron——各种Unix和Linux版本操作系统上的Cron服务。
- 在Windows上设置Cron——Windows上的Cron服务
- Apple 系统——内置的"crontab"服务与在Unix或Linux上设置Cron是一样的。然而,可能你会想使用“苹果方式”来启动它——参阅在MAC系统上设置Cron。
- 使用Web服务商服务设置Cron——不同的Web服务商设置Cron的例子。
这里还有更多关于特殊主机设置Cron的说明(请检查他们是否已经过时):
使用第三方Cron服务
除了上述部署在自己服务器上Cron服务之外,还可以使用第三方Cron服务(通常称之为webcron):
- EasyCron——一个webcron提供商,有了它,无需再使用crontab或其他任务调度程序来设置cron job。
设置Moodle内的Cron
下面是一些Moodle内关于Cron操作方面的设置:
- 设置Cron——Cron进程的密码及CLI设置。
远程Cron
采用基于Web版本的Cron可以极其完美的把Cron进程放在与Moodle服务器不同的地方。例如,Unix服务器上的Cron服务可以调用基于Moodle服务器的Windows上的Cron Web页面以进行操作Cron。
为数个Moodle服务器执行Cron
- 如果所有的服务器均为Web服务器且他们共同服务于一个Moodle站点(某种类型的集群服务器),那么只有一个服务器可以执行Cron job。
- 如果其中2台Web服务器运行了2个不同的Moodle站点,那么每个站点都均需要一个Cron job。(即便是一个单独的Apache Web服务器也可以通过虚拟主机功能使用不同的域名来运行不同的Moodle站点,参见:https://httpd.apache.org/docs/2.2/vhosts/index.html。)
- 如果你指的是这个设置:https://moodle.org/mod/forum/discuss.php?d=238005,它是说一台Web服务器和一个Moodle实例。那么一个Cron job是对的。
- 参阅这个论坛主题。
Moodle 2.7+的Cron
Cron获得了重大更新,现在可以支持计划内与计划外的任务了——[http://tracker.moodle.org/browse/MDL-25499 MDL-25499]。这些改变的益处是:
- 管理员可以配置每个任务的日程。参见计划任务
- 任务可以同时进行
- 任务进程可以锁定以防同一个任务在同一时间被不同的进程执行
这样处理带来的结果就是Cron可以更为频繁的执行,这意味着(例如)论坛帖子可以更快地发送出去。管理员可以像之前一样保持同一时间执行Cron,但是还是强烈建议把执行Cron的频率增加到至少每分钟一次。
调试计划任务
有时,特定的Cron任务不能正常运行。在Moodle 2.7之前的版本——任何不能正常运行的任务都会抛出异常以阻止其他Cron运行。检测每一次Cron是否完成的唯一方式是在执行Cron时添加一些自动检查的输出内容(如,搜索字符串“Cron完成于”)。
在Moodle 2.7及以后的版本,一个失败的计划任务不会阻止完成其他待办任务。当一个任务失败时,它会被标记为失败并计划重新尝试运行。如果重新尝试后任务依旧失败,那么下一次的计划时间会推后至最长到每24小时执行一次。检查计划任务管理员页面,就会明白如何判断任务是失败的(失败的任务计划时间肯定会有延迟——重新尝试运行失败的任务时肯定会有数秒的等待时间)。调试失败的任务的简单方法是使用CLI计划任务执行程序并检测输出结果。
另请参阅
Moodle论坛上的讨论:
- Cron——谁可以给我一些有关Cron功能确认信息?
- Cronjob问题
- 放慢Cron:避免同时Cron进行
- cron.php的可视性(能见度)
- 如何在Windows上记录计划任务的输出内容——当在Windows计划任务遇到问题时,这个话题给出了一个不错的、有用的想法,既是应该把计划任务的输出内容记录到日志文件中去。