ホームページ  >  記事  >  バックエンド開発  >  PHP でスケジュールされたタスクを実行するためのいくつかの方法とアイデア

PHP でスケジュールされたタスクを実行するためのいくつかの方法とアイデア

小云云
小云云オリジナル
2018-03-14 14:07:183845ブラウズ

PHP自体にはタイミング機能がなく、PHPはマルチスレッド化できません。 PHP のスケジュールされたタスク機能を実現するには、他のツールと組み合わせる必要があります。たとえば、WordPress には非常に強力な wp-cron 機能が組み込まれています。この記事では、いくつかの一般的な PHP スケジュール済みタスクのアイデアを詳しく分析します。

1. CronTab を使用して、Linux サーバー上で PHP を定期的に実行します

比較的複雑な PHP のサーバー実行から始めましょう。 PHP をサーバーにインストールすると、nginx や Apache などのサーバー環境ソフトウェアがインストールされているかどうかに関係なく、PHP ファイルを実行できます。 Linux では、コマンド ラインと CronTab を使用してタスクをスケジュールするのが優れた選択肢であり、最も効率的な選択肢でもあります。

まず、コマンドラインモードに入ります。サーバーとして、Linux は通常、デフォルトでコマンド ライン モードに入ります。もちろん、管理サーバーも通常、便宜上、root ユーザーとしてログインします。コマンドラインで

crontab -e

と入力すると、ファイルが開かれ、非編集状態になります。キーボードの i を押して編集モードに入ることができます。コンテンツ。このファイルの各行はスケジュールされたタスクです。新しい行を作成すると、新しいスケジュールされたタスクが作成されます (もちろん、この行に特定の形式で記述することを意味します)。例として、次の内容の行を追加してみましょう:

00 * * * * lynx -dump https://www.yourdomain.com/script.php

これはどういう意味ですか?実は上記の行は2つの部分から構成されており、前半部分が時間、後半部分が操作内容となります。たとえば、上記の

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 -dump https://www.yourdomain.com/script.php

これは、lynx を通じてこの URL にアクセスすることを意味します。 URL へのリモート アクセスを実現するには、主に lynx、curl、および wget を使用します。効率を向上させたい場合は、PHP を直接使用してローカル PHP ファイルを実行するのが最良の選択です。例:

00 */2 * * * /usr/local/bin/php /home/www/script.php

このステートメントは Every 2 で使用できます。時と 0 分では、script.php は Linux 内部の PHP 環境を通じて実行されます。これは URL 経由でアクセスされず、サーバー環境を通じて直接実行されるため、当然効率が高くなります。 。 たくさんの。

はい、必要なスケジュールされたタスクがいくつか追加されました。キーボードの Esc キーをクリックし、「:wq」と入力して Enter を押すと、設定したスケジュールされたタスクが保存され、新しいスケジュールされたタスクが作成されたことを示すプロンプトが画面に表示されます。次のステップは、script.php を適切に記述することです。

ここでは CronTab の詳しい使い方は紹介しません。このスケジュールされたタスク機能をより柔軟に使用したい場合は、自分で crontab について詳しく学ぶ必要があります。

2. Windowsサーバーでphpを定期的に実行するにはbatを使用します

WindowsとLinuxには同様のcmdファイルとbatファイルがあり、このbatファイルを実行することは、内部のコマンドを実行することと同じです。シーケンス (もちろん、プログラミングはロジックを通じて実装することもできます) なので、bat コマンド ファイルを使用して、Windows サーバー上で PHP スケジュールされたタスクを実装できます。実際、Windows でのスケジュールされたタスクの原理は Linux のそれと同じですが、方法とアプローチは異なります。さて、始めましょう。

まず、より適切だと思われる場所に cron.bat ファイルを作成し、それをテキスト エディター (メモ帳でも可) で開き、次の内容を書き込みます:

D:\php\php.exe -q D:\website\test.php

この文は、php を使用することを意味します。 .exe を使用して php ファイル test.php を実行します。上記の contab と同様に、サーバー環境をバイパスし、実行効率が比較的高くなります。書き込んだ後、「保存」をクリックしてエディタを閉じます。

次のステップは、cron.bat を実行するスケジュールされたタスクを設定することです。 「スタート」→「コントロールパネル」→「タスクスケジュール」→「タスクスケジュールの追加」の順に開き、開いたインターフェースでスケジュールされたタスクの時刻とパスワードを設定し、cron.batを選択してマウントします。 OK、このようなスケジュールされたタスクが作成されました。スケジュールされたタスクを右クリックして実行すると、時間切れになると cron.bat が実行され、cron.bat が実行されます。 phpを実行します。

3. 所有されていないサーバー (仮想ホスト) に PHP スケジュールされたタスクを実装する

ウェブマスターが自分のサーバーを持たず、仮想ホストを借りている場合、サーバー システムに入って上記の操作を実行することはできません。現時点では、PHP でスケジュールされたタスクをどのように実行すればよいでしょうか?実際には、複数の方法があります。

4.ignore_user_abort(true) を使用して無限ループをスリープします

PHP ドキュメントの先頭に次の文を直接言います:

ignore_user_abort(true);

このとき、ユーザーがブラウザを閉じても、URL 経由でこの PHP にアクセスします (接続を切断します) )、php はサーバー上で実行を続けます。この機能を使用すると、非常に強力な機能を実現できます。つまり、スケジュールされたタスクをアクティブ化した後、バックグラウンド タスクに似たものを実行できます。

そして sleep(n) は、ここでプログラムが実行されるとき、一時的に実行を続行せず、n 秒間休止することを意味します。この php にアクセスすると、ページの読み込みに少なくとも n 秒かかることがわかります。実際、この長期待機動作はリソースを消費するため、大量に使用することはできません。

