摘要:正常情況下,PHP執行的都是同步請求,代碼自上而下依次執行,但有些場景如發送郵件、執行耗時任務等操作時就不適用于同步請求,只能使用異步處理請求。場景要求:客戶端調用服...
正常情況下,PHP執行的都是同步請求,代碼自上而下依次執行,但有些場景如發送郵件、執行耗時任務等操作時就不適用于同步請求,只能使用異步處理請求。
場景要求:
客戶端調用服務器 a.php 接口,需要執行一個長達 10s-20s 不等的耗資源操作,假如客戶端響應請求時間為5秒(請求響應超時時間),5s 以上無回復即斷開連接。
解決設想:
客戶端調用 a.php 之后,a.php 執行異步多線程操作調用 b.php,a.php 調用成功后即刻反饋給客戶端回執,b.php 自動執行耗資源操作。
方案:
利用 fsockopen() 方法解決PHP異步請求
1.封裝異步請求函數 asyncRequest(),代碼如下:
/** * php異步請求 * @param $url string 請求Url * @param $post array POST參數 * @return string */ function asyncRequest($url, $post = array()){ $matches = parse_url($url); !isset($matches['host']) && $matches['host'] = ''; !isset($matches['path']) && $matches['path'] = ''; !isset($matches['query']) && $matches['query'] = ''; !isset($matches['port']) && $matches['port'] = 80; P($matches); $host = $matches['host']; $port = $matches['port']; $path = $matches['path'] ? $matches['path'].($matches['query'] ? '?'.$matches['query'] : '') : '/'; $post = $post && is_array($post) ? http_build_query($post) : ''; $errno = 0; $errstr = ''; $timeout = 30; //連接超時時間(S) if(function_exists('fsockopen')) { $fp = @fsockopen($host, $port, $errno, $errstr, $timeout); } elseif (function_exists('pfsockopen')) { $fp = @pfsockopen($host, $port, $errno, $errstr, $timeout); } elseif (function_exists('stream_socket_client')) { $fp = @stream_socket_client('tcp://'.$host.':'.$port, $errno, $errstr, $timeout); } else { $fp = false; } if (!$fp) { return '連接失敗'; } if ($errno || !$fp) { return $errstr; } $out = "POST $path HTTP/1.1\r\n"; $out .= "Accept: */*\r\n"; //$out .= "Referer: $Referer\r\n"; $out .= "Accept-Language: zh-cn\r\n"; $out .= "Content-Type: application/x-www-form-urlencoded\r\n"; $out .= "User-Agent: ".$_SERVER['HTTP_USER_AGENT']."\r\n"; $out .= "Host: $host\r\n"; $out .= 'Content-Length: '.strlen($post)."\r\n"; $out .= "Connection: Close\r\n"; $out .= "Cache-Control: no-cache\r\n"; $out .= $post; stream_set_blocking($fp, false); //非阻塞 stream_set_timeout($fp, $timeout);//響應超時時間(S) $result = @fwrite($fp, $out); @fclose($fp); return $result; }
2.正常接口 a.php,如下:
$url = 'http://localhost/b.php'; $param = array( 'istest' => 1, ); $asyncData = asyncRequest($url, $param); echo 'a.php success';
3.耗時接口 b.php,如下:
set_time_limit(0); ignore_user_abort(true);//設置與客戶機斷開是否會終止執行 fastcgi_finish_request();//提高請求的處理速度 sleep(15); echo "耗時15秒";
網友評論:
實用
2023-10-07 20:22:43 回復