PHP實現(xiàn)執(zhí)行定時任務的幾種思路詳解
linux服務器上使用CronTab定時執(zhí)行php
我們先從相對比較復雜的服務器執(zhí)行php談起。服務器上安裝了php,就可以執(zhí)行php文件,無論是否安裝了nginx或Apache這樣的服務器環(huán)境軟件。而Linux中,使用命令行,用CronTab來定時任務,又是絕佳的選擇,而且也是效率最高的選擇。
首先,進入命令行模式。作為服務器的linux一般都默認進入命令行模式的,當然,我們管理服務器也一般通過putty等工具遠程連接到服務器,為了方便,我們用root用戶登錄。在命令行中鍵入:
crontab -e
之后就會打開一個文件,并且是非編輯狀態(tài),則是vi的編輯界面,通過敲鍵盤上的i,進入編輯模式,就可以編輯內(nèi)容。這個文件中的每一行就是一個定時任務,我們新建一行,就是新建一條定時任務(當然是指這一行內(nèi)按照一定的格式進行書寫)。我們現(xiàn)在來舉個例子,增加一行,內(nèi)容如下:
00 * * * * lynx -dump https://www.yourdomain.com/script.php
這是什么意思呢?實際上上面這一行由兩部分組成,前面一部分是時間,后面一部分是操作內(nèi)容。例如上面這個,
00 * * * *
就是指當當前時間的分鐘數(shù)為00時,執(zhí)行該定時任務。時間部分由5個時間參數(shù)組成,分別是:
分 時 日 月 周
第1列表示分鐘1~59 每分鐘用或者 */1表示,/n表示每n分鐘,例如*/8就是每8分鐘的意思,下面也是類推
第2列表示小時1~23(0表示0點)
第3列表示日期1~31
第4列表示月份1~12
第5列標識號星期0~6(0表示星期天)
整個句子的后面部分就是操作的具體內(nèi)容。
lynx -dump https://www.yourdomain.com/script.php
意思就是說通過lynx訪問這個url。我們在使用中主要用到lynx、curl、wget來實現(xiàn)對url的遠程訪問,而如果要提高效率,直接用php去執(zhí)行本地php文件是最佳選擇,例如:
00 */2 * * * /usr/local/bin/php /home/www/script.php
這條語句就可以在每2小時的0分鐘,通過linux內(nèi)部php環(huán)境執(zhí)行script.php,注意,這里可不是通過url訪問,通過服務器環(huán)境來執(zhí)行哦,而是直接執(zhí)行,因為繞過了服務器環(huán)境,所以效率當然要高很多。
好了,已經(jīng)添加了幾條需要的定時任務了吧。點擊鍵盤上的Esc鍵,輸入“:wq”回車,這樣就保存了設置的定時任務,屏幕上也能看到提示創(chuàng)建了新的定時任務。接下來就是好好寫你的script.php了。
關于CronTab的更多用法這里就不介紹了,如果你想更靈活的使用這個定時任務功能,應該自己再去深入學習一下crontab。
Windows服務器上使用bat定時執(zhí)行php
windows上和linux上有一個類似的cmd和bat文件,bat文件類似于shell文件,執(zhí)行這個bat文件,就相當于依次執(zhí)行里面的命令(當然,還可以通過邏輯來實現(xiàn)編程),所以,我們可以利用bat命令文件在windows服務器上面實現(xiàn)PHP定時任務。實際上在windows上定時任務,和linux上道理是一樣的,只不過方法和途徑不同。好了下面開始。
首先,在一個你覺得比較適當?shù)奈恢脛?chuàng)建一個cron.bat文件,然后用文本編輯器打開它(記事本都可以),在里面寫上這樣的內(nèi)容:
D:\php\php.exe -q D:\website\test.php
這句話的意思就是,使用php.exe去執(zhí)行test.php這個php文件,和上面的contab一樣,繞過了服務器環(huán)境,執(zhí)行效率也比較高。寫好之后,點擊保存,關閉編輯器。
接下來就是設置定時任務來運行cron.bat。依次打開:“開始–>控制面板–>任務計劃–>添加任務計劃”,在打開的界面中設置定時任務的時間、密碼,通過選擇,把cron.bat掛載進去。確定,這樣一個定時任務就建立好了,在這個定時任務上右鍵,運行,這個定時任務就開始執(zhí)行了,到點時,就會運行cron.bat處理,cron.bat再去執(zhí)行php。
非自有服務器(虛擬主機)上實現(xiàn)php定時任務
如果站長沒有自己的服務器,而是租用虛擬主機,就無法進入服務器系統(tǒng)進行上述操作。這個時候應該如何進行php定時任務呢?其實方法又有多個。
使用ignore_user_abort(true)和sleep死循環(huán)
在一個php文檔的開頭直接來一句:
ignore_user_abort(true);
這時,通過url訪問這個php的時候,即使用戶把瀏覽器關掉(斷開連接),php也會在服務器上繼續(xù)執(zhí)行。利用這個特性,我們可以實現(xiàn)非常牛的功能,也就是通過它來實現(xiàn)定時任務的激活,激活之后就隨便它自己怎么辦了,實際上就有點類似于后臺任務。
而sleep(n)則是指當程序執(zhí)行到這里時,暫時不往下執(zhí)行,而是休息n秒鐘。如果你訪問這個php,就會發(fā)現(xiàn)頁面起碼要加載n秒鐘。實際上,這種長時間等待的行為是比較消耗資源的,不能大量使用。
那么定時任務到底怎么實現(xiàn)呢?使用下面的代碼即可實現(xiàn):
0 ? $loop : 0; if(!$loop) break; // 如果循環(huán)的間隔為零,則停止 sleep($loop); // ... // 執(zhí)行某些代碼 // ... @unlink(dirname(__FILE__).'/cron-run'); // 這里就是通過刪除cron-run來告訴程序,這個定時任務已經(jīng)在執(zhí)行過程中,不能再執(zhí)行一個新的同樣的任務 $loop = $interval; } while(true);
通過執(zhí)行上面這段php代碼,即可實現(xiàn)定時任務,直到你刪除cron-switch文件,這個任務才會停止。
但是有一個問題,也就是如果用戶直接訪問這個php,實際上沒有任何作用,頁面也會停在這個地方,一直處于加載狀態(tài),有沒有一種辦法可以消除這種影響呢?fsockopen幫我們解決了這個問題。
fsockopen可以實現(xiàn)在請求訪問某個文件時,不必獲得返回結(jié)果就繼續(xù)往下執(zhí)行程序,這是和curl通常用法不一樣的地方,我們在使用curl訪問網(wǎng)頁時,一定要等curl加載完網(wǎng)頁后,才會執(zhí)行curl后面的代碼,雖然實際上curl也可以實現(xiàn)“非阻塞式”的請求,但是比fsockopen復雜的多,所以我們優(yōu)先選擇fsockopen,fsockopen可以在規(guī)定的時間內(nèi),比如1秒鐘以內(nèi),完成對訪問路徑發(fā)出請求,完成之后就不管這個路徑是否返回內(nèi)容了,它的任務就到這里結(jié)束,可以繼續(xù)往下執(zhí)行程序了。利用這個特性,我們在正常的程序流中加入fsockopen,對上面我們創(chuàng)建的這個定時任務php的地址發(fā)出請求,即可讓定時任務在后臺執(zhí)行。如果上面這個php的url地址是www.yourdomain.com/script.php,那么我們在編程中,可以這樣:
// ... // 正常的php執(zhí)行程序 // .. // 遠程請求(不獲取內(nèi)容)函數(shù),下面可以反復使用 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服務器上就無法執(zhí)行成功 fclose($fp); return array('error_code' => 0); } } _sock('www.yourdomain.com/script.php'); // ... // 繼續(xù)執(zhí)行其他動作 // ..
把這段代碼加入到某個定時任務提交結(jié)果程序中,在設置好時間后,提交,然后執(zhí)行上面這個代碼,就可以激活該定時任務,而且對于提交的這個用戶而言,沒有任何頁面上的堵塞感。
借用用戶的訪問行為來執(zhí)行某些延遲任務
但是上面使用sleep來實現(xiàn)定時任務,是效率很低的一種方案。我們希望不要使用這種方式來執(zhí)行,這樣的話就可以解決效率問題。我們借用用戶訪問行為來執(zhí)行任務。用戶對網(wǎng)站的訪問其實是一個非常豐富的行為資源,包括搜索引擎蜘蛛對網(wǎng)站的訪問,都可以算作這個類型。在用戶訪問網(wǎng)站時,內(nèi)部加一個動作,去檢查任務列表中是否存在沒有被執(zhí)行的任務,如果存在,就將這個任務執(zhí)行。對于用戶而言,利用上面所說的fsockopen,根本感覺不到自己的訪問竟然還做出了這樣的貢獻。但是這種訪問的缺點就是訪問很不規(guī)律,比如你希望在凌晨2點執(zhí)行某項任務,但是這個時間段非常倒霉,沒有用戶或任何行為到達你的網(wǎng)站,直到早上6點才有一個新訪問。這就導致你原本打算2點執(zhí)行的任務,到6點才被執(zhí)行。
這里涉及到一個定時任務列表,也就是說你需要有一個列表來記錄所有任務的時間、執(zhí)行什么內(nèi)容。一般來說,很多系統(tǒng)會采用數(shù)據(jù)庫來記錄這些任務列表,比如wordpress就是這樣做的。我則利用文件讀寫特性,提供了托管在github上的開源項目php-cron,你可以去看看。總之,如果你想要管理多個定時任務,靠上面的單個php是無法合理布局的,必須想辦法構(gòu)建一個schedules列表。由于這里面的邏輯比較復雜,就不再詳細闡述,我們僅停留在思路層面上。
借用第三方定時任務跳板
很好玩的是,一些服務商提供了各種類型的定時任務,例如阿里云的ACE提供了單獨的定時任務,你可以填寫自己應用下的某個uri。百度云BCE提供了服務器監(jiān)測功能,每天會按照一定的時間規(guī)律訪問應用下的固定uri。類似的第三方平臺上還有很多定時任務可以用。你完全可以用這些第三方定時任務作為跳板,為你的網(wǎng)站定時任務服務。比如說,你可以在阿里云ACE上建立一個每天凌晨2點的定時任務,執(zhí)行的uri是/cron.php。然后你創(chuàng)建一個cron.php,里面則采用fsockopen去訪問你真正要執(zhí)行某些任務的網(wǎng)站的url,例如上面的www.yourdomain.com/script.php,而且在cron.php中還可以訪問多個url。然后把cron.php上傳到你的ACE上面去,讓ACE的定時任務去訪問/cron.php,然后讓cron.php去遠程請求目標網(wǎng)站的定時任務腳本。
循環(huán)利用include包含文件(待驗證)
php面向過程的特性使得其程序是從上往下執(zhí)行的,利用這個特性,在我們使用include某個文件時,就會執(zhí)行被引入的文件,知道include的文件內(nèi)程序執(zhí)行完之后,再往下執(zhí)行。如果我們創(chuàng)建一個循環(huán),再利用sleep,不斷的include某個文件,使循環(huán)執(zhí)行某段程序,則可以達到定時執(zhí)行的目的。我們再進一步,并不是利用while(true)來實現(xiàn)循環(huán),而是利用被include文件本身再include自身來實現(xiàn)循環(huán),比如我們創(chuàng)建一個do.php,它的內(nèi)容如下:
if(...) exit(); // 通過某個開關來關閉執(zhí)行 // ... // 執(zhí)行某些程序 // ... sleep($loop); // 這個$loop在include('do.php');之前賦值 include(dirname(__FILE__).'/do.php');
其實通過這種方法執(zhí)行和while的思路也像。而且同樣用到sleep,效率低。
PHP 網(wǎng)站
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔相應法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔相應法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。