개발 꿀팁/PHP

PHP 시간 초과 처리 요약

Jammie 2022. 9. 14. 15:23
반응형

개술
PHP 개발에서 작업 중에 타임아웃 처리까지 매우 많이 사용되는 경우, 몇 가지 시나리오를 말씀드리겠습니다.

1. 비동기적으로 데이터를 가져옵니다. 백엔드 데이터 원본 중 하나를 가져오지 못하면 건너뜁니다. 전체 페이지 표시에 영향을 주지 않습니다.
2. 웹 서버가 해당 페이지의 처리 성능이 저하되어 다른 페이지에 접근할 수 없도록 하기 위해일부 페이지에 대한 작업 설정
3. 업로드 또는 처리 시간이 불확실한 경우 전체 프로세스에 대해 모든 타임아웃을 없음으로 설정해야 합니다.제한, 그렇지 않으면 어느 한 단계가 잘못 설치되면, 뜻하지 않은 집행이 중단될 수 있습니다.
4. 복수개의 백엔드 모듈(MySQL, Memcached, HTTP 인터페이스), 싱글 방지인터페이스 성능이 너무 나빠서 전면에서 데이터 획득이 너무 느려서 페이지 열림 속도에 영향을 주어 눈사태가 발생함
5. ...시간 초과가 필요한 경우가 많다.
이러한 장소에서는 타임아웃 설정을 고려해야 하지만, PHP에서의 타임아웃은 모두 카테고리별로, 각각의 처리 방식과전략은 모두 다르고, 시스템의 설명을 위해, 나는 PHP에서 자주 사용하는 타임아웃 처리의 총결을 정리했다.

웹 서버 시간 초과 처리
아파치
일반적으로 성능이 매우 높은 경우에는 기본적으로 모든 시간 초과 설정이 30초이지만 파일이나 네트워크를 업로드합니다.속도가 매우 느린 경우에는 시간 초과 조작을 촉발할 수 있다.

현재 apachefastcgiphp-fpm 모드에서는 세 가지 시간 초과 설정이 있습니다.

fastcgi 시간 초과 설정:

다음과 같이 httpd.conf의 fastcgi 연결 설정을 수정합니다

<IfModule mod_fastcgi.c>
    FastCgiExternalServer /home/forum/apache/apache_php/cgi-bin/php-cgi -socket /home/forum/php5/etc/php-fpm.sock
    ScriptAlias /fcgi-bin/ "/home/forum/apache/apache_php/cgi-bin/"
    AddHandler php-fastcgi .php
    Action php-fastcgi /fcgi-bin/php-cgi
    AddType application/x-httpd-php .php
</IfModule>

