参考链接,
PHP本身没有定时功能,PHP也不能多线程,PHP的定时任务功能需要和其他工具结合才能实现,比如内置的wp-cron函数就非常强大。本文我们就来深入剖析一下PHP定时任务的几种常见思路。
在Linux服务器上使用PHP的计划执行
先从相对复杂的PHP服务器执行说起,只要服务器上安装了PHP,那么无论是否安装了nginx或者其他服务器环境软件,都可以执行PHP文件。在Linux下,使用命令行来安排任务是一个绝佳的选择,也是最高效的选择。
首先进入命令行模式,Linux作为服务器,一般默认进入命令行模式,当然我们一般都是通过putty等工具远程连接服务器来管理服务器,为了方便,我们以root用户登录,在命令行中输入:
crontab -e
之后会打开一个非编辑状态的文件,也就是 vi 的编辑界面。按下键盘上的 i 键,就可以进入编辑模式,编辑内容。这个文件的每一行都是一个计划任务,我们新建一行就相当于新建了一项计划任务(当然,这就意味着该行要按照一定的格式书写)。我们来举个例子,增加一行,内容如下:
00 * * * * lynx -dump https://www.yourdomain.com/script.php
这是什么意思呢?其实上面这一行由两部分组成,第一部分是时间,第二部分是操作内容。例如:
00 * * * *
意思是当前分钟为00的时候,执行定时任务。时间部分由5个时间参数组成,分别是:
分 时 日 月 周
第一列代表第 1 至 59 分钟,每分钟用或 */1 表示,/n 表示每 n 分钟,例如 */8 表示每 8 分钟,依此类推。
第二列表示小时1至23(0表示0点)
第三列代表日期1至31
第四列代表 1 至 12 月份
第 5 列标识星期几,从 0 到 6(0 表示星期日)
后半句是操作的具体内容。
lynx -dump https://www.yourdomain.com/script.php
意思是通过lynx来访问这个URL,我们主要使用lynx、curl、wget来实现远程访问URL,如果想提高效率,直接使用php执行本地的php文件是最好的选择,例如:
00 */2 * * * /usr/local/bin/php /home/www/script.php
这条语句可以每隔2小时0分钟通过Linux内部PHP环境执行一次.php,注意这里不是通过URL访问,经过服务器环境执行,而是直接执行,既然绕过了服务器环境,效率当然就高很多了。
OK,你已经添加了几个计划任务。点击键盘上的Esc键,输入“:wq”并回车,保存计划任务。你还可以看到屏幕上有提示,提示你新建了计划任务。接下来就是编写你的.php文件了。
更多的用法这里就不介绍了,如果想更灵活的使用这个计划任务功能,还是要深入研究一下。
使用bat在服务器上定时执行php
网上和Linux上都有类似的cmd和bat文件,bat文件类似于shell文件,执行这个bat文件就相当于是按照顺序执行里面的命令(当然也可以通过逻辑编程)。所以我们可以利用bat命令文件来实现服务器上的PHP定时任务。其实网上的定时任务的原理和Linux上的是一样的,只是方法和途径不一样而已。好,我们开始吧。
首先在你认为比较合适的位置创建一个cron.bat文件,然后用文本编辑器(记事本就可以)打开它,并在里面写入以下内容:
D:\php\php.exe -q D:\website\test.php
这句话的意思是使用php.exe来执行php文件test.php,跟上面的一样,绕过了服务器环境,执行效率更高,写完之后点击保存,关闭编辑器。
接下来就是设置一个计划任务来运行cron.bat。依次打开:“开始->控制面板->任务计划->添加任务计划”,在打开的界面中设置计划任务的时间和密码,选择挂载cron.bat。OK,这样一个计划任务就建立好了,右键点击这个计划任务,运行,这个计划任务就开始执行了。这时候就会运行cron.bat来处理,然后cron.bat就会执行php。
在非自有服务器(虚拟主机)上实现PHP计划任务
如果站长没有自己的服务器而是租用虚拟主机的话,就无法进入服务器系统进行上述操作了,这时候该如何进行PHP计划任务呢?其实方法有多种。
使用(true)并睡眠无限循环
在 PHP 文档的开头,只需说:
ignore_user_abort(true);
这时候通过URL访问这个PHP时,即使用户关闭了浏览器(断开连接),PHP仍然会在服务器上继续执行。利用这个特性,我们可以实现一个很强大的功能,那就是通过它来激活计划任务,激活之后它就可以为所欲为,其实有点类似于后台任务。
Sleep(n) 的意思是程序执行到这里之后就不再进一步执行,而是休息 n 秒。如果你访问这个 php,你会发现页面至少需要 n 秒才能加载完成。其实这种长时间等待的行为比较耗资源,不宜大量使用。
那么如何实现计划任务呢?可以使用以下代码:
0 ? $loop : 0;
if(!$loop) break; // 如果循环的间隔为零,则停止
sleep($loop);
// ...
// 执行某些代码
// ...
@unlink(dirname(__FILE__).'/cron-run'); // 这里就是通过删除cron-run来告诉程序,这个定时任务已经在执行过程中,不能再执行一个新的同样的任务
$loop = $interval;
} while(true);
通过执行上述PHP代码,可以实现计划任务,并且直到删除cron文件后任务才会停止。
但是有一个问题,就是如果用户直接访问这个PHP的话,其实是没有任何影响的,页面会停在这个地方,继续加载。有没有办法可以消除这个影响呢?帮我们解决这个问题。
在请求访问某个文件时,有可能不得到返回结果而继续执行程序。这和 curl 的通常用法不同,当我们使用 curl 访问网页时,必须等到 curl 加载完网页后,才能执行 curl 后面的代码。虽然 curl 其实也可以实现“非阻塞”请求,但是要复杂得多,所以我们更希望在规定的时间内,比如 1 秒内,完成对访问路径的请求。完成后,不管该路径是否返回内容,它的任务就到此结束,程序可以继续执行。利用这个特性,我们把它加入到正常的程序流程中,向上面我们创建的计划任务 php 的地址发送请求,这样就可以在后台执行计划任务了。如果上面 php 的 URL 地址是,那么我们在编程中可以这样做:
// ...
// 正常的php执行程序
// ..
// 远程请求(不获取内容)函数,下面可以反复使用
function _sock($url) {
$host = parse_url($url,PHP_URL_HOST);
$port = parse_url($url,PHP_URL_PORT);
$port = $port ? $port : 80;
$scheme = parse_url($url,PHP_URL_SCHEME);
$path = parse_url($url,PHP_URL_PATH);
$query = parse_url($url,PHP_URL_QUERY);
if($query) $path .= '?'.$query;
if($scheme == 'https') {
$host = 'ssl://'.$host;
}
$fp = fsockopen($host,$port,$error_code,$error_msg,1);
if(!$fp) {
return array('error_code' => $error_code,'error_msg' => $error_msg);
}
else {
stream_set_blocking($fp,true);//开启了手册上说的非阻塞模式
stream_set_timeout($fp,1);//设置超时
$header = "GET $path HTTP/1.1\r\n";
$header.="Host: $host\r\n";
$header.="Connection: close\r\n\r\n";//长连接关闭
fwrite($fp, $header);
usleep(1000); // 这一句也是关键,如果没有这延时,可能在nginx服务器上就无法执行成功
fclose($fp);
return array('error_code' => 0);
}
}
_sock('www.yourdomain.com/script.php');
// ...
// 继续执行其他动作
// ..
在一个计划任务提交结果程序中添加此代码,设置时间,提交,然后执行上述代码即可启动计划任务。而且对于提交任务的用户来说,页面不会有任何堵塞。
利用用户访问行为执行一些延时任务
但是使用sleep来实现定时任务是一种非常低效的方案,我们希望不要使用这种方式来执行,这样效率问题就可以解决了。我们利用用户的访问行为来执行任务。用户对网站的访问其实是一个非常丰富的行为资源,包括搜索引擎蜘蛛对网站的访问,都可以算作这种类型。当用户访问网站时,内部会添加一个,检查任务列表中是否有未执行的任务,如果有,则执行该任务。对于用户来说,使用上面这种访问方式,并不觉得自己的访问做出了这样的贡献。但是这种访问方式的缺点就是访问非常不规律,比如你想在凌晨2点执行一个任务,但是这个时间段很倒霉,一直到凌晨6点都没有用户或者任何行为到达你的网站,又有新的访问。这就导致你原本计划在凌晨2点执行的任务,直到凌晨6点才被执行
这就涉及到定时任务列表了,也就是说你需要一个列表来记录所有任务的时间,执行什么任务。一般来说,很多系统都是通过数据库来记录这些任务列表的,比如这个。我利用文件读写特性提供了一个开源项目php-cron托管在 ,你可以去看看。总之,如果要管理多个定时任务,用上面单个php是无法合理安排的,必须要想办法建一个列表。由于这里的逻辑比较复杂,我就不详细阐述了,只停留在思路层面。
借用第三方计划任务跳板
有意思的是,有些服务商提供的定时任务类型是多种多样的。比如阿里云的ACE就提供了单独的定时任务,你填写你的应用下的某个URI就可以了。百度云BCE提供了服务器监控功能,会按照一定的时间规律,每天访问应用下的固定URI。类似的第三方平台上也有很多定时任务,你可以以这些第三方的定时任务为跳板,服务于你网站的定时任务。比如你可以在阿里云ACE上设置每天凌晨2点的定时任务,执行的URI是/cron.php。然后你创建一个cron.php,它会使用该URL访问你真正想要执行某些任务的网站,比如上面这种,在cron.php中可以访问多个URL。然后上传cron.php到你的ACE中,让ACE的定时任务访问/cron.php,然后让cron.php远程请求目标网站的定时任务脚本。
回收包含文件(有待验证)
PHP 面向过程的特性使得它的程序是从上到下执行的,利用这个特性,当我们使用某个文件时,会先执行引入的文件,等已知文件中的程序执行完后,再进一步执行。如果我们建立一个循环,然后使用 sleep 不断执行某个文件,循环执行某个程序,就可以达到定时执行的目的。我们再进一步,用文件本身来实现循环,而不是使用 while(true)。例如我们建立一个 do.php,它的内容如下:
if(...) exit(); // 通过某个开关来关闭执行
// ...
// 执行某些程序
// ...
sleep($loop); // 这个$loop在include('do.php');之前赋值
include(dirname(__FILE__).'/do.php');
这样执行的思路其实和while类似。而且也是用到了sleep,效率比较低。
PHP 计划任务很有意思,说实话用系统的 php.exe 直接执行 PHP 文件效率更高,但是对于很多普通站长来说,虚拟主机无法直接用 PHP 执行原生程序,本文只提供一些解决方案,本人也只是学习,有很多问题或者表述不正确的地方,希望大家指出,大家可以通过本文的思路,制定自己的解决方案,希望大家能把解决方案发布出来和我一起讨论。
扫一扫在手机端查看
-
Tags : php 定时任务只在主服务器执行
我们凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求,请立即点击咨询我们或拨打咨询热线: 13761152229,我们会详细为你一一解答你心中的疑难。