PHP超時(shí)的坑

結(jié)合去年國(guó)慶和過(guò)年期間平臺(tái)碰到的一些問(wèn)題邀层,下面主要介紹的是PHP里面會(huì)涉及到的各種超時(shí)以及其中存在的坑脱柱。

Nginx的超時(shí)配置

fastcgi_connct_timeout 60

Nginx和fastcgi進(jìn)程建立連接的超時(shí)時(shí)間栽惶,默認(rèn)60秒署惯,如果超過(guò)了這個(gè)時(shí)間就斷開連接漫蛔。

fastcgi_read_timeout 300

和fastcgi進(jìn)程建立連接后獲得fastcgi進(jìn)程響應(yīng)的超時(shí)時(shí)間抛蚤,默認(rèn)60秒,如果超過(guò)了這個(gè)時(shí)間都沒(méi)有獲得響應(yīng)就斷開連接姓言。我們經(jīng)常碰到的是'504 Gateway Time-out'瞬项,就是因?yàn)楹蠖诉B接沒(méi)有在超時(shí)時(shí)間內(nèi)返回?cái)?shù)據(jù)導(dǎo)致的。我們經(jīng)常碰到的是'502 Bad Gateway'何荚,是因?yàn)閒astcig進(jìn)程報(bào)錯(cuò)囱淋,導(dǎo)致連接斷開。

fastcgi_send_timeout 300

Nginx向fastcgi進(jìn)程發(fā)送請(qǐng)求的超時(shí)時(shí)間餐塘,默認(rèn)60秒妥衣,如果超過(guò)了這個(gè)時(shí)間都沒(méi)有發(fā)送完就斷開連接〗渖担可以通過(guò)上傳比較大的文件税手,就會(huì)出現(xiàn)超時(shí),然后就會(huì)返回'504 Gateway Time-out'需纳。

PHP芦倒,PHP-FPM 的超時(shí)配置

max_execution_time 300

這個(gè)參數(shù)是在php.ini中設(shè)置的,說(shuō)實(shí)在的這個(gè)參數(shù)沒(méi)有什么太大的意義不翩,因?yàn)檫@個(gè)300秒的超時(shí)時(shí)間僅僅是統(tǒng)計(jì)本身代碼的執(zhí)行時(shí)間兵扬,不包括網(wǎng)絡(luò)請(qǐng)求,系統(tǒng)調(diào)用口蝠,數(shù)據(jù)庫(kù)查詢器钟,sleep()等的時(shí)間,如果超過(guò)這個(gè)時(shí)間會(huì)產(chǎn)生一個(gè)'Fatal error: Maximum execution time'的錯(cuò)誤妙蔗,然后返回的是'500 Internal Server Error'傲霸。我們程序大部分的時(shí)間都是花在網(wǎng)絡(luò)請(qǐng)求,數(shù)據(jù)庫(kù)查詢方面的。

request_terminate_timeout 0

這個(gè)參數(shù)是在php-fpm中設(shè)置的昙啄,這個(gè)超時(shí)時(shí)間就是整個(gè)fastcgi花費(fèi)的所有時(shí)間乃摹,這個(gè)和max_execution_time最大的不同,如果總時(shí)間超過(guò)了,會(huì)直接將FPM進(jìn)程kill掉跟衅,然后返回'502 Bad Gateway'。很多人認(rèn)為配置了這個(gè)參數(shù)max_execution_time就失效了播歼,實(shí)際不是的伶跷,先達(dá)到哪個(gè)的超時(shí)時(shí)間就哪個(gè)配置起作用的。

建議是不要開啟這個(gè)參數(shù)秘狞,因?yàn)槿绻隳硞€(gè)程序超時(shí)了叭莫,進(jìn)程直接kill掉,你的數(shù)據(jù)完整性就沒(méi)有辦法保證了烁试,可以在nginx那邊做連接超時(shí)的控制和做好程序請(qǐng)求第三方資源超時(shí)時(shí)間的控制雇初。

接口請(qǐng)求方面的超時(shí)設(shè)置

這部分要特別注意,在沒(méi)有什么并發(fā)量的時(shí)候沒(méi)有什么問(wèn)題减响,在并發(fā)量大的時(shí)候靖诗,如果有些對(duì)接的第三方系統(tǒng)掛了或是處理速度很慢了,你的FPM進(jìn)程很快就會(huì)用完支示,然后就是各種502刊橘,然后很有可能的就是系統(tǒng)崩潰了。我們?cè)诳蚣軐用嫠毯瑁瑢?duì)需要用到的請(qǐng)求方法做了統(tǒng)一的封裝促绵。

/**
 * CURL 提交請(qǐng)求數(shù)據(jù),現(xiàn)在基本的接口都是使用curl進(jìn)行post請(qǐng)求
 *
 * @author dwer
 * @date   2016-04-11
 * @param string $url 請(qǐng)求URL
 * @param string $postData 請(qǐng)求發(fā)送的數(shù)據(jù)
 * @param int $port 請(qǐng)求端口
 * @param int $timeout 超時(shí)時(shí)間
 * @param array $headers 請(qǐng)求頭信息
 * @return bool|mixed
 */