기본 설정은 30s입니다. 사용자 지정이 필요한 경우 설정을 100초로 수정하십시오. (수정 후 apache를 다시 시작합니다

<IfModule mod_fastcgi.c>
    FastCgiExternalServer /home/forum/apache/apache_php/cgi-bin/php-cgi -socket /home/forum/php5/etc/php-fpm.sock  -idle-timeout 100
    ScriptAlias /fcgi-bin/ "/home/forum/apache/apache_php/cgi-bin/"
    AddHandler php-fastcgi .php
    Action php-fastcgi /fcgi-bin/php-cgi
    AddType application/x-httpd-php .php
</IfModule>

500 에러가 초과되면 백엔드 php 서비스와의 연결을 끊고 apache 에러 로그를 기록합니다

[Thu Jan 27 18:30:15 2011] [error] [client 10.81.41.110] FastCGI: comm with server "/home/forum/apache/apache_php/cgi-bin/php-cgi" aborted: idle timeout (30 sec)
[Thu Jan 27 18:30:15 2011] [error] [client 10.81.41.110] FastCGI: incomplete headers (0 bytes) received from server "/home/forum/apache/apache_php/cgi-bin/php-cgi"

기타 fastcgi 설정 인자 설명:

IdleTimeout 멍하니 있는 기일
ProcessLifeTime 프로세스의 최장 라이프사이클, 경과 후 무조건 kill
MaxProcessCount 최대 프로세스 개수
DefaultMinClassProcessCount 프로그램당 시작하는 최소 프로세스 개수
DefaultMaxClassProcessCount 프로그램당 시작할 수 있는 최대 프로세스 개수
IPCConnectTimeout 프로그램 응답 시간 초과
IPCCommTimeout 프로그램과 통신하는 데 가장 오랜 시간이 걸리면 위의 오류는 이 값을 너무 작게 설정해서 생긴 것일 수 있다
MaxRequestsPerProcess 프로세스당 최대 처리 개수 달성 후 자살

Lighttpd
설정:lighttpd.conf

Lighttpd 할당시간 초과에 대한 파라미터는 다음과 같습니다(편쓰기 제한 시간 초과, 쓰기 제한 시간 초과 매개 변수 동일성:

주로 선택 사항:

server.max-keep-alive-idle=5
server.max-read-idle=60
server.read-timeout=0
server.max-connection-idle=360
# keep-alive당 최대 요청 수, 기본값 16
server.max-keep-alive-requests = 100

# keep-alive의 최대 대기 시간, 단위는 초, 기본값은 5입니다
server.max-keep-alive-idle = 1200

# lighttpd의 work 하위 프로세스 수, 기본값은 0, 단일 프로세스 실행
server.max-worker = 2

# 사용자가 요청을 보내는 동안 최대 중단 시간(단위는 초)을 제한합니다,
# 요청이 전송되는 동안 사용자가 중간에 너무 오래 멈추면 lighttpd가 먼저 연결을 끊습니다
# 기본값은 60(초)
server.max-read-idle = 1200

# 사용자가 응답을 받는 동안 최대 중단 시간(단위는 초)을 제한합니다,
# 사용자가 응답을 받는 동안(다 받지 않음), 중간에 너무 오래 멈추면 lighttpd가 먼저 연결을 끊습니다
# 기본값은 360(초)
server.max-write-idle = 12000

# 읽기 클라이언트 요청의 시간 초과 제한, 단위는 초이며 0은 제한이 없음을 나타냅니다
# max-read-idle보다 작게 설정하면 read-timeout이 활성화됩니다
server.read-timeout = 0

# 응답 페이지 작성 시 클라이언트에 대한 타임아웃 제한, 단위는 초, 배합은 0으로 제한되지 않음
# max-write-idle보다 작게 설정하면 write-timeout이 활성화됩니다
server.write-timeout = 0

# mod_proxy_core를 사용하면 백엔드와의 인터랙션 시간 제한입니다. 단위는 초입니다
server.max-connection-idle = 1200

설명:

하나의 keep-alive 접속상의 연속적인 요구에 대하여, 제1의 요청 내용을 송신하는 최대 간격은 파라미터 max-read-idle에 의해 결정되며, 제2의 요청부터는 송신 요청 내용의 최대 간격은 파라미터 max-keep-alive-idle에 의해 결정된다.요청 간 간격 초과도 max-keep-alive-idle에 의해 결정된다.요청 내용을 전송하는 총 시간 초과는 파라미터 read-timeout에 의해 결정된다.Lighttpd와 백엔드 인터랙티브 데이터의 타임아웃은 max-connection-idle에 의해 결정된다.

Nginx
설정:nginx.conf

http { 
    #Fastcgi: (백엔드에 대한 fastcgi가 유효하며, fastcgi는 proxy 모드에 속하지 않는다)
    fastcgi_connect_timeout 5;    #연결 시간 초과
    fastcgi_send_timeout 10;       #쓰기 시간 초과
    fastcgi_read_timeout 10;        #읽기 시간 초과

    #Proxy: (proxy/upstreams에 적용됨)
    proxy_connect_timeout 15s;    #연결 시간 초과
    proxy_read_timeout 24s;          #읽기 시간 초과
    proxy_send_timeout 10s;         #쓰기 시간 초과
}

설명:

Nginx의 타임아웃 설정은 매우 명확하고 이해하기 쉬우며, 위의 타임아웃은 작업 패턴에 따라 다르지만 타임아웃으로 인한 문제는 매우 많다.


PHP 자체 타임아웃 처리
PHP-fpm
설정:php-fpm.conf

<?xml version="1.0" ?>
<configuration>
//...
  Sets the limit on the number of simultaneous requests that will be served.
  Equivalent to Apache MaxClients directive.
  Equivalent to PHP_FCGI_CHILDREN environment in original php.fcgi
  Used with any pm_style.
  #php-cgi의 프로세스 수
  <value name="max_children">128</value>


  The timeout (in seconds) for serving a single request after which the worker process will be terminated
  Should be used when 'max_execution_time' ini option does not stop script execution for some reason
  '0s' means 'off'
 #php-fpm 요청 실행 시간 초과, 0s는 절대 초과되지 않습니다. 그렇지 않으면 Ns를 초로 설정합니다
  <value name="request_terminate_timeout">0s</value>

  The timeout (in seconds) for serving of single request after which a php backtrace will be dumped to slow.log file
  '0s' means 'off'
  <value name="request_slowlog_timeout">0s</value>

</configuration>

설명:

php.ini에서 php-cgi(php-fpm)에서는 PHP 스크립트의 최대 실행 시간을 설정할 수 있는 매개 변수 max_execution_time이 있습니다. 그러나 php-cgi(php-fpm)에서는 이 매개 변수가 작동하지 않습니다.실제로 PHP 스크립트의 최대 실행을 제어할 수 있는 경우

<valuename="request_terminate_timeout">0s</value>

mod_php5.so를 사용하는 모드에서 max_execution_time을 실행하면 효력이 발생하지만 php-fpm 모드에서는 효력이 발생하지 않는다는 것이다.



PHP
설정:php.ini

옵션:

max_execution_time=30

또는 코드에 다음을 설정합니다

ini_set("max_execution_time",30);
set_time_limit(30);

설명:

현재 세션에 적용됩니다. 예를 들어, 0 설정은 계속 시간 초과되지 않습니다. 그러나 php의 safe_mode가 켜져 있으면 이 설정은 적용되지 않습니다.

효과는 동일하나 구체적인 내용은 php-fpm의 일부를 참고하여 php-fpm에 request_terminat이 설정되어 있다면e_timeout이면 max_execution_time은 효력이 발생하지 않는다.

백엔드 & 인터페이스 액세스 시간 초과
HTTP 액세스
일반적으로 우리는 HTTP 접근 방식이 많은데, 주로 curl, socket, file_get_contents() 등의 방법이다.
상대방의 서버가 계속 응답하지 않을 때 우리는 비극적이고 서버 전체를 죽이기 쉬우므로 http를 방문할 때도 초를 고려해야 합니다.시의적절한 문제.

CURL HTTP 액세스
CURL은 우리가 흔히 사용하는 비교적 신뢰할 수 있는 HTTP 프로토콜 인터페이스에 접근하는 lib 라이브러리로, 성능이 높고 일부 동시 지원 기능 등이 있다.

CURL:

curl_setopt($ch, opt)는 다음과 같은 시간 초과 설정을 할 수 있습니다.

*(중요) CURLOPT_TIMEOUT cURL이 실행할 수 있는 최대 초수를 설정합니다.
*(중요) CURLOPT_TIMEOUT_MS 설정cURL이 허용하는 최대 밀리초수를 세다. (cURL 7.16.2에서 추가됨)PHP 5.2.3부터 사용 가능합니다. )

CURLOPT_CONNECTTIMEOUT가 연결을 시작하기 전에 대기하는 시간입니다.0, 무한히 기다린다.
CURLOPT_CONNECTTIMEOUT_MS가 밀리초 단위로 연결을 시도합니다.단위. 0으로 설정하면 무한히 대기한다. cURL 7.16.2에 가입되어 있습니다.PHP 5.2부터.3부터 사용할 수 있습니다.
CURLOPT_DNS_CACHE_TIMEOUT 설정 메모리에 DNS 정보 저장시간은 기본 120초다

curl 일반 초급 시간 초과:

$ch=curl_init();
curl_setopt($ch,CURLOPT_URL,$url);
curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch,CURLOPT_TIMEOUT,60);//1초만 세팅하면 돼요.
curl_setopt($ch,CURLOPT_HTTPHEADER,$headers);
curl_setopt($ch,CURLOPT_USERAGENT,$defined_vars['HTTP_USER_AGENT']);