那么定时任务到底怎么实现呢?使用下面的代码即可实现:

<?php

ignore_user_abort(true);
set_time_limit(0);
date_default_timezone_set(&#39;PRC&#39;); // 切换到中国的时间$run_time = strtotime(&#39;+1 day&#39;); // 定时任务第一次执行的时间是明天的这个时候$interval = 3600*12; // 每12个小时执行一次if(!file_exists(dirname(__FILE__).&#39;/cron-run&#39;)) exit(); // 在目录下存放一个cron-run文件,如果这个文件不存在,说明已经在执行过程中了,该任务就不能再激活,执行第二次,否则这个文件被多次访问的话,服务器就要崩溃掉了do {  if(!file_exists(dirname(__FILE__).&#39;/cron-switch&#39;)) 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__).&#39;/cron-run&#39;); // 这里就是通过删除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地址是www.yourdomain.com/script.php,那么我们在编程中,可以这样:

// ...// 正常的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 .= &#39;?&#39;.$query;  if($scheme == &#39;https&#39;) {
    $host = &#39;ssl://&#39;.$host;
  }

  $fp = fsockopen($host,$port,$error_code,$error_msg,1);  if(!$fp) {    return array(&#39;error_code&#39; => $error_code,&#39;error_msg&#39; => $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(&#39;error_code&#39; => 0);
  }
}

_sock(&#39;www.yourdomain.com/script.php&#39;);// ...// 继续执行其他动作// ..

把这段代码加入到某个定时任务提交结果程序中,在设置好时间后,提交,然后执行上面这个代码,就可以激活该定时任务,而且对于提交的这个用户而言,没有任何页面上的堵塞感。

5、借用用户的访问行为来执行某些延迟任务

但是上面使用sleep来实现定时任务,是效率很低的一种方案。我们希望不要使用这种方式来执行,这样的话就可以解决效率问题。我们借用用户访问行为来执行任务。用户对网站的访问其实是一个非常丰富的行为资源,包括搜索引擎蜘蛛对网站的访问,都可以算作这个类型。在用户访问网站时,内部加一个动作,去检查任务列表中是否存在没有被执行的任务,如果存在,就将这个任务执行。对于用户而言,利用上面所说的fsockopen,根本感觉不到自己的访问竟然还做出了这样的贡献。但是这种访问的缺点就是访问很不规律,比如你希望在凌晨2点执行某项任务,但是这个时间段非常倒霉,没有用户或任何行为到达你的网站,直到早上6点才有一个新访问。这就导致你原本打算2点执行的任务,到6点才被执行。

这里涉及到一个定时任务列表,也就是说你需要有一个列表来记录所有任务的时间、执行什么内容。一般来说,很多系统会采用数据库来记录这些任务列表,比如wordpress就是这样做的。我则利用文件读写特性,提供了托管在github上的开源项目php-cron,你可以去看看。总之,如果你想要管理多个定时任务,靠上面的单个php是无法合理布局的,必须想办法构建一个schedules列表。由于这里面的逻辑比较复杂,就不再详细阐述,我们仅停留在思路层面上。

6、借用第三方定时任务跳板

很好玩的是,一些服务商提供了各种类型的定时任务,例如阿里云的ACE提供了单独的定时任务,你可以填写自己应用下的某个uri。百度云BCE提供了服务器监测功能,每天会按照一定的时间规律访问应用下的固定uri。类似的第三方平台上还有很多定时任务可以用。你完全可以用这些第三方定时任务作为跳板,为你的网站定时任务服务。比如说,你可以在阿里云ACE上建立一个每天凌晨2点的定时任务,执行的uri是/cron.php。然后你创建一个cron.php,里面则采用fsockopen去访问你真正要执行某些任务的网站的url,例如上面的www.yourdomain.com/script.php,而且在cron.php中还可以访问多个url。然后把cron.php上传到你的ACE上面去,让ACE的定时任务去访问/cron.php,然后让cron.php去远程请求目标网站的定时任务脚本。

7、循环利用include包含文件(待验证)

php面向过程的特性使得其程序是从上往下执行的,利用这个特性,在我们使用include某个文件时,就会执行被引入的文件,知道include的文件内程序执行完之后,再往下执行。如果我们创建一个循环,再利用sleep,不断的include某个文件,使循环执行某段程序,则可以达到定时执行的目的。我们再进一步,并不是利用while(true)来实现循环,而是利用被include文件本身再include自身来实现循环,比如我们创建一个do.php,它的内容如下:

if(...) exit(); // 通过某个开关来关闭执行// ... 
// 执行某些程序// ...

sleep($loop); // 这个$loop在include(&#39;do.php&#39;);之前赋值

include(dirname(__FILE__).&#39;/do.php&#39;);

其实通过这种方法执行和while的思路也像。而且同样用到sleep,效率低。

PHP スケジュールされたタスクは非常に興味深いものですが、正直に言うと、システムの php.exe を使用して PHP ファイルを直接実行する方が効率的ですが、多くの一般的な Web マスターにとって、仮想ホストはネイティブ PHP ファイルを直接実行できません。この記事では解決策のアイデアをいくつか紹介しているだけです。多くの質問や間違った表現がありますので、この記事のアイデアを参考にして解決策を開発していただければ幸いです。それについて私と話し合ってください。

関連する推奨事項:

スケジュールされたタスクの PHP 実装の詳細な説明

スケジュールされたタスクの PHP 実装の詳細な説明_PHP

スケジュールされたタスクの PHP 実装の詳細な説明_php スキル

以上がPHP でスケジュールされたタスクを実行するためのいくつかの方法とアイデアの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。