前言
最近小編在開發過程中,遇到一些處理操作,並不需要實時返回結果,怎麼繞過等待?這個時候我們需要用到異步進行請求,長話短說。
一、ajax和img標籤
如果是web服務器返回html代碼給客戶端,我們可以使用一些特殊方法來實現所謂的異步,就是在返回給客戶端的HTML代碼中,嵌入ajax調用,或者,嵌入一個img標籤,src指向要執行的耗時腳本(還有一些類似script標籤)。如果客戶端不是html代碼,其實我們也可以拼接出完整的圖片鏈接(該鏈接包含處理,然後跳轉資源到圖片),然後客戶端進行展示。
二、使用popen()
resource popen ( string command, string mode );
//打開一個指向進程的管道,該進程由派生給定的 command 命令執行而產生。所以可以通過調用它,但忽略它的輸出。
pclose(popen("/home/xinchen/backend.php &", 'r'));
這個方法避免了第一個方法的缺點,並且也很快。但是問題是,這種方法不能通過HTTP協議請求另外的一個WebService,只能執行本地的腳本文件。並且只能單向打開,無法穿大量參數給被調用腳本。
並且如果,訪問量很高的時候,會產生大量的進程。如果使用到了外部資源,還要自己考慮競爭。
三、使用php的curl擴展
- curl實現方式的本質是, 設置超時時間爲1秒或者n毫秒;
- 注意: 如果設置超時時間爲毫秒,那麼要確認,CURL版本>=7.16.2, PHP 版本>=5.2.3.
/**
* 使用curl方式發送異步請求, put方式
*/
public function _curl($url,$params) {
$ch = curl_init();
$headers = array("Content-type: application/json;charset='utf-8'",
"Accept: application/json",
"Cache-Control: no-cache","Pragma: no-cache");
curl_setopt($ch, CURLOPT_CUSTOMREQUEST,"PUT"); //設置請求方式
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);//設置提交的字符串
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); //設置頭信息
curl_setopt($ch, CURLOPT_URL, $url); // 要訪問的地址
curl_setopt($ch, CURLOPT_RETURNTRANSFER , 1 ); //獲取的信息以文件流的形式返回
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //不進行ssl驗證
curl_setopt($ch, CURLOPT_AUTOREFERER, 1); // 自動設置Referer
//設置超時時間爲1秒,超過1秒則關閉連接
curl_setopt($ch,CURLOPT_TIMEOUT,1);
//curl_setopt($ch, CURLOPT_NOSIGNAL, 1); //注意,毫秒超時一定要設置這個
//curl_setopt($ch, CURLOPT_TIMEOUT_MS, 200); //超時毫秒,cURL 7.16.2中被加入。從PHP 5.2.3起可使用
curl_setopt($ch, CURLOPT_HEADER, 0); // 設置是否顯示返回頭信息 1返回 0不返回
curl_setopt($ch, CURLOPT_NOBODY, 0); //不想在輸出中包含body部分,設置這個選項爲一個非零值
$result = curl_exec($ch);
curl_close($ch);
return json_decode($result);
}
這種方法的缺點就是你必須要等待1秒或者多少毫秒,如果程序執行很快,豈不是白等了?
四、使用exec
使用exec執行linux命令,交給後臺完成。
例:
exec("nohup curl xxxx.com &");
五、使用fsockopen
我們創建了一個基於fsockopen的函數,這個函數中利用fsockopen去訪問url,但是在訪問時,並不要求獲取url顯示的內容,而是僅僅發出訪問請求,請求到達後馬上關閉這個訪問,缺點是要拼接header。
get方式
path不需要拼接完整url,只需要路徑
<?php
$host = "xxxx.com";//主機地址,如localhost
$path = 'test/test.php';//要請求的url,如http://localhost/test/test.php
// $fp = fsockopen( 'ssl://'.$host , 443, $errno, $errstr, 30);//https請求方式
$fp = fsockopen($host , 80, $errno, $errstr, 30);
if(!$fp){
exit('請求失敗');
}else{
//拼接header部分
$header = "GET $path?uid=28CSW6JO3B0N&chat_uid=2ABR5GZPRW6F&gift_id=".json_encode(['uniqid'=>2019110610157975])." / HTTP/1.1\r\n";
$header .= "Host: $host\r\n";
$header .= "Connection: Close\r\n\r\n";
fwrite($fp, $header);
// // 輸出請求結果(測試時用)
// $receive = '';
// while (!feof($fp))
// {
// $receive .= ''.fgets($fp, 128);
// }
// echo "".$receive;
fclose($fp);
}
post方式
$host = "xxx.com";//主機地址,如localhost
$path = 'http://xxx.com/test/test.php';//要請求的url
$fp = fsockopen( $host , 80, $errno, $errstr, 30);
if(!$fp){
exit('請求失敗');
}else{
//要發送的數據
$data['name'] = '測試';
$data['desc'] = '測試';
$post = http_build_query($data);
$len = strlen($post);
//拼接header部分
$header = "POST $path HTTP/1.1\r\n";
$header .= "Host: $host\r\n";
$header .= "Content-type: application/x-www-form-urlencoded\r\n";
$header .= "Connection: Close\r\n";
$header .= "Content-Length: $len\r\n";
$header .= "\r\n";
$header .= $post."\r\n";
fwrite($fp, $header);
// //輸出請求結果(測試時用)
// $receive = '';
// while (!feof($fp))
// {
// $receive .= ''.fgets($fp, 128);
// }
// echo "".$receive;
fclose($fp);
}
這種方法是比較好的,但是有一個缺點,你使用後會發現,偶發性請求無效,nginx狀態499。
參考:https://blog.csdn.net/panjiapengfly/article/details/103010517 解決上述問題。
小結:小編這裏說的其實都是比較輕便的方法,如果一些要求比較高的,可以使用隊列形式來完成。