curl 일반 초급 시간 초과 사용:

curl_setopt($ch,CURLOPT_TIMEOUT,60);

curl이 밀리초초초과(밀리초초과 같이

curl_easy_setopt(curl,CURLOPT_NOSIGNAL,1L);

또는 다음과 같다

curl_setopt($ch,CURLOPT_NOSIGNAL,true);밀리초 단위의 타임아웃 설정을 지원할 수 있습니다

밀리초 단위의 curl 시간 초과 예제:

<?php
if (!isset($_GET['foo'])) {
        // Client
        $ch = curl_init('http://example.com/');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_NOSIGNAL, 1);    //주의, 밀리초 초과 시간에는 반드시 이것을 설정해야 합니다
        curl_setopt($ch, CURLOPT_TIMEOUT_MS, 200);  //시간 초과 밀리초, cURL 7.16.2에 추가되었다.PHP 5.2.3부터 사용 가능
        $data = curl_exec($ch);
        $curl_errno = curl_errno($ch);
        $curl_error = curl_error($ch);
        curl_close($ch);

        if ($curl_errno > 0) {
                echo "cURL Error ($curl_errno): $curl_error\n";
        } else {
                echo "Data received: $data\n";
        }
} else {
        // Server
        sleep(10);
        echo "Done.";
}
?>

기타 몇 가지 기술:

경험에 비추어 보면 다음과 같다.cURL버전>=libcurl/7.21.0버전, 밀리초급 타임아웃은 반드시 유효하므로 꼭 기억해주세요.
curl_multi의 밀리초급 타임아웃도 문제가 있다.단일 접근은 ms급 타임아웃을 지원하며 curl_multi는 병렬로 여러 개를 맞추면 안 됩니다.
스트림 접근 HTTP
curl을 제외하고 나는HTTP 프로토콜은 fsockopen이나 file 오퍼레이션 함수를 이용하여 자체적으로 처리하는 경우도 많기 때문에 우리는 이것에 대해시간 초과 처리도 필수다.

보통 연결 시간 초과는 가능합니다직접 설정, 단 스트림 읽기 시간 초과는 따로 처리하셔야 합니다.

자체 코드 작성:

$tmCurrent = gettimeofday();
$intUSGone = ($tmCurrent['sec'] - $tmStart['sec']) * 1000000
        + ($tmCurrent['usec'] - $tmStart['usec']);
