宝塔定时任务执⾏php源码任务_PHP实现执⾏定时任务的⼏
种思路详解
PHP本⾝是没有定时功能的,PHP也不能多线程。PHP的定时任务功能必须通过和其他⼯具结合才能实现,例如WordPress内置了wp-cron的功能,很厉害。本⽂,我们就来深⼊的解析⼏种常见的php定时任务的思路。
Linux服务器上使⽤CronTab定时执⾏php
我们先从相对⽐较复杂的服务器执⾏php谈起。服务器上安装了php,就可以执⾏php⽂件,⽆论是否安装了nginx或Apache这样的服务器环境软件。⽽Linux中,使⽤命令⾏,⽤CronTab来定时任务,⼜是绝佳的选择,⽽且也是效率最⾼的选择。
⾸先,进⼊命令⾏模式。作为服务器的linux⼀般都默认进⼊命令⾏模式的,当然,我们管理服务器也⼀般通过putty等⼯具远程连接到服务器,为了⽅便,我们⽤root⽤户登录。在命令⾏中键⼊:
crontab -e
之后就会打开⼀个⽂件,并且是⾮编辑状态,则是vi的编辑界⾯,通过敲键盘上的i,进⼊编辑模式,就
可以编辑内容。这个⽂件中的每⼀⾏就是⼀个定时任务,我们新建⼀⾏,就是新建⼀条定时任务(当然是指这⼀⾏内按照⼀定的格式进⾏书写)。我们现在来举个例⼦,增加⼀⾏,内容如下:
这是什么意思呢?实际上上⾯这⼀⾏由两部分组成,前⾯⼀部分是时间,后⾯⼀部分是操作内容。例如上⾯这个,
00 * * * *
就是指当当前时间的分钟数为00时,执⾏该定时任务。时间部分由5个时间参数组成,分别是:
分 时 ⽇ ⽉ 周
第1列表⽰分钟1~59 每分钟⽤或者 */1表⽰,/n表⽰每n分钟,例如*/8就是每8分钟的意思,下⾯也是类推
第2列表⽰⼩时1~23(0表⽰0点)
第3列表⽰⽇期1~31
第4列表⽰⽉份1~12
第5列标识号星期0~6(0表⽰星期天)
整个句⼦的后⾯部分就是操作的具体内容。
意思就是说通过lynx访问这个url。我们在使⽤中主要⽤到lynx、curl、wget来实现对url的远程访问,⽽如果要提⾼效率,直接⽤php去执⾏本地php⽂件是最佳选择,例如:
00 */2 * * * /usr/local/bin/php /home/www/script.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⽂件,然后⽤⽂本编辑器打开它(记事本都可以),在⾥⾯写上这样的内容:
D:\ -q D:\website\test.php
这句话的意思就是,使⽤去执⾏test.php这个php⽂件,和上⾯的contab⼀样,绕过了服务器环境,执⾏效率也⽐较⾼。写好之后,点击保存,关闭编辑器。
接下来就是设置定时任务来运⾏cron.bat。依次打开:“开始–>控制⾯板–>任务计划–>添加任务计划”,在打开的界⾯中设置定时任务的时间、密码,通过选择,把cron.bat挂载进去。确定,这样⼀个定时任务就建⽴好了,在这个定时任务上右键,运⾏,这个定时任务就开始执⾏了,到点时,就会运⾏cron.bat处理,cron.bat再去执⾏php。
⾮⾃有服务器(虚拟主机)上实现php定时任务
如果站长没有⾃⼰的服务器,⽽是租⽤虚拟主机,就⽆法进⼊服务器系统进⾏上述操作。这个时候应该如何进⾏php定时任务呢?其实⽅法⼜有多个。
使⽤ignore_user_abort(true)和sleep死循环
在⼀个php⽂档的开头直接来⼀句:
ignore_user_abort(true);
这时,通过url访问这个php的时候,即使⽤户把浏览器关掉(断开连接),php也会在服务器上继续执⾏。利⽤这个特性,我们可以实现⾮常⽜的功能,也就是通过它来实现定时任务的激活,激活之后就随便它⾃⼰怎么办了,实际上就有点类似于后台任务。
⽽sleep(n)则是指当程序执⾏到这⾥时,暂时不往下执⾏,⽽是休息n秒钟。如果你访问这个php,就会发现页⾯起码要加载n秒钟。实际上,这种长时间等待的⾏为是⽐较消耗资源的,不能⼤量使⽤。
那么定时任务到底怎么实现呢?使⽤下⾯的代码即可实现:
ignore_user_abort(true);
set_time_limit(0);
date_default_timezone_set('PRC'); // 切换到中国的时间
$run_time = strtotime('+1 day'); // 定时任务第⼀次执⾏的时间是明天的这个时候
$interval = 3600*12; // 每12个⼩时执⾏⼀次
if(!file_exists(dirname(__FILE__).'/cron-run')) exit(); // 在⽬录下存放⼀个cron-run⽂件,如果这个⽂件不存在,说明已经在执⾏过程中了,该任务就不能再激活,执⾏第⼆次,否则这个⽂件被多次访问的话,服务器就要崩溃掉了
do {
if(!file_exists(dirname(__FILE__).'/cron-switch')) break; // 如果不存在cron-switch这个⽂件,就停⽌执⾏,这是⼀个开关的作⽤
$gmt_time = microtime(true); // 当前的运⾏时间,精确到0.0001秒
$loop = isset($loop) && $loop ? $loop : $run_time - $gmt_time; // 这⾥处理是为了确定还要等多久才开始第⼀次执⾏任务,$loop 就是要等多久才执⾏的时间间隔
$loop = $loop > 0 ? $loop : 0;
if(!$loop) break; // 如果循环的间隔为零,则停⽌
sleep($loop);
// ...
// 执⾏某些代码
// ...
@unlink(dirname(__FILE__).'/cron-run'); // 这⾥就是通过删除cron-run来告诉程序,这个定时任务已经在执⾏过程中,不能再执⾏⼀个新的同样的任务
$loop = $interval;
} while(true);
通过执⾏上⾯这段php代码,即可实现定时任务,直到你删除cron-switch⽂件,这个任务才会停⽌。
但是有⼀个问题,也就是如果⽤户直接访问这个php,实际上没有任何作⽤,页⾯也会停在这个地⽅,⼀直处于加载状态,有没有⼀种办法可以消除这种影响呢?fsockopen帮我们解决了这个问题。
fsockopen可以实现在请求访问某个⽂件时,不必获得返回结果就继续往下执⾏程序,这是和curl通常⽤法不⼀样的地⽅,我们在使⽤curl 访问⽹页时,⼀定要等curl加载完⽹页后,才会执⾏curl后⾯的代码,虽然实际上curl也可以实现“⾮阻塞式”的请求,但是⽐fsockopen 复杂的多,所以我们优先选择fsockopen,fsockopen可以在规定的时间内,⽐如1秒钟以内,完成对访问路径发出请求,完成之后就不管这个路径是否返回内容了,它的任务就到这⾥结束,可以继续往下执⾏程序了。利⽤这个特性,我们在正常的程序流中加⼊fsockopen,对上⾯我们创建的这个定时任务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('urdomain/script.php');
// ...
// 继续执⾏其他动作
// ..
把这段代码加⼊到某个定时任务提交结果程序中,在设置好时间后,提交,然后执⾏上⾯这个代码,就可以激活该定时任务,⽽且对于提交的这个⽤户⽽⾔,没有任何页⾯上的堵塞感。
借⽤⽤户的访问⾏为来执⾏某些延迟任务
但是上⾯使⽤sleep来实现定时任务,是效率很低的⼀种⽅案。我们希望不要使⽤这种⽅式来执⾏,这样的话就可以解决效率问题。我们借⽤⽤户访问⾏为来执⾏任务。⽤户对⽹站的访问其实是⼀个⾮常php编程手册
丰富的⾏为资源,包括搜索引擎蜘蛛对⽹站的访问,都可以算作这个类型。在⽤户访问⽹站时,内部加⼀个动作,去检查任务列表中是否存在没有被执⾏的任务,如果存在,就将这个任务执⾏。对于⽤户⽽⾔,利⽤上⾯所说的fsockopen,根本感觉不到⾃⼰的访问竟然还做出了这样的贡献。但是这种访问的缺点就是访问很不规律,⽐如你希望在凌晨2点执⾏某项任务,但是这个时间段⾮常倒霉,没有⽤户或任何⾏为到达你的⽹站,直到早上6点才有⼀个新访问。这就导致你原本打算2点执⾏的任务,到6点才被执⾏。
这⾥涉及到⼀个定时任务列表,也就是说你需要有⼀个列表来记录所有任务的时间、执⾏什么内容。⼀般来说,很多系统会采⽤数据库来记录这些任务列表,⽐如wordpress就是这样做的。我则利⽤⽂件读写特性,提供了托管在github上的开源项⽬php-cron,你可以去看看。总之,如果你想要管理多个定时任务,靠上⾯的单个php是⽆法合理布局的,必须想办法构建⼀个schedules列表。由于这⾥⾯的逻辑⽐较复杂,就不再详细阐述,我们仅停留在思路层⾯上。
借⽤第三⽅定时任务跳板
很好玩的是,⼀些服务商提供了各种类型的定时任务,例如阿⾥云的ACE提供了单独的定时任务,你可以填写⾃⼰应⽤下的某个uri。百度云BCE提供了服务器监测功能,每天会按照⼀定的时间规律访问应⽤下的固定uri。类似的第三⽅平台上还有很多定时任务可以⽤。你完全可以⽤这些第三⽅定时任务
作为跳板,为你的⽹站定时任务服务。⽐如说,你可以在阿⾥云ACE上建⽴⼀个每天凌晨2点的定时任务,执⾏的uri是/cron.php。然后你创建⼀个cron.php,⾥⾯则采⽤fsockopen去访问你真正要执⾏某些任务的⽹站的url,例如上⾯的
循环利⽤include包含⽂件(待验证)
php⾯向过程的特性使得其程序是从上往下执⾏的,利⽤这个特性,在我们使⽤include某个⽂件时,就会执⾏被引⼊的⽂件,知道include 的⽂件内程序执⾏完之后,再往下执⾏。如果我们创建⼀个循环,再利⽤sleep,不断的include某个⽂件,使循环执⾏某段程序,则可以达到定时执⾏的⽬的。我们再进⼀步,并不是利⽤while(true)来实现循环,⽽是利⽤被include⽂件本⾝再include⾃⾝来实现循环,⽐如我们创建⼀个do.php,它的内容如下:
if(...) exit(); // 通过某个开关来关闭执⾏
// ...
/
/ 执⾏某些程序
// ...
sleep($loop); // 这个$loop在include('do.php');之前赋值
include(dirname(__FILE__).'/do.php');
其实通过这种⽅法执⾏和while的思路也像。⽽且同样⽤到sleep,效率低。
PHP定时任务是⼀个⾮常有意思的东西,虽然说实话,⽤系统的去直接执⾏php⽂件的效率更⾼,但是对于很多普通站长⽽⾔,虚拟主机是⽆法做到直接php执⾏原⽣程序的。本⽂仅提供⼀些解决的思路,我也仅仅是在学习中,有很多问题或表述都不正确,希望你指出来;你可以通过本⽂的思路,开发出⾃⼰的⼀种解决⽅案,希望你能将⽅案发布,并与我⼀起探讨。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论