PHPタイムアウトピット

5450 ワード

去年の国慶節と正月の間にプラットフォームが出会ったいくつかの問題と結びつけて、以下は主にPHPの中で関わる各種のタイムアウトとその中に存在する穴を紹介します.
Nginxのタイムアウト構成fastcgi_connct_timeout 60
Nginxとfastcgiプロセスが接続を確立するタイムアウト時間は、デフォルトで60秒で、この時間を超えると接続が切断されます.fastcgi_read_timeout 300
fastcgiプロセスと接続を確立した後にfastcgiプロセス応答を取得するタイムアウト時間は、デフォルトでは60秒で、この時間を超えても応答が得られなければ接続を切断します.バックエンド接続がタイムアウト時間内にデータを返さないため、「504 Gateway Time-out」によく遭遇します.私たちがよく出会うのは「502 Bad Gateway」で、fastcigプロセスがエラーを報告し、接続が切断されたためです.fastcgi_send_timeout 300
Nginxがfastcgiプロセスに要求を送信するタイムアウト時間は、デフォルトで60秒で、この時間を超えても送信が完了しない場合は接続を切断します.比較的大きなファイルをアップロードすることでタイムアウトが発生し、「504 Gateway Time-out」に戻ります.
PHP,PHP-FPMのタイムアウト構成max_execution_time 300
このパラメータはphpです.iniで設定されているのは、実際にこのパラメータはあまり意味がありません.この300秒のタイムアウト時間は、自分のコードの実行時間を統計するだけなので、ネットワークリクエスト、システム呼び出し、データベースクエリー、sleep()などの時間は含まれません.これを超えると、「Fatal error:Maximum execution time」というエラーが発生します.そして戻ってきたのは'500 Internal Server Error'です.私たちのプログラムの大部分の時間は、ネットワークリクエスト、データベースクエリーに費やされています.request_terminate_timeout 0
このパラメータはphp-fpmで設定されており、このタイムアウト時間はfastcgi全体にかかるすべての時間であり、これはmax_execution_timeと最も大きく異なり、総時間が超えた場合、FPMプロセスkillを直接削除し、'502 Bad Gateway'に戻る.多くの人は、このパラメータmax_execution_timeを配置すると失効すると考えていますが、実際にはそうではありません.まず、どのタイムアウト時間に達しても、どの配置が機能します.
このパラメータをオンにしないことをお勧めします.もしあなたのプログラムがタイムアウトしたら、プロセスが直接killを落として、あなたのデータの完全性は保証できません.nginxの向こうで接続タイムアウトの制御とプログラム要求の第三者資源のタイムアウト時間の制御を行うことができます.
インタフェースリクエストのタイムアウト設定
この部分は特に注意して、コンカレント量がない时は何の问题もなくて、コンカレント量が大きい时、もしいくつかのドッキングのサードパーティのシステムが挂かってあるいは処理速度が遅くなったら、あなたのFPMのプロセスはすぐに使い切って、それから各种502で、それからとてもできるのはシステムが崩壊しました.我々はフレームワークの面で,必要な要求方法を統一的にカプセル化した.
/**
 * CURL       ,           curl  post  
 *
 * @author dwer
 * @date   2016-04-11
 * @param string $url   URL
 * @param string $postData        
 * @param int $port     
 * @param int $timeout     
 * @param array $headers      
 * @return bool|mixed
 */
function pft_curl_post($url, $postData, $port = 80, $timeout = 25, $headers = []) {
    //      
    $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);

    //    
    $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のパッケージ
/**
 *      file_get_contents,    file_get_contents      ,        ,        ,           ,           。
 * @author dwer
 *  
 * @param  string  $url   url
 * @param  integer $timeout     
 * @param  array   $header     
 * @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のパッケージ
/**
 *      SOAP     ,        soap      
 * @author dwer
 *
 *       ,               
 * $soapClient = new PftSoapClient('xxx.wsdl');
 * $soapClient->setTimeout(25);
 * $soapClient->getMyMoney($params);
 */
class PftSoapClient extends \SoapClient {
    //     
    private $timeout = 0;

    //      
    public function setTimeout($timeout) {
        $timeout = intval($timeout);
        $timeout = $timeout <= 0 ? 25 : $timeout;

        $this->timeout = $timeout;
    }

    //    
    public function __doRequest($request, $location, $action, $version, $oneWay = FALSE) {
        if ($this->timeout <= 0) {
            //       
            $res = parent::__doRequest($request, $location, $action, $version, $oneWay);
        } else {
            //          
            $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;
    }
}

データベース、Redis側のタイムアウト
MysqlとRedisサーバの構成でwait_が設定されます.timeoutとtimeoutパラメータは、接続がタイムアウトしても接続がない場合に切断されることを保証します.プログラムの面では、タイムアウト後の「The MySQL server has gone away」と「PHP Fatal error:Uncaution exception'RedisException'with message'read error on connection」の異常キャプチャと再接続処理を処理する必要があります.しかし、通常、これらは下部フレームワークで統一的にパッケージされているはずです.