if ($intUSGone > $this->_intReadTimeoutUS) {
    return false;
}

또는 스트림 처리 함수 stream_set_timeout( )과 stream_get_meta_data( )를 내장하여 처리한다

<?php 
// Timeout in seconds 
$timeout = 5; 
$fp = fsockopen("example.com", 80, $errno, $errstr, $timeout); 
if ($fp) { 
        fwrite($fp, "GET / HTTP/1.0\r\n"); 
        fwrite($fp, "Host: example.com\r\n"); 
        fwrite($fp, "Connection: Close\r\n\r\n"); 
        stream_set_blocking($fp, true);   //중요, 비차단 모드로 설정
        stream_set_timeout($fp,$timeout);   //설정 시간 초과
        $info = stream_get_meta_data($fp); 
        while ((!feof($fp)) && (!$info['timed_out'])) { 
                $data .= fgets($fp, 4096); 
                $info = stream_get_meta_data($fp); 
                ob_flush; 
                flush(); 
        } 
        if ($info['timed_out']) { 
                echo "Connection Timed Out!"; 
        } else { 
                echo $data; 
        } 
}

file_get_contents 시간 초과:

<?php
$timeout = array(
    'http' => array(
        'timeout' => 5 //시간 초과 시간을 초 단위로 설정합니다
    )
);
$ctx = stream_context_create($timeout);
$text = file_get_contents("http://example.com/", 0, $ctx);
?>

fopen 시간 초과:

<?php
$timeout = array(
    'http' => array(
        'timeout' => 5 //시간 초과 시간을 초 단위로 설정합니다
    )
);
$ctx = stream_context_create($timeout);
if ($fp = fopen("http://example.com/", "r", false, $ctx)) {
  while( $c = fread($fp, 8192)) {
    echo $c;
  }
  fclose($fp);
}
?>

MySQL
php의 mysql 클라이언트는 타임아웃 옵션을 설정하지 않았고, mysqli도 mysql도 없지만 libmysql은 타임아웃 옵션을 제공하고 우리가 php에 숨겼을 뿐이다.

그렇다면 PHP에서 이 조작을 어떻게 사용하는가는 MySQL 조작 상수를 우리 스스로 정의해야 하며, 주로 관련된 상수는 다음과 같다

MYSQL_OPT_READ_TIMEOUT=11;
MYSQL_OPT_WRITE_TIMEOUT=12;

이 두 개를 정의하면 options를 사용하여 적절한 값을 설정할 수 있습니다.

단, mysql 내부에서 구현한 점이 있습니다.

1. 시간 초과 설정 단위는 초, 최소 1초 설정
2. 그러나 mysql 하단의 read는 두 번 다시 시도하기 때문에 실제는 3이 된다.초
두 번 다시 시도 + 자기 한 번 = 3배 시간 초과. 그러면 최소 시간 초과입니다.간은 3초, 이보다 낮지 않을 것, 대부분의 응용에 대해말하자면 받아들일 수 있지만, 일부 응용에 대해서는 우수해야 한다.녹이다.

mysql에 대한 액세스 제한 시간을 설정하는 php 인스턴스 보기:

<?php
//읽기/ 쓰기 시간 초과 설정
if (!defined('MYSQL_OPT_READ_TIMEOUT')) {
    define('MYSQL_OPT_READ_TIMEOUT',  11);
}
if (!defined('MYSQL_OPT_WRITE_TIMEOUT')) {
    define('MYSQL_OPT_WRITE_TIMEOUT', 12);
}
//설정 시간 초과
$mysqli = mysqli_init();
$mysqli->options(MYSQL_OPT_READ_TIMEOUT, 3);
$mysqli->options(MYSQL_OPT_WRITE_TIMEOUT, 1);

//데이터베이스 연결
$mysqli->real_connect("localhost", "root", "root", "test");
if (mysqli_connect_errno()) {
    printf("Connect failed: %s/n", mysqli_connect_error());
    exit();
}
//쿼리 실행 sleep 1초 동안 시간 초과됨
printf("Host information: %s/n", $mysqli->host_info);
if (!($res=$mysqli->query('select sleep(1)'))) {
    echo "query1 error: ". $mysqli->error ."/n";
} else {
    echo "Query1: query success/n";
}
//쿼리를 실행할 때 9초 동안 시간이 초과됩니다
if (!($res=$mysqli->query('select sleep(9)'))) {
    echo "query2 error: ". $mysqli->error ."/n";
} else {
    echo "Query2: query success/n";
}
$mysqli->close();
echo "close mysql connection/n";
?>

Memcached
PHP 확장
php_memcache 클라이언트:
연결 시간 초과:

boolMemcache::connect(string $host[,int $port[,int $timeout]])

get와 set 모두 명확한 타임아웃 파라미터가 없다.

libmemcached 클라이언트: php 인터페이스에서 눈에 띄지 않음의 타임아웃 파라미터입니다.

