PHP在Linux下執行定時任務的實現思路詳解

2020-12-14 代碼之家

PHP本身是沒有定時功能的,PHP也不能多線程。

PHP的定時任務功能必須通過和其他工具結合才能實現,例如WordPress內置了wp-cron的功能,很厲害。

一、Linux伺服器上使用CronTab定時執行php

我們先從相對比較複雜的伺服器執行php談起。

伺服器上安裝了php,就可以執行php文件,無論是否安裝了nginx或Apache這樣的伺服器環境軟體。

而Linux中,使用命令行,用CronTab來定時任務,又是絕佳的選擇,而且也是效率最高的選擇。

首先,進入命令行模式。

作為伺服器的linux一般都默認進入命令行模式的,當然,我們管理伺服器也一般通過putty等工具遠程連接到伺服器,為了方便,我們用root用戶登錄。

在命令行中鍵入:

crontab -e

之後就會打開一個文件,並且是非編輯狀態,則是vi的編輯界面,通過敲鍵盤上的i,進入編輯模式,就可以編輯內容。

這個文件中的每一行就是一個定時任務,我們新建一行,就是新建一條定時任務(當然是指這一行內按照一定的格式進行書寫)。

我們現在來舉個例子,增加一行,內容如下:

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

這是什麼意思呢?實際上上面這一行由兩部分組成,前面一部分是時間,後面一部分是操作內容。

例如上面這個:

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.codezhijia.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環境執行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:phpphp.exe -q D:websitetest.php這句話的意思就是,使用php.exe去執行test.php這個php文件,和上面的contab一樣,繞過了伺服器環境,執行效率也比較高。

寫好之後,點擊保存,關閉編輯器。

接下來就是設置定時任務來運行cron.bat。

依次打開:「開始–>控制面板–>任務計劃–>添加任務計劃」,在打開的界面中設置定時任務的時間、密碼,通過選擇,把cron.bat掛載進去。

確定,這樣一個定時任務就建立好了,在這個定時任務上右鍵,運行,這個定時任務就開始執行了,到點時,就會運行cron.bat處理,cron.bat再去執行php。

三、非自有伺服器(虛擬主機)上實現php定時任務

如果站長沒有自己的伺服器,而是租用虛擬主機,就無法進入伺服器系統進行上述操作。

這個時候應該如何進行php定時任務呢?其實方法又有多個。

1、使用ignore_user_abort(true)和sleep死循環

在一個php文檔的開頭直接來一句:

ignore_user_abort(true);

這時,通過url訪問這個php的時候,即使用戶把瀏覽器關掉(斷開連接),php也會在伺服器上繼續執行。

利用這個特性,我們可以實現非常牛的功能,也就是通過它來實現定時任務的激活,激活之後就隨便它自己怎麼辦了,實際上就有點類似於後臺任務。

而sleep(n)則是指當程序執行到這裡時,暫時不往下執行,而是休息n秒鐘。

如果你訪問這個php,就會發現頁面起碼要加載n秒鐘。實際上,這種長時間等待的行為是比較消耗資源的,不能大量使用。

那麼定時任務到底怎麼實現呢?使用下面的代碼即可實現:

<?phpignore_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地址是www.codezhijia.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 .= '?'.$query; if($scheme == 'https') { $host = 'ssl://'.$host; } $fp = fsockopen($host,$port,$error_code,$error_msg,1); if(!$fp) { returnarray('error_code' => $error_code,'error_msg' => $error_msg); } else { stream_set_blocking($fp,true);//開啟了手冊上說的非阻塞模式 stream_set_timeout($fp,1);//設置超時$header = "GET $path HTTP/1.1rn"; $header.="Host: $hostrn"; $header.="Connection: closernrn";//長連接關閉 fwrite($fp, $header); usleep(1000); // 這一句也是關鍵,如果沒有這延時,可能在nginx伺服器上就無法執行成功 fclose($fp); returnarray('error_code' => 0); } } _sock('www.codezhijia.com/script.php'); // ...// 繼續執行其他動作// ..

把這段代碼加入到某個定時任務提交結果程序中,在設置好時間後,提交,然後執行上面這個代碼,就可以激活該定時任務,而且對於提交的這個用戶而言,沒有任何頁面上的堵塞感。

2、借用用戶的訪問行為來執行某些延遲任務

但是上面使用sleep來實現定時任務,是效率很低的一種方案。

我們希望不要使用這種方式來執行,這樣的話就可以解決效率問題。

我們借用用戶訪問行為來執行任務。用戶對網站的訪問其實是一個非常豐富的行為資源,包括搜尋引擎蜘蛛對網站的訪問,都可以算作這個類型。

在用戶訪問網站時,內部加一個動作,去檢查任務列表中是否存在沒有被執行的任務,如果存在,就將這個任務執行。

對於用戶而言,利用上面所說的fsockopen,根本感覺不到自己的訪問竟然還做出了這樣的貢獻。

但是這種訪問的缺點就是訪問很不規律,比如你希望在凌晨2點執行某項任務,但是這個時間段非常倒黴,沒有用戶或任何行為到達你的網站,直到早上6點才有一個新訪問。

這就導致你原本打算2點執行的任務,到6點才被執行。

這裡涉及到一個定時任務列表,也就是說你需要有一個列表來記錄所有任務的時間、執行什麼內容。

一般來說,很多系統會採用資料庫來記錄這些任務列表,比如wordpress就是這樣做的。

我則利用文件讀寫特性,提供了託管在github上的開源項目php-cron,你可以去看看。

總之,如果你想要管理多個定時任務,靠上面的單個php是無法合理布局的,必須想辦法構建一個schedules列表。

由於這裡面的邏輯比較複雜,就不再詳細闡述,我們僅停留在思路層面上。

3、借用第三方定時任務跳板

很好玩的是,一些服務商提供了各種類型的定時任務,例如阿里雲的ACE提供了單獨的定時任務,你可以填寫自己應用下的某個uri。

百度雲BCE提供了伺服器監測功能,每天會按照一定的時間規律訪問應用下的固定uri。

類似的第三方平臺上還有很多定時任務可以用。你完全可以用這些第三方定時任務作為跳板,為你的網站定時任務服務。

比如說,你可以在阿里雲ACE上建立一個每天凌晨2點的定時任務,執行的uri是/cron.php。

然後你創建一個cron.php,裡面則採用fsockopen去訪問你真正要執行某些任務的網站的url,例如上面的www.codezhijia.com/script.php,而且在cron.php中還可以訪問多個url。

然後把cron.php上傳到你的ACE上面去,讓ACE的定時任務去訪問/cron.php,然後讓cron.php去遠程請求目標網站的定時任務腳本。

4、循環利用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.exe去直接執行php文件的效率更高,但是對於很多普通站長而言,虛擬主機是無法做到直接php執行原生程序的。

本文僅提供一些解決的思路,我也僅僅是在學習中,有很多問題或表述都不正確,希望你指出來;你可以通過本文的思路,開發出自己的一種解決方案。

感興趣的小夥伴請關注我,一起努力,一起學習!

相關焦點

  • PHP實現執行定時任務的幾種思路詳解
    本文,我們就來深入的解析幾種常見的php定時任務的思路。Linux伺服器上使用CronTab定時執行php我們先從相對比較複雜的伺服器執行php談起。伺服器上安裝了php,就可以執行php文件,無論是否安裝了nginx或Apache這樣的伺服器環境軟體。而Linux中,使用命令行,用CronTab來定時任務,又是絕佳的選擇,而且也是效率最高的選擇。
  • php Swoole實現毫秒級定時任務
    項目開發中,如果有定時任務的業務要求,我們會使用linux的crontab來解決,但是它的最小粒度是分鐘級別,如果要求粒度是秒級別的,甚至毫秒級別的,crontab就無法滿足,值得慶幸的是swoole提供的強大的毫秒定時器。應用場景舉例我們可能會遇到這樣的場景:以上的三個場景我們都可以歸納為定時任務的範疇。
  • 為什麼你的 Spring Task 定時任務沒有定時執行?
    前言定時任務的使用,在開發中可謂是家常便飯了,定時發送郵件、簡訊。 避免資料庫,數據表過大,定時將數據轉儲。通知、對帳等等。當然實現定時任務的方式也有很多,比如使用 linux 下的 contab 腳本,jdk 中自帶的 Timer 類。
  • 定時任務實現原理詳解
    一、摘要在很多業務的系統中,我們常常需要定時的執行一些任務,例如定時發簡訊、定時變更數據、定時發起促銷活動等等。在上篇文章中,我們簡單的介紹了定時任務的使用方式,不同的架構對應的解決方案也有所不同,總結起來主要分單機和分布式兩大類,本文會重點分析下單機的定時任務實現原理以及優缺點,分布式框架的實現原理會在後續文章中進行分析。
  • Asp定時執行操作、Asp定時讀取資料庫(網頁定時操作詳解)
    Asp定時執行操作、Asp定時讀取資料庫(網頁定時操作詳解) '----------------------版權資訊----------------------------'-----------Author:Vbell----------------------------------
  • c定時 linux專題及常見問題 - CSDN
    1.定時任務介紹1.1 crond是什麼crond是linux系統中用來定期執行命令或指定程序的一種服務或軟體。特殊要求:(秒級別)crond服務就無法搞定了,一般工作中寫腳本用守護進程執行[root@shellbiancheng jiaobenlianxi]# cat while1.sh #!
  • Python實現定時執行任務的三種方式簡單示例
    本文實例講述了Python實現定時執行任務的三種方式。分享給大家供大家參考,具體如下: 1.定時任務代碼 2.周期性執行任務
  • 學會這 10 種定時任務,我有點飄了
    前言在不用的業務場景下要用不同的定時任務,其實我們的選擇還是挺多的。本文總結了 10 種非常實用的定時任務,總有一種是適合你的。一. linux 自帶的定時任務crontab不知道你有沒有遇到過這種場景:有時需要臨時統計線上的數據,然後導出到 excel 表格中。
  • Linux下快速搭建php開發環境
    關注我喲小編 隔天推送php教程,php技巧,php視頻教程,MySQL,筆試題等諸多優質內容,最接地氣、重服務的本地微信平臺!關注我們妥妥沒錯!更改安裝程序的安裝權限,輸入命令:chmod 755 xampp-linux-*-installer.run  (註:星號代表你下載XAMPP的版本號)。運行安裝程序輸入命令:./xampp-linux-*-installer.run    安裝成功後用命令:sudo /opt/lampp/lampp start   啟動。
  • 我有 10 種方法搞定定時任務,10種!
    前言最近有幾個讀者私信給我,問我他們的業務場景,要用什麼樣的定時任務。確實,在不用的業務場景下要用不同的定時任務,其實我們的選擇還是挺多的。我今天給大家總結10種非常實用的定時任務,總有一種是適合你的。
  • CentOS「linux」學習筆記11:crontab定時任務的常用參數和基本語法介紹
    linux基礎操作:主要介紹了crontab定時任務的常用參數和基本語法crontab[定時任務,重複執行特定的命令或腳本,類似Windows下的計劃任務]chrontab -e 編輯模式,chrontab -l 查詢任務,chrontab -r 刪除所有任務service crond restart 重啟任務  定時任務基本語法:* * * * * 命令或腳本  第一位 * 表示分鐘取值範圍:0-59  第二位 * 表示小時取值範圍:0-23  第三位 * 表示日期取值範圍:1-31  第四位 * 表示月份取值範圍:1-12  第五位 * 表示星期幾取值範圍
  • Linux入侵排查思路
    相比較開機啟動項,定時任務要更重要點,畢竟伺服器一般都是時刻運行的crontab -l 列出當前用戶cron服務的詳細內容默認編寫的crontab文件會保存在(/var/spool/cron/用戶名)下一般情況下是排查一下目錄是否存在惡意腳本/var/spool/cron/*/etc/crontab/etc
  • 【安卓按鍵精靈】定時執行指定任務
    說到「定時」有這麼幾種常見的形式:(1)每天固定時間執行,就像手機鬧鐘一樣,到時間就會執行操作。
  • Linux系統利用可執行文件的Capability實現權限提升
    capabilities實現權限提升。這樣一來,權限檢查的過程就變成了:在執行特權操作時,如果線程的有效身份不是root,就去檢查其是否具有該特權操作所對應的capabilities,並以此為依據,決定是否可以執行特權操作。
  • Swoole 在 php框架(Laravel)中是如何實現異步任務隊列的
    Swoole 異步任務實現原理我們知道,PHP 本身的設計是同步阻塞的,不支持多線程和異步 IO,所以當我們執行一些耗時的操作,比如發送廣播,或者郵件,如果直接在當前進程中操作,會導致伺服器響應變慢,因此要藉助一些第三方服務來處理以實現異步功能,比如隊列,而 Swoole
  • Python 實現定時任務的八種方案!
    在日常工作中,我們常常會用到需要周期性執行的任務,一種方式是採用 Linux 系統自帶的 crond 結合命令行實現。另外一種方式是直接使用Python。接下來整理的是常見的Python定時任務的實現方式。
  • 教你學會在Linux上運行Crontab定時任務
    linux內置的cron進程能幫我們實現這些需求,cron搭配shell腳本,非常複雜的指令也沒有問題。
  • Spring 定時任務玩出花!
    整體思路我先來說說這個項目的整體思路,這樣方便大家理解下面的內容。extends Object> getType(String name) {    }}3.2 SchedulingRunnable將來每一個定時任務執行的時候,我們都開啟一個新的線程去執行這個定時任務,SchedulingRunnable 就是關於這個線程的配置,我們來看下:
  • 改善Linux內核實時性方法的研究與實現
    另一個影響Linux實時性的因素就是關中斷,同步操作中使用的關中斷指令增大了中斷延遲,這樣很多由中斷驅動的實時任務得不到及時的執行,系統的實時性能得不到保障。因此提高Linux的可搶佔性,改進其中斷機制有利於改善內核的實時性。  本文詳細闡述了這些措施的原理並基於標準Linux2.6內核加以實現,最後通過測試,驗證了此改進方法的效果。
  • Linux下33個常用的Find實例
    在當前目錄查找文件使用的名字在當前目錄下,查找所有名稱為linuxprobe.txt的文件# find . -name linuxprobe.txt./linuxprobe.txt2.在home目錄下查找文件查找home目錄下所有文件名為linuxprobe.txt的文件# find /home -name linuxprobe.txt/home/linuxprobe.txt3.