>  기사  >  백엔드 개발  >  PHP 구성 파일의 여러 시간 초과 구성에 대한 간략한 분석

PHP 구성 파일의 여러 시간 초과 구성에 대한 간략한 분석

青灯夜游
青灯夜游앞으로
2022-03-09 11:21:235483검색

이 기사에서는 PHP 구성 파일에 대해 설명하고 구성 파일(php.ini 및 php-fpm.conf)의 여러 시간 초과 관련 구성을 분석합니다.

PHP 구성 파일의 여러 시간 초과 구성에 대한 간략한 분석

1. 개요

php.ini와 php-fpm.conf에는 시간 초과 관련 구성이 많이 있는데, 이러한 구성은 정확히 어떤 역할을 할까요? 소스코드에서는 어떻게 구현되나요? 이 문서에서는 다음 시간 제한 구성에 대해 설명합니다. timeout

    실행 환경:
  • Mac 10.14.2 + PHP 7.3.7
  • 2. 구문 분석 규칙 구성
파싱 규칙
php.ini 구문 분석은 php_module_startup() 단계에서 완료되고, ini_entry는 main에 있습니다. 각 php.ini 구성에 대해 .c에 정의된 구문 분석 규칙의 형식은 다음과 같습니다.
    ZEND_INI_ENTRY3_EX(name, default_value, modifiable, on_modify, arg1, arg2, arg3, displayer)
  • PHP는 다양한 유형의 구성에 대해 많은 매크로를 정의하며, ZEND_INI_ENTRY3_EX는 이후의 최종 결과입니다. 확장된 매크로입니다. PHP_INI_ENTRY 매크로
  • PHP_INI_ENTRY(name, default_value, modifiable, on_modify)
  • 매개변수 설명
  • name: 구성 이름

    default_value:

    구성 기본값

    수정 가능:

    설정할 수 있는 구성 범위

    php_module_startup()阶段完成,ini_entry是在 main.c 中为每个php.ini配置定义的解析规则,格式如下:

    // max_input_time,默认值为无限制
    STD_PHP_INI_ENTRY("max_input_time", "-1",    PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateLong, max_input_time, php_core_globals, core_globals)
      
    // max_execution_time,默认值为30s,修改函数为OnUpdateTimeout
    PHP_INI_ENTRY("max_execution_time", "30",    PHP_INI_ALL, OnUpdateTimeout)

    PHP为不同类型的配置定义了很多宏,ZEND_INI_ENTRY3_EX 是它们展开后的最终宏,比如PHP_INI_ENTRY

    이 모드는 PHP 명령을 언제, 어디서, 설정할 수 있는지 여부를 결정합니다. 설명서의 각 지침에는 해당 지침이 속하는 모드가 있습니다. 예를 들어, 일부 명령은 ini_set()을 사용하여 PHP 스크립트에서 설정할 수 있지만 다른 명령은 php.ini 또는 httpd.conf에서만 설정할 수 있습니다.

    예를 들어, output_buffering 명령은

    PHP_INI_PERDIR에 속하므로 ini_set()을 사용하여 설정할 수 없습니다. 그러나 display_errors 지시문은 PHP_INI_ALL

    에 속하며 ini_set()을 포함하여 어디에서나 설정할 수 있습니다.义 모드

    의미

    Php_ini_user

    PHP_INI_PERDIR

    은 php.ini, .htaccess 또는 httpd.conf

    PHP_INI_SYSTEM에서 설정할 수 있습니다. PHP_INI_ALL
    php.ini 또는 httpd에서 설정할 수 있습니다. .conf
    어디서나 설정할 수 있음

    on_modify: 配置修改函数

    三、max_input_time、max_execution_time

    因为max_input_timemax_execution_time 联系比较密切,所以放在一起来讲。

    php.ini 解释

    max_input_time

    ; Maximum amount of time each script may spend parsing request data. It's a good
    ; idea to limit this time on productions servers in order to eliminate unexpectedly
    ; long running scripts.
    ; Note: This directive is hardcoded to -1 for the CLI SAPI
    ; http://php.net/max-input-time

    翻译过来就是:max_input_time是每个脚本可以花在解析请求数据上的最大时间。在生产服务器上通过限制max_input_time可以清除掉长时间运行的脚本。在CLI模式下会硬编码为-1,即无限制。

    max_execution_time

    ; Maximum execution time of each script, in seconds
    ; http://php.net/max-execution-...
    ; Note: This directive is hardcoded to 0 for the CLI SAPI

    翻译:max_execution_time是每个脚本的最大可执行时间。在CLI模式下硬编码为0

    配置解析规则

    // max_input_time,默认值为无限制
    STD_PHP_INI_ENTRY("max_input_time", "-1",    PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateLong, max_input_time, php_core_globals, core_globals)
      
    // max_execution_time,默认值为30s,修改函数为OnUpdateTimeout
    PHP_INI_ENTRY("max_execution_time", "30",    PHP_INI_ALL, OnUpdateTimeout)

    OnUpdateTimeout()函数如下,由第二节可知配置解析发生在php_module_startup()阶段,此时EG(timeout_seconds)被赋值为了max_execution_time,但还没有设置定时器。

    // main.c
    static PHP_INI_MH(OnUpdateTimeout)
    {
        if (stage==PHP_INI_STAGE_STARTUP) {
            /* Don't set a timeout on startup, only per-request */
        /* EG(timeout_seconds) = max_execution_time */
            ZEND_ATOL(EG(timeout_seconds), ZSTR_VAL(new_value));
            return SUCCESS;
        }
        zend_unset_timeout();
        ZEND_ATOL(EG(timeout_seconds), ZSTR_VAL(new_value));
        zend_set_timeout(EG(timeout_seconds), 0);
        return SUCCESS;
    }

    设置超时定时器

    // main.c
    int php_request_startup(void) 
    {
      ......
      if (PG(max_input_time) == -1) {
        zend_set_timeout(EG(timeout_seconds), 1);
      } else {
      zend_set_timeout(PG(max_input_time), 1);
      }
      ......
    }
    
    int php_execute_script(zend_file_handle *primary_file)
    {
      ......
        if (PG(max_input_time) != -1) {
        zend_set_timeout(INI_INT("max_execution_time"), 0);
      }  
      ......
    }

    从上面代码可以看到,如果设置了max_input_time(即值不等于-1,-1可以认为是在CLI模式下),在php_request_startup()阶段会设置一个定时器,超时时间为max_input_time;在php_execute_script()阶段会重新设置一个定时器,超时时间为max_execution_time。那么整个PHP脚本执行的最大执行时间就等于max_input_time + max_execution_time

    如果没有设置max_input_time的话(即值等于-1),在php_request_startup()阶段也会设置一个定时器,但超时时间被设为了EG(timeout_seconds),而EG(timeout_seconds)已经在php_module_startup()阶段被赋值为max_execution_time,所以此时的超时时间就是max_execution_time;在php_execute_script()阶段不会重新设置定时器,前一阶段设置的max_execution_time定时器仍然生效着。那么整个PHP脚本的最大执行时间就是max_execution_time
    PHP 구성 파일의 여러 시간 초과 구성에 대한 간략한 분석

    zend_set_time() 使用setitimer(ITIMER_PROF, &t_r, NULL); 来实现定时器,ITIMER_PROF会统计包括用户态和内核态下所花费的时间,而像sleep()这样的系统调用会让进程挂起,不占用cpu时间片,所以这俩超时时间是不包括sleep()时间的。

    当定时器到时间后,ZendVM会抛出E_ERROR,即Fatal error错误。

    四、process_control_timeout

    php-fpm.conf 解释

    ; Time limit for child processes to wait for a reaction on signals from master.
    ; Available units: s(econds), m(inutes), h(ours), or d(ays)
    ; Default Unit: seconds

    翻译:process_control_timeout是留给子进程处理来自master进程信号的时间限制。

    分析

    当master进程接收到SIGINTSIGTERMSIGQUITSIGUSR2这些信号时,会调用fpm_pctl()来进行处理。

    首先master进程会根据 接收到的信号 和 当前fpm的运行状态 来决定发送给worker进程的是SIGQUIT还是SIGTERM信号,同时注册时间为process_control_timeout的定时事件。

    如果在process_control_timeout时间内子进程没有退出,那么master进程会升级SIGQUITSIGTERMSIGTERMSIGKILL,并注册1s的定时事件。SIGKILL就直接终止worker进程了,SIGTERM还能再给worker进程1s的时间。

    综上,process_control_timeout可以理解为master进程留给worker进程结束自己的时间,要是到时间worker还没搞定那就开始master自己的策略了。

    五、request_terminate_timeout、request_slowlog_timeout

    因为request_terminate_timeoutrequest_slowlog_timeout 联系比较密切,所以放在一起来讲。

    php-fpm.conf 설명

    request_terminate_timeout

    ; 작업자 프로세스가 종료된 후의 시간 초과입니다. 이 옵션은 'max_execution_time' ini 옵션
    에 사용해야 합니다. 어떤 이유에서든 '0' 값은 'off'를 의미합니다.
    ; 사용 가능한 단위: s(econds)(default), m(inutes), h(ours) 또는 d(ays)
    ;

    번역: 요청 실행을 위한 제한 시간이며, 그 이후에는 작업자 프로세스가 종료됩니다. 이 옵션은 max_execution_time ini 옵션이 어떤 이유로 스크립트 실행을 중지하지 못할 때 사용해야 합니다.

    max_execution_time 这个ini选项由于某些原因不能停止脚本执行的时候。

    request_slowlog_timeout

    ; The timeout for serving a single request after which a PHP backtrace will be
    ; dumped to the 'slowlog' file. A value of '0s' means 'off'.
    ; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
    ; Default Value: 0

    翻译:执行一个请求的超时时间,在这之后一个PHP的backtrace会被输出到slowlog文件里。

    分析

    request_slowlog_timeoutrequest_terminate_timeout 用在master进程的心跳检测中(fpm_pctl_heartbeat()),心跳时间heartbeat 简化后的算法是

    • 在开启request_terminate_timeout情况下:request_terminate_timeout/1000*3

    • 在未开启request_terminate_timeout情况下:request_slowlog_timeout/1000*3 或者 0

    • request_terminate_timeout >= request_slowlog_timeout

    第三条规则是为了保证slowlog不影响到正常的请求,heartbeat 取超时时间的1/3应该是为了避免心跳检测过于频繁,因为每次心跳检测都需要遍历所有worker进程。

    如果超时事件发生了,那么将直接kill掉worker进程,kill(child_pid, SIGTERM);request_slowlog_timeout

    ; PHP 역추적이 'slowlog' 파일에 덤프되는 시간 초과입니다. 사용 가능한 단위: s(econds) (기본값), m(inutes), h(ours) 또는 d(ays); 기본값: 0

    번역: 요청 실행을 위한 제한 시간, 그 이후에는 PHP 역추적이 느린 로그 파일에 출력됩니다. 🎜🎜Analytic🎜🎜request_slowlog_timeoutrequest_terminate_timeout 은 마스터 프로세스(fpm_pctl_heartbeat())의 하트비트 감지에 사용됩니다. 하트비트 시간heartbeat 단순화된 알고리즘은 🎜
    • 🎜 request_terminate_timeout이 켜진 경우: request_terminate_timeout/ 1000*3🎜
    • 🎜request_terminate_timeout이 활성화되지 않은 경우: request_slowlog_timeout/1000*3 또는 0🎜
    • 🎜request_terminate_timeout >= request_slowlog_timeout🎜
    🎜세 번째 규칙은 느린 로그가 일반 요청에 영향을 미치지 않도록 하는 것입니다. 하트비트는 각 하트비트 감지는 모든 작업자 프로세스를 통과해야 하기 때문에 시간 초과 1/3은 너무 빈번한 하트비트 감지를 방지하는 것이어야 합니다. 🎜🎜시간 초과 이벤트가 발생하면 작업자 프로세스가 kill(child_pid, SIGTERM);로 직접 종료되고 커널은 리소스를 재활용하고 client_socket을 닫고 nginx는 502 오류를 반환합니다. 브라우저에. 🎜🎜추천 학습: "🎜PHP 비디오 튜토리얼🎜"🎜

    위 내용은 PHP 구성 파일의 여러 시간 초과 구성에 대한 간략한 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

  • 성명:
    이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제