설명:그러니 PHP에서 Memcached에 접속하는 것은 존재한다.hack 부분을 직접 조작하거나 온라인 패치를 참고해야 하는 문제가 많다.

C&C++ 접근 Memcached
클라이언트:libmemcached 클라이언트

설명: memcache 시간 초과 설정은 5와 같은 작은 점을 설정할 수 있습니다.10밀리초면 충분하니 그 이상이면 차라리 db에서 조회하는 게 낫다.

다음은 set 데이터를 연결하고 읽는 시간 제한의 C++ 예제입니다

//연결 시간 제한 만들기 (Memcached 연결)
memcached_st* MemCacheProxy::_create_handle()
{
        memcached_st * mmc = NULL;
        memcached_return_t prc;
        if (_mpool != NULL) {  // get from pool
          mmc = memcached_pool_pop(_mpool, false, &prc);
          if (mmc == NULL) {
            __LOG_WARNING__("MemCacheProxy", "get handle from pool error [%d]", (int)prc);
          }
          return mmc;
        }

        memcached_st* handle = memcached_create(NULL);
        if (handle == NULL){
          __LOG_WARNING__("MemCacheProxy", "create_handle error");
          return NULL;
        }

        // 연결/ 읽기 시간 초과 설정
        memcached_behavior_set(handle, MEMCACHED_BEHAVIOR_HASH, MEMCACHED_HASH_DEFAULT);
        memcached_behavior_set(handle, MEMCACHED_BEHAVIOR_NO_BLOCK, _noblock);  //参数MEMCACHED_BEHAVIOR_NO_BLOCK为1使超时配置生效,不设置超时会不生效,关键时候会悲剧的,容易引起雪崩
        memcached_behavior_set(handle, MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT, _connect_timeout);  //连接超时
        memcached_behavior_set(handle, MEMCACHED_BEHAVIOR_RCV_TIMEOUT, _read_timeout);    //读超时
        memcached_behavior_set(handle, MEMCACHED_BEHAVIOR_SND_TIMEOUT, _send_timeout);    //写超时
        memcached_behavior_set(handle, MEMCACHED_BEHAVIOR_POLL_TIMEOUT, _poll_timeout);

        // 일치 hash 설정
        //      memcached_behavior_set_distribution(handle, MEMCACHED_DISTRIBUTION_CONSISTENT);
        memcached_behavior_set(handle, MEMCACHED_BEHAVIOR_DISTRIBUTION, MEMCACHED_DISTRIBUTION_CONSISTENT);

        memcached_return rc;
        for (uint i = 0; i < _server_count; i++){
          rc = memcached_server_add(handle, _ips[i], _ports[i]);
          if (MEMCACHED_SUCCESS != rc) {
            __LOG_WARNING__("MemCacheProxy", "add server [%s:%d] failed.", _ips[i], _ports[i]);
          }
        }

        _mpool = memcached_pool_create(handle, _min_connect, _max_connect);
        if (_mpool == NULL){
          __LOG_WARNING__("MemCacheProxy", "create_pool error");
          return NULL;
        }

        mmc = memcached_pool_pop(_mpool, false, &prc);
        if (mmc == NULL) {
          __LOG_WARNING__("MyMemCacheProxy", "get handle from pool error [%d]", (int)prc);
        }
        //__LOG_DEBUG__("MemCacheProxy", "get handle [%p]", handle);
        return mmc;
}

//key 타임아웃 설정(set 데이터에서 memcached)
bool MemCacheProxy::_add(memcached_st* handle, unsigned int* key, const char* value, int len, unsigned int timeout)
{
        memcached_return rc;

        char tmp[1024];
        snprintf(tmp, sizeof (tmp), "%u#%u", key[0], key[1]);
        //타임아웃 값이 있어요
        rc = memcached_set(handle, tmp, strlen(tmp), (char*)value, len, timeout, 0);
        if (MEMCACHED_SUCCESS != rc){
          return false;
        } 
        return true;
}
//Memcache 읽기 시간 초과 (설정되지 않음)
libmemcahed 소스에서 인터페이스 정의:
 
LIBMEMCACHED_APIchar*memcached_get(memcached_st*ptr,constchar*key,size_tkey_length,size_t*value_length,uint32_t*flags,memcached_return_t*error);

LIBMEMCACHED_APImemcached_return_tmemcached_mget(memcached_st*ptr,constchar*const*keys,constsize_t*key_length,size_tnumber_of_keys);

인터페이스를 보면 데이터를 읽을 때 시간 초과 설정이 없음을 알 수 있다

어떻게 실현하는 시간 제한
이런 프로그램에 기능이 필요한 예를 들면, 너는 시간 제한을 방문한 뒤 단독으로 한 블록 모듈을 들 Socket는 우리들 위에서 Socket을 묘사할 때의 그 어떤 합의도, 그렇게 이럴 때 사유가 필요할 것을 스스로 찾아 정책을 일부 실현할 때 처리 시간 제한이 필요하다. 일부 코드 처리

