溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

php中怎么使用fsockopen實(shí)現(xiàn)異步請(qǐng)求

發(fā)布時(shí)間:2021-06-25 16:15:53 來源:億速云 閱讀:260 作者:Leah 欄目:編程語(yǔ)言

php中怎么使用fsockopen實(shí)現(xiàn)異步請(qǐng)求,相信很多沒有經(jīng)驗(yàn)的人對(duì)此束手無(wú)策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個(gè)問題。

php執(zhí)行一段程序,有可能幾毫秒就執(zhí)行完畢,也有可能耗時(shí)較長(zhǎng)。

例如,用戶下單這個(gè)事件,如果調(diào)用了些第三方服務(wù)進(jìn)行發(fā)郵件、短信、推送等通知,可能導(dǎo)致前端一直在等待。

而有的時(shí)候,我們并不關(guān)心這些耗時(shí)腳本的返回結(jié)果,只要執(zhí)行就行了。這時(shí)候就需要采用異步的方式執(zhí)行。

眾所周知,PHP沒有直接支持多線程這種東西。我們可以采用折衷的方式實(shí)現(xiàn)。這里主要說的就是fsockopen。

通過fsockopen發(fā)送請(qǐng)求并忽略返回結(jié)果,程序可以馬上返回。

示例代碼:

$fp = fsockopen("www.example.com", 80, $errno, $errstr, 30);
if (!$fp) {
    echo "$errstr ($errno)<br />\n";
} else {
    $out = "GET /backend.php   HTTP/1.1\r\n";
    $out .= "Host: www.example.com\r\n";
    $out .= "Connection: Close\r\n\r\n";
 
    fwrite($fp, $out);
    /*忽略執(zhí)行結(jié)果
    while (!feof($fp)) {
        echo fgets($fp, 128);
    }*/
    fclose($fp);
}

需要注意的是我們需要手動(dòng)拼出header頭信息。通過打開注釋部分,可以查看請(qǐng)求返回結(jié)果,但這時(shí)候又變成同步的了,因?yàn)槌绦驎?huì)等待返回結(jié)果才結(jié)束。

實(shí)際測(cè)試的時(shí)候發(fā)現(xiàn),不忽略執(zhí)行結(jié)果,調(diào)試的時(shí)候每次都會(huì)成功發(fā)送sock請(qǐng)求;但忽略執(zhí)行結(jié)果,經(jīng)常看到?jīng)]有成功發(fā)送sock請(qǐng)求。查看nginx日志,發(fā)現(xiàn)很多狀態(tài)碼為499的請(qǐng)求。

后來找到了原因:

fwrite之后馬上執(zhí)行fclose,nginx會(huì)直接返回499,不會(huì)把請(qǐng)求轉(zhuǎn)發(fā)給php處理。

客戶端主動(dòng)端口請(qǐng)求連接時(shí),NGINX 不會(huì)將該請(qǐng)求代理給上游服務(wù)(FastCGI PHP 進(jìn)程),這個(gè)時(shí)候 access log 中會(huì)以 499 記錄這個(gè)請(qǐng)求。

解決方案:

1)nginx.conf增加配置

# 忽略客戶端中斷
fastcgi_ignore_client_abort on;

2)fwrite之后使用usleep函數(shù)休眠20毫秒:

usleep(20000);

后來測(cè)試就沒有發(fā)現(xiàn)失敗的情況了。

附上完整代碼:

<?php
/**
 * 工具類
 * */
class FsockService {
    
    public static function post($url, $param){
        $host = parse_url($url, PHP_URL_HOST);
        $port = 80;
        $errno = '';
        $errstr = '';
        $timeout = 30;
        $data = http_build_query($param);
        // create connect
        $fp = fsockopen($host, $port, $errno, $errstr, $timeout);
        if(!$fp){
            return false;
        }
        // send request
        $out = "POST ${url} HTTP/1.1\r\n";
        $out .= "Host:${host}\r\n";
        $out .= "Content-type:application/x-www-form-urlencoded\r\n";
        $out .= "Content-length:".strlen($data)."\r\n";
        $out .= "Connection:close\r\n\r\n";
        $out .= "${data}";
        fwrite($fp, $out);
        //忽略執(zhí)行結(jié)果;否則等待返回結(jié)果
//        if(APP_DEBUG === true){
        if(false){
            $ret = '';
            while (!feof($fp)) {
                $ret .= fgets($fp, 128);
            }
        }
        usleep(20000); //fwrite之后馬上執(zhí)行fclose,nginx會(huì)直接返回499
        fclose($fp);
    }
    public static function get($url, $param){
        $host = parse_url($url, PHP_URL_HOST);
        $port = 80;
        $errno = '';
        $errstr = '';
        $timeout = 30;
        $url = $url.'?'.http_build_query($param);
        // create connect
        $fp = fsockopen($host, $port, $errno, $errstr, $timeout);
        if(!$fp){
            return false;
        }
        // send request
        $out = "GET ${url} HTTP/1.1\r\n";
        $out .= "Host:${host}\r\n";
        $out .= "Connection:close\r\n\r\n";
        fwrite($fp, $out);
        //忽略執(zhí)行結(jié)果;否則等待返回結(jié)果
//        if(APP_DEBUG === true){
        if(false){
            $ret = '';
            while (!feof($fp)) {
                $ret .= fgets($fp, 128);
            }
        }
        usleep(20000); //fwrite之后馬上執(zhí)行fclose,nginx會(huì)直接返回499
        fclose($fp);
    }
   
}
?>

看完上述內(nèi)容,你們掌握php中怎么使用fsockopen實(shí)現(xiàn)異步請(qǐng)求的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI