PHP異步調用、多線程、計劃任務

PHP編程也能實現這些看起來很“高級”的編程任務。
異步調用一般用來執行耗時較長的操作,讓程序在服務器後臺執行,前臺用戶無需等待。參考這篇文章:PHP異步調用避免程序運行超時
案例:PHP語言
某SNS社區,在系統裏,用戶給自己的好友(好友數量上百)發送郵件,每封郵件內容不一,發送後提示發送完畢!
常用PHP寫法
sendmail.php
<?php
$count=count($emailarr);//$emailarr數組爲好友的郵件地址
for($i=0;$i<$count;$i++)
{
sendmail(.....);//發送郵件
}
echo ''發送完畢';
?>
假設該次發送100封郵件。本次操作會出現什麼結果呢?

用戶體驗:用戶等待->發送數十封郵件出去->系統超時返回錯誤信息

本次操作由於需要發送大量的郵件,導致php執行時間過長,用戶煩躁的等待。當apache或者nginx等待超過允許執行時間,返回超時錯誤。這個時候用戶不明確本次操作到底成功與否,到底發出了幾封郵件。
我們可以看出該代碼用戶體驗極差,並且不能夠順利完成任務。

那應該怎麼操作呢?
這裏提到一個概念,異步執行
用戶體驗:用戶等待->發送完畢
朋友們就會問,怎麼缺少發信環節?
OK,發信環節就在用戶提交請求的時候,把發信任務轉給了一個單獨處理髮信的php程序處理了,當用戶看見“發送完畢”的時候其實信還沒發送完,這個時候,發信程序正在後臺努力的工作着,一封一封的向外發送

sendmail.php
<?php
$domain="www.***.com";
$url="/system_mail.php";
$par="email=".implode(',',$emailarr)."&........";
$header = "POST $url HTTP/1.0\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($par) . "\r\n\r\n";
$fp = @fsockopen ($domain, 80, $errno, $errstr, 30);
fputs ($fp, $header . $par);
fclose($fp);

echo ''發送完畢';
?>
system_mail.php
<?php
ini_set("ignore_user_abort",true);
ignore_user_abort(true);//此處的代碼需要php.ini開啓相關的選項,保證php執行不超時的,不明白,參考我的另一篇文章 “關閉瀏覽器後,php腳本會不會繼續運行”
//獲取email地址,發信,此處爲發信代碼
?>

好了,改成異步方式後,用戶提交信息,可以立即得到結果“發送完畢”。信呢,會在後臺一封一封的發送,直到發送完畢。

前幾天用ASP.NET實現了計劃任務功能,心裏想,PHP或許也能實現,搜索了一下,確實也能實現。參考這篇文章:PHP計劃任務的實現

php計劃任務的實現 (zt)
文章分類:PHP編程 
<?php 
ignore_user_abort(); //即使Client斷開(如關掉瀏覽器),PHP腳本也可以繼續執行. 
set_time_limit(0); // 執行時間爲無限制,php默認的執行時間是30秒,通過set_time_limit(0)可以讓程序無限制的執行下去 
$interval=60*5; // 每隔5分鐘運行 
do{ 
$fp = fopen('test.txt','a'); 
fwrite($fp,'test'); 
fclose($fp); 
sleep($interval); // 等待5分鐘 
}while(true); 
?>


php定時計劃任務介紹2010-05-06 10:10以前對se特別感興趣,但是自己又不會java,lucene等搜索引擎開發工具,於是不斷挖掘php的功效。

最後發現php也可以做抓取,並且原理很易:直接獲取頁面源文件,然後通過正則或字符串的參照截取來獲取需要的信息。但是性能上不能和搜索引擎的多線程抓取相比。

實現了上一步之後,又思考着,如果抓取可以自動定時獲取,那麼人工運行可執行頁面也就省下來了。

後來也在一些php開源程序中瞭解到關於"計劃任務"的效果:可以定時運行某程序,比如數據庫備份,更新緩存,生成靜態頁面,生成網站地圖等。

最近由於項目需要定時更新遠程數據庫到本地,網上搜了搜,還真找到了。

ignore_user_abort();函數搭配set_time_limit(0);和sleep($interval);即可實現以上自動更新。

先給出一個基本的範式,其中有個人的測試程序:

<?php
ignore_user_abort(); // run script in background
set_time_limit(0); // run script forever
$interval=30; // do every 15 minutes...
do{
$fp = fopen('text3.txt','a');
fwrite($fp,'test');
fclose($fp);
sleep($interval); // wait 15 minutes
}while(true);
?>

首先運行該程序,然後關閉該頁面,程序仍然運行中,test會每隔30秒的填補到text3.txt文件。

實現效果如圖:(略)

最後根據php手冊簡單介紹一些相關的知識:

1.連接處理:

在 PHP 內部,系統維護着連接狀態,其狀態有三種可能的情況:

0 - NORMAL(正常)
1 - ABORTED(異常退出)
2 - TIMEOUT(超時)

當 PHP 腳本正常地運行 NORMAL 狀態時,連接爲有效。當遠程客戶端中斷連接時,ABORTED 狀態的標記將會被打開。遠程客戶端連接的中斷通常是由用戶點擊 STOP 按鈕導致的。當連接時間超過 PHP 的時限時,TIMEOUT 狀態的標記將被打開。

可以決定腳本是否需要在客戶端中斷連接時退出。有時候讓腳本完整地運行會帶來很多方便,即使沒有遠程瀏覽器接受腳本的輸出。默認的情況是當遠程客戶端連接中斷時腳本將會退出。該處理過程可由 php.ini 的 ignore_user_abort 或由 Apache .conf 設置中對應的"php_value ignore_user_abort"以及 ignore_user_abort() 函數來控制。如果沒有告訴 PHP 忽略用戶的中斷,腳本將會被中斷,除非通過 register_shutdown_function() 設置了關閉觸發函數。通過該關閉觸發函數,當遠程用戶點擊 STOP 按鈕後,腳本再次嘗試輸出數據時,PHP 將會檢測到連接已被中斷,並調用關閉觸發函數。

腳本也有可能被內置的腳本計時器中斷。默認的超時限制爲 30 秒。這個值可以通過設置 php.ini 的 max_execution_time 或 Apache .conf 設置中對應的"php_value max_execution_time"參數或者 set_time_limit() 函數來更改。當計數器超時的時候,腳本將會類似於以上連接中斷的情況退出,先前被註冊過的關閉觸發函數也將在這時被執行。在該關閉觸發函數中,可以通過調用 connection_status() 函數來檢查超時是否導致關閉觸發函數被調用。如果超時導致了關閉觸發函數的調用,該函數將返回 2。

需要注意的一點是 ABORTED 和 TIMEOUT 狀態可以同時有效。這在告訴 PHP 忽略用戶的退出操作時是可能的。PHP 將仍然注意用戶已經中斷了連接但腳本仍然在運行的情況。如果到了運行的時間限制,腳本將被退出,設置過的關閉觸發函數也將被執行。在這時會發現函數 connection_status() 返回 3。

2.相關函數:

int ignore_user_abort ( [bool setting] )
This function sets whether a client disconnect should cause a script to be aborted. It will return the previous setting and can be called without an argument to not change the current setting and only return the current setting.

int connection_aborted ( void )
Returns TRUE if client disconnected.

int connection_status ( void )
Returns the connection status bitfield.

至於多線程這個課題,參考一下這篇文章:PHP多線程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章