PHP에서 이뤄지고 있다. 시간 제한
1, 가장 간단한 초급:(시간 제한 시간 초과 달성하고 있다.)급초
링크:간단한 사고가 한개를 설치하고 백엔드가 아니지 않았다면 패턴을 연결하는 체증에 시달려 시간과 순환을 판단할 때 현재 시간 사이의 차이를 뛰어넘고 있다.

오리지널에서 시간 초과 phpsocket:( 갈 때마다 현재 시간의 순환이 감소하면서도 성능이 나빠 할 수 있게 될 것이 높은 사용 cpu)

<?php
    $host = "127.0.0.1";
    $port = "80";
    $timeout = 15;  //timeout in seconds

    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)
      or die("Unable to create socket\n");

    socket_set_nonblock($socket)     //차단 모드로 설정해야 합니다
      or die("Unable to set nonblock on socket\n");

    $time = time();
    //순환할 때 매번 해당 값을 뺀다
    while (!@socket_connect($socket, $host, $port))    //연결이 안 되면 계속 사순환이 되고
    {
      $err = socket_last_error($socket);
      if ($err == 115 || $err == 114)
      {
        if ((time() - $time) >= $timeout)    //매번 시간 초과 여부를 판단해야 한다
        {
          socket_close($socket);
          die("Connection timed out.\n");
        }
        sleep(1);
        continue;
      }
      die(socket_strerror($err) . "\n");
    }
    socket_set_block($this->socket)    //복원 차단 모드
      or die("Unable to set block on socket\n");
?>

2. 업그레이드: PHP 자체 비동기 IO 사용(밀리초 단위 시간 초과)
설명:

비동기 IO: 비동기 IO의 개념과 동기 IO가 상대적이다.비동기 프로세스 호출로 보내기호출자는 즉시 결과를 얻을 수 없다.실제로 이 호출을 처리하는 위젯은 완료된 후 상태, 알림 및 콜백으로 호출자에게 알립니다.비동기 IO는 비트를 그룹으로 나눠 전송하며 그룹은 8비트의 한 글자 이상일 수 있다.송신자는 이러한 비트 세트를 임의의 시각에 송신할 수 있으며, 수신자는 그들이 언제 도착할지 결코 알지 못한다.

다중화: 다중화 모델은 여러 IO 조작을 감지하여 조작할 수 있도록 되돌립니다.집합, 이렇게 하면 그것을 조작할 수 있다.이것에 의해, 블록킹 IO가 각 IO와 비블록킹 점유 시스템의 자원을 수시로 처리하지 못한다는 판정이 회피된다.

socket_select()를 사용하여 시간 초과 달성

socket_select(…,floor($timeout),ceil($timeout*1000000));

select 특징:마이크로초까지 설정할 수 있는 타임아웃!

socket_select()를 사용한 타임아웃 코드( 필요)비동기 IO프로그래밍에 대한 지식을 알고 이해해야 함)

### 호출 클래스 ####
<?php 
$server = new Server; 
$client = new Client; 

for (;;) { 
  foreach ($select->can_read(0) as $socket) { 

    if ($socket == $client->socket) { 
      // New Client Socket 
      $select->add(socket_accept($client->socket)); 
    } 
    else { 
      //there's something to read on $socket 
    } 
  } 
} 
?> 

### 비동기 다중화 IO & 시간 초과 연결 처리 클래스 ###
<?php 
class select { 
  var $sockets; 

  function select($sockets) { 

    $this->sockets = array(); 

    foreach ($sockets as $socket) { 
      $this->add($socket); 
    } 
  } 

  function add($add_socket) { 
    array_push($this->sockets,$add_socket); 
  } 

  function remove($remove_socket) { 
    $sockets = array(); 

    foreach ($this->sockets as $socket) { 
      if($remove_socket != $socket) 
        $sockets[] = $socket; 
    } 

    $this->sockets = $sockets; 
  } 

  function can_read($timeout) { 
    $read = $this->sockets; 
    socket_select($read,$write = NULL,$except = NULL,$timeout); 
    return $read; 
  } 

  function can_write($timeout) { 
    $write = $this->sockets; 
    socket_select($read = NULL,$write,$except = NULL,$timeout); 
    return $write; 
  } 
} 
?>

C&C++에서 시간 초과 달성
보통 리눅스/C에서++에서, :alarm()을 사용하여 타이머를 설정하거나, :select(), poll(), epoll()과 같은 비동기 다중 IO를 사용하여 밀리초 단위의 시간 초과를 수행할 수 있습니다.2차 패키지의 비동기식 io 라이브러리(libev)로도 가능하다.

1. alarm 사용 중용신호 구현 시간 초과(초 단위 시간 초과)
설명: 리눅스 커널 c온넥트 타임아웃은 통상 75초인데, 10초처럼 짧게 설정해 커넥트에서 미리 돌아올 수 있다.여기에서는 신호 처리 메커니즘을 사용하여 alarm을 호출하고 시간 초과 후 SIGALRM 신호를 생성한다(select를 사용하여 구현도 가능)

