場景要求
客戶端調(diào)用服務(wù)器a.php接口倘潜,需要執(zhí)行一個長達5s-20s不等的耗資源操作,但是客戶端響應(yīng)請求時間為5秒(微信公眾賬號服務(wù)器請求響應(yīng)超時時間)废睦,5s以上無回復(fù)即斷開連接养泡。
解決設(shè)想
客戶端調(diào)用a.php之后嗜湃,a.php執(zhí)行異步多線程操作調(diào)用b.php澜掩,a.php調(diào)用成功后即刻反饋給客戶端回執(zhí),b.php自動執(zhí)行耗資源操作肩榕。
難點
PHP沒有真正實現(xiàn)多線程操作的方法。所以需要通過其它手段來進行模擬多線程橘荠。
方案一
利用CURL非阻塞調(diào)用b.php,實現(xiàn)過程可以參考
http://blog.csdn.net/linvo/article/details/5956629
但是有一個問題哥童,就是a.php會繼續(xù)等待b.php的響應(yīng)。
于是臨時想了一個解決方案:
在此處代碼中匀泊,將$curlopt_timeout改為1
/**
* 單個CURL調(diào)用超時限制
*/
public $curlopt_timeout = 1;
private $param = array();
但是這樣做就違背了curl本身的邏輯限制朵你。
方案二
利用socket
在a.php中加入以下代碼
$fp = fsockopen("test.com", 80, $errno, $errstr, 30);
if (!$fp){
echo 'error fsockopen';
}
else{
stream_set_blocking($fp,0);
$http = "GET /test/b.php HTTP/1.1\r\n";
$http .= "Host: test.com\r\n";
$http .= "Connection: Close\r\n\r\n";
fwrite($fp,$http);
fclose($fp);
}
即可實現(xiàn)a.php調(diào)用b.php無阻塞。
代碼中stream_set_blocking函數(shù)用來設(shè)定socket鏈接為無阻塞方式(默認為阻塞)抡医。
問題
在使用方案二以后,遇到了一個問題忌傻,即客戶端短時間內(nèi)多次調(diào)用a.php大脉,出現(xiàn)部分請求 沒有執(zhí)行b.php 的情況水孩。
解決方法:
在Nginx的nginx.conf文件中,查看worker_processes為1秤标,判斷服務(wù)端響應(yīng)請求的線程啟動限制太大宙刘,得知服務(wù)器本身配置為雙核CPU,判斷2-4線程比較合適荐类,于是修改worker_processes為4.問題得到解決!
報錯:stream_set_blocking() expects parameter 1 to be resource, null given
解決方法玉罐,修改php.ini函數(shù)設(shè)置潘拨,找到
disable_functions =
將proc_open和stream_socket_server兩個參數(shù)刪除
重啟php
完整代碼:
/**
* @parem $url 網(wǎng)頁地址 http://www.test.com/test/test.php
* @parem $port 網(wǎng)址端口 默認80
* @parem $t 腳本請求時間 默認30s
* @parem $method 請求方式 get/post
* @parem $data ['test'=>'1']
* */
public function asyncPHP($url, $port = 80, $t = 30, $method = 'get', $data = null)
{
$info = parse_url($url);
$fp = fsockopen($info["host"], $port, $errno, $errstr, $t);
//stream_set_blocking函數(shù)用來設(shè)定socket鏈接為無阻塞方式
stream_set_blocking($fp, 0);
// 判斷是否有數(shù)據(jù)
if (isset($data) && !empty($data)) {
$query = http_build_query($data); // 數(shù)組轉(zhuǎn)url 字符串形式
} else {
$query = null;
}
// 判斷 請求方式
if ($method == 'post') {
$head = "POST " . $info['path'] . " HTTP/1.0" . PHP_EOL;
} else {
$head = "GET " . $info['path'] . "?" . $query . " HTTP/1.0" . PHP_EOL;
}
$head .= "Host: " . $info['host'] . PHP_EOL; // 請求主機地址
$head .= "Referer: " . $url . PHP_EOL;
if (isset($data) && !empty($data) && ($method == 'post')) {
$head .= "Content-type: application/x-www-form-urlencoded" . PHP_EOL;
$head .= "Content-Length: " . strlen(trim($query)) . PHP_EOL;
$head .= PHP_EOL;
$head .= trim($query);
} else {
$head .= PHP_EOL;
}
fwrite($fp, $head);
fclose($fp);
}