function pft_curl_post($url, $postData, $port = 80, $timeout = 25, $headers = []) {
    //超時(shí)時(shí)間處理
    $timeout = intval($timeout);
    $timeout = $timeout <= 0 ? 25 : $timeout;

    $ch       = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_PORT, $port);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);

    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    if ((is_array($headers) || is_object($headers)) && count($headers)) {
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    }

    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
    $res = curl_exec($ch);

    //錯(cuò)誤處理
    $errCode = curl_errno($ch);
    if ($errCode > 0) {
        curl_close($ch);
        return false;
    } else {
        //獲取HTTP碼
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        if ($httpCode != 200) {
            curl_close($ch);
            return false;
        } else {
            curl_close($ch);
            return $res;
        }
    }
}

file_get_contents的封裝

/**
 * 統(tǒng)一封裝的file_get_contents, 原生的file_get_contents絕對(duì)不能使用嘴纺,沒(méi)有設(shè)置超時(shí)時(shí)間败晴,如果碰到網(wǎng)絡(luò)問(wèn)題,這個(gè)進(jìn)程會(huì)一直卡在那邊栽渴,無(wú)情地占用你的網(wǎng)絡(luò)連接尖坤。
 * @author dwer
 *  
 * @param  string  $url 請(qǐng)求url
 * @param  integer $timeout 超時(shí)時(shí)間
 * @param  array   $header 請(qǐng)求頭部
 * @return 
 */
function pft_file_get_contents($url, $timeout = 10, $header = []){
    $url     = strval($url);
    $timeout = intval($timeout);
    $timeout = $timeout <= 0 ? 10 : $timeout;

    $contextOptions = [
        'http' => ['timeout' => $timeout]
    ];
    if($header) {
        $contextOptions['http']['header'] = $header;
    }

    $context = stream_context_create($contextOptions);
    $res = file_get_contents($url, false, $context);
    return $res;
}

SoapClient的封裝

/**
 * 統(tǒng)一封裝的SOAP客戶端封裝,有些系統(tǒng)還在使用soap協(xié)議提供接口
 * @author dwer
 *
 * 用法是一樣的闲擦,只是添加了設(shè)置超時(shí)的時(shí)間的方法
 * $soapClient = new PftSoapClient('xxx.wsdl');
 * $soapClient->setTimeout(25);
 * $soapClient->getMyMoney($params);
 */
class PftSoapClient extends \SoapClient {
    //超時(shí)的時(shí)間
    private $timeout = 0;

    //設(shè)置超時(shí)時(shí)間
    public function setTimeout($timeout) {
        $timeout = intval($timeout);
        $timeout = $timeout <= 0 ? 25 : $timeout;

        $this->timeout = $timeout;
    }

    //請(qǐng)求接口
    public function __doRequest($request, $location, $action, $version, $oneWay = FALSE) {
        if ($this->timeout <= 0) {
            //使用默認(rèn)的方式
            $res = parent::__doRequest($request, $location, $action, $version, $oneWay);
        } else {
            //使用添加了超時(shí)的方式
            $socketTime = ini_get('default_socket_timeout');
            ini_set('default_socket_timeout', $this->timeout);
            $res = parent::__doRequest($request, $location, $action, $version, $oneWay);
            ini_set('default_socket_timeout', $socketTime);
        }

        return $res;
    }
}

數(shù)據(jù)庫(kù),Redis方面的超時(shí)

在Mysql和Redis服務(wù)器的配置中就會(huì)設(shè)置wait_timeout 和 timeout 參數(shù)糖驴,保證連接在超時(shí)沒(méi)有連接的情況下斷開連接。在程序方面需要做的就是處理超時(shí)后的'The MySQL server has gone away' 和 'PHP Fatal error: Uncaught exception 'RedisException' with message 'read error on connection'' 的異常捕獲和重連接處理佛致。不過(guò)正常情況下贮缕,這些在底層框架應(yīng)該都做了統(tǒng)一的封裝。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末俺榆,一起剝皮案震驚了整個(gè)濱河市感昼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌罐脊,老刑警劉巖定嗓,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蜕琴,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡宵溅,警方通過(guò)查閱死者的電腦和手機(jī)凌简,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)恃逻,“玉大人雏搂,你說(shuō)我怎么就攤上這事】芩穑” “怎么了凸郑?”我有些...
    開封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)矛市。 經(jīng)常有香客問(wèn)我芙沥,道長(zhǎng),這世上最難降的妖魔是什么浊吏? 我笑而不...
    開封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任而昨,我火速辦了婚禮,結(jié)果婚禮上找田,老公的妹妹穿的比我還像新娘配紫。我一直安慰自己,他們只是感情好午阵,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開白布躺孝。 她就那樣靜靜地躺著,像睡著了一般底桂。 火紅的嫁衣襯著肌膚如雪植袍。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天籽懦,我揣著相機(jī)與錄音于个,去河邊找鬼。 笑死暮顺,一個(gè)胖子當(dāng)著我的面吹牛厅篓,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播捶码,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼羽氮,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了惫恼?” 一聲冷哼從身側(cè)響起档押,我...
    開封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后令宿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體叼耙,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年粒没,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了筛婉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡癞松,死狀恐怖爽撒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情拦惋,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布安寺,位于F島的核電站厕妖,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏挑庶。R本人自食惡果不足惜言秸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望迎捺。 院中可真熱鬧举畸,春花似錦、人聲如沸凳枝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)岖瑰。三九已至叛买,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蹋订,已是汗流浹背率挣。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留露戒,地道東北人椒功。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像智什,于是被迫代替她去往敵國(guó)和親动漾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容