alarym 초급으로 구현 connect 설정의 타임아웃 코드 예:

//신호 처리 함수
static void connect_alarm(int signo)
{
     debug_printf("SignalHandler");
     return;
}

//alarm 타임아웃 연결 구현
static void conn_alarm()
{ 
  Sigfunc * sigfunc ; //기존 신호 처리 함수
  sigfunc=signal(SIGALRM, connect_alarm); //신호처리함수 connect_alarm을 설정하여 기존의 신호처리함수를 (있다면) 보존한다
    int timeout = 5;

    //알람 설정
  if( alarm(timeout)!=0 ){ 
    //... 알람은 이미 설정 처리되었습니다
  } 

    //연결 작업을 수행합니다
    if (connect(m_Socket, (struct sockaddr *)&addr, sizeof(addr)) < 0 ) {
        if ( errno == EINTR ) { //오류 번호가 EINTR로 설정되어 있으면 시간 초과로 중단되었음을 나타냅니다
            debug_printf("Timeout");
            m_connectionStatus = STATUS_CLOSED;
            errno = ETIMEDOUT; //세 번의 악수가 계속 진행되는 것을 방지하다 
            return ERR_TIMEOUT;
        }
        else {
            debug_printf("Other Err");
            m_connectionStatus = STATUS_CLOSED;
            return ERR_NET_SOCKET;
        }
    }
  alarm(0);//시계 끄기
  signal(SIGALRM, sigfunc); //원래의 신호 처리 함수를 복원하다
  return; 
}

// 데이터 읽기 시간 제한 설정
recv 설정도 가능타임아웃 5초 만에 응답 못 받으면 중단

signal( ... ); 
alarm(5); 
recv( ... ); 
alarm(0); 
static void sig_alarm(int signo){return;}

클라이언트가 읽기(readline, …)를 차단할 때 서버가 다운되면 클라이언트 TCP는 서버에서 ACK를 수신하여 데이터 세그먼트를 계속 재전송하려고 시도합니다. 재전송을 포기하고 오류를 반환하는 데 약 9분이 걸립니다.이로 인해 고객이 읽기를 차단할 때 호출이 시간 초과되었습니다.

2. 비동기 다중화 IO 사용(밀리초 단위 시간 초과)
비동기 IO 실행 흐름:

1. 우선 플래그 비트를 Non-blocking 모드로 하고, 비블록킹 모드에서 connect 함수를 하향 조정한다.
2.connect를 호출하면 보통 TCP가 세 번 악수하는 데 시간이 걸리고, 비차단 호출이 즉시 완료되지 않으면 오류를 반환하기 때문에 여기서 EINPROGRESS를 반환합니다. 연결이 설정되었지만 아직 완료되지 않았음을 나타냅니다.
3. 현재 소켓을 fd_ZERO(), FD_SET() 매크로를 사용하여 fd_set wset와 fd_set wset에 세트하고 타임아웃 시간(struct timev)을 설정합니다.al *timeout)
4. select (socket, &rset, &wset, NULL, timeout)
0을 반환하면 connect가 시간 초과되었음을 나타냅니다. 커널에서 시간 초과 제한이 75초이기 때문에 시간 초과를 설정할 필요가 없습니다.

static void conn_select() {
    // Open TCP Socket
    m_Socket = socket(PF_INET,SOCK_STREAM,0);
    if( m_Socket < 0 )
    {
        m_connectionStatus = STATUS_CLOSED;
        return ERR_NET_SOCKET;
    }

    struct sockaddr_in addr;
    inet_aton(m_Host.c_str(), &addr.sin_addr);
    addr.sin_port = htons(m_Port);
    addr.sin_family = PF_INET;

    // Set timeout values for socket
    struct timeval timeouts;
    timeouts.tv_sec = SOCKET_TIMEOUT_SEC ;   // const -> 5
    timeouts.tv_usec = SOCKET_TIMEOUT_USEC ; // const -> 0
    uint8_t optlen = sizeof(timeouts);

    if( setsockopt( m_Socket, SOL_SOCKET, SO_RCVTIMEO,&timeouts,(socklen_t)optlen) < 0 )
    {
        m_connectionStatus = STATUS_CLOSED;
        return ERR_NET_SOCKET;
    }

    // Set the Socket to TCP Nodelay ( Send immediatly after a send / write command )
    int flag_TCP_nodelay = 1;
    if ( (setsockopt( m_Socket, IPPROTO_TCP, TCP_NODELAY,
            (char *)&flag_TCP_nodelay, sizeof(flag_TCP_nodelay))) < 0)
    {
        m_connectionStatus = STATUS_CLOSED;
        return ERR_NET_SOCKET;
    }
    // Save Socket Flags
    int opts_blocking = fcntl(m_Socket, F_GETFL);
    if ( opts_blocking < 0 )
    {
        return ERR_NET_SOCKET;
    }
    //비차단 모드로 설정
    int opts_noblocking = (opts_blocking | O_NONBLOCK);
    // Set Socket to Non-Blocking
    if (fcntl(m_Socket, F_SETFL, opts_noblocking)<0)
    {
        return ERR_NET_SOCKET;
    }
    // Connect
    if ( connect(m_Socket, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    {
        // EINPROGRESS always appears on Non Blocking connect
        if ( errno != EINPROGRESS )
        {
            m_connectionStatus = STATUS_CLOSED;
            return ERR_NET_SOCKET;
        }
        // Create a set of sockets for select
        fd_set socks;
        FD_ZERO(&socks);
        FD_SET(m_Socket,&socks);
        // Wait for connection or timeout
        int fdcnt = select(m_Socket+1,NULL,&socks,NULL,&timeouts);
        if ( fdcnt < 0 )
        {
            return ERR_NET_SOCKET;
        }
        else if ( fdcnt == 0 )
        {
            return ERR_TIMEOUT;
        }
    }
    //Set Socket to Blocking again
    if(fcntl(m_Socket,F_SETFL,opts_blocking)<0)
    {
        return ERR_NET_SOCKET;
    }

    m_connectionStatus = STATUS_OPEN;
    return 0;
}

설명: 타임아웃 구현에 있어 어떤 스크립트 언어든: PHP, Python, Perl의 기본 저변은 C&C++이며, 이러한 타임아웃 처리를 이해하기 위해서는 리눅스 프로그래밍과 네트워크 프로그래밍에 대한 지식이 필요하다

결론
1. PHP 응용 계층은 어떻게 시간 초과를 설정합니까?
PHP는 처리 시간 초과 차원이 매우 많고, 차원이 다르므로, 필요하다.백엔드 시간 초과를 포함하려면:
브라우저(클라이언트) -> 액세스 계층 -> Web서버 -> PHP -> 백엔드 (MySQL, Memca)ched)

즉, 액세스 계층(Web 서버 계층)의 타임아웃 시간PHP(PHP-FPM)에서 설정한 타임아웃 시간보다 커야 하며, 이후로는 안 됩니다.처리가 끝나면 너는 앞에서 시간을 초과해서 닫는다.이것은 매우 좋은 도구이다.그리고 PHP의 타임아웃 시간이 PHP 자체 백엔드(MySQL, HTTP,Memcached)의 타임아웃, 그렇지 않으면 결말이 앞과 같다.

2. 시간 초과 설정 원칙은?
영구히 시간 초과를 하지 않기를 원하는 코드(예: 업로드, 또는정기적으로 달리는 프로그램), 나는 여전히 시간 초과 시간을 설정할 것을 건의한다. 예를 들어, 12시간이다.한 php 프로세스나 백엔드를 영구적으로 다져 다른 페이지에 서비스를 제공하지 못해 모든 기기에 눈사태를 일으키지 않도록 하기 위해서다.
빠른 응답을 요구하는 프로그램이라면 백엔드 시간 초과를 권장합니다예를 들어 500ms 연결, 1s 읽기, 1s 쓰기, 이런 속도, 이런 식으로 짧게 설정하세요.애벌랜치 적용 문제를 대폭 줄일 수 있어 서버 부하를 크게 줄이지 않는다.

3. 자체 개발 시간 초과 방문 적합?
만부득이한 경우가 아니라면 기존의 많은 네트워크를 사용하는 것이 좋습니다.프로그래밍 틀이든 기초 라이브러리든, 안에는 일반적으로 모두 시간 초과 실현이 있는데, 예를 들면 일부 네트워크와 같다.IIO의 lib 라이브러리는 가능한 한 내장되어 있는 것을 사용하고, 스스로 반복하여 바퀴를 만들면 버그가 생기기 쉽고, 유지보수가 용이하지 않습니다(단, 학습에 의한 목적이라면 별론입니다).)。

4. 기타 건의
시간 초과는 모든 응용 프로그램에서 큰 문제로, 응용 프로그램을 개발하고 있다.때는 모두 고려해야 한다.나는 응용 프로그램이 시간을 초과하여 백 초를 설정하는 것을 본 적이 있는데, 이런 성능은 확실하다.틀렸어, 내가 예를 들어보자.
예를 들어 너 php-fpm이 128개의 php-를 켰어CGI 프로세스, 그리고 시간 초과 설정이 32s이면 백엔드 서비스 비율이더 나쁘고 극단적인 경우 초당 최대 응답 가능한 요청은 다음과 같습니다.
128/32 = 4개
잘못 본 거 아니야, 1초에 4개만 처리할 수 있어. 그 서비스도.형편없어요! php-cgi 프로세스를 크게 할 수는 있지만, 메모리가 부족하고,프로세스 간 전환 비용도 증가하고, cpu야, 메모리야 모두 증가하며, 서비스도 불안정해진다.따라서 가능한 한 합리적인 시간 초과 값을 설정하거나 백엔드를 재촉한다.성능을 높이다

 

반응형