저희 웹 프로젝트의 경우, 신도시 증가로 인해 방문 횟수가 늘어나고, 인터페이스를 제공하는 업체로서 최근 "502" 요청이 많이 들어오고 있습니다. 다운스트림 피드백에 의해 보고됩니다.
502, 잘못된 게이트웨이는 일반적으로 업스트림(여기서는 PHP)의 오류입니다. PHP의 경우 502의 일반적인 원인은 스크립트 실행이 시간 초과 설정 시간을 초과하거나 시간 초과 설정이 너무 커서 PHP 프로세스가 중단되는 것입니다. 고객을 픽업하는 유휴 작업자 프로세스가 없습니다.
우리 프로젝트는 PHP 실행 시간 설정이 너무 짧기 때문에 발생합니다. 이 경우 먼저 PHP 실행 시간을 적절하게 늘리고 결국 최적화가 먼저 지워지는지 확인하세요.
PHP 실행 시간을 제어하는 두 가지 옵션이 있습니다. php.ini의 max_execution_time과 php-fpm의 request_terminate_timeout입니다. request_terminate_timeout은 max_execution_time을 재정의할 수 있으므로 전역 php.ini를 변경하지 않으려면 php의 구성만 변경하면 됩니다. -fpm입니다.
다음으로 php 스크립트 실행이 설정된 시간을 초과하면 nginx가 502를 반환하는 이유를 자세히 분석하겠습니다.
먼저 장면을 설정하고 문제를 재현해 보겠습니다.
nginx와 php는 쉬운 추적을 위해 각각 하나의 워커만 시작합니다.
php-fpm의 request_terminate_timeout은 3초로 설정되어 있습니다.
테스트 스크립트 test.php
sleep(20); echo 'ok';
go go go:
브라우저에서 www.v.com/test.php를 방문하고 3초 후에... 404가 예상대로 나타납니까? ? ? 무엇? ? ?
nginx 구성 파일을 살펴보세요
이 위치 구성은 5xx 오류가 발생했을 때 더 좋은 인터페이스로 이동하기 위한 것이지만 /usr/share/nginx에서 찾았습니다. /html 그 아래에 50x.html 파일이 없습니다. 그래서 404를 받았습니다. 이것이 문제에 대한 나의 판단의 정확성에 영향을 미치지 않습니까? 그냥 댓글로 달아주세요! 다시 방문하여 3초 정도 기다리면 드디어 '정상' 인터페이스가 나옵니다.
이제 환경이 준비되었으므로 웹 문제 해결 루틴을 따르겠습니다. 먼저 오류 로그를 살펴보겠습니다.
nginx:
보고된 오류는 다음과 같습니다. ) failed (104: 연결 재설정 by 피어.
recv failed, the 연결이 재설정되었습니다. 연결이 재설정된 이유는 무엇입니까?
우리는 php-fpm의 오류 로그를 보고 있습니다:
php_admin_value[error_log] 옵션은 php.ini의 오류를 덮어쓰는 php의 오류 로그입니다. 그러나 이는 php의 오류가 아니라 php-fpm의 오류입니다. php-fpm의 오류 로그는 error_log에 있습니다. 옵션이 지정되었습니다.)
각 요청은 2개의 경고와 1개의 알림을 생성합니다.
경고: 스크립트 실행 시간이 초과되어 종료되었습니다.
경고: 하위 프로세스가 sigterm 신호를 수신하고 종료되었습니다. 새로운 하위 프로세스 (pm.min_spare_servers = 1로 설정했기 때문에)
php의 작업자 프로세스 실행 시간이 초과되면 스크립트 실행이 종료될 뿐만 아니라 작업자 프로세스도 종료되는 오류가 발생하는 것 같습니다. php 작업자 프로세스가 종료되어 연결이 재설정되었습니다(tcp 연결의 한 쪽이 연결이 끊기면 다른 쪽에게 먼저 전송됩니다)
로그에서 PHP 스크립트 실행 시간이 초과되고 작업자가 하위 작업을 수행한 것을 알 수 있습니다. 프로세스가 종료되어 nginx가 피어에 의한 연결 재설정 오류를 보고합니다. strace를 통해 php 및 nginx의 상황을 살펴보겠습니다.
php:
1.nginx 연결 요청 수락(소켓, 바인드 및 수신은 모두 마스터에서 완료됩니다. nginx에 대한 포트는 47039입니다. fd0에서 데이터를 읽는 것은 fast-cgi 프로토콜에 의해 지정됩니다. 수락 후 연결된 설명자는 3입니다. . fd3에서 nginx 전송을 읽습니다. 들어오는 데이터는 fastcgi 프로토콜 형식이며 856바이트를 수신합니다.
fastcgi 프로토콜 데이터 패킷은 8바이트로 정렬되어 있으며 패킷 헤더와 패킷 본문으로 구성되어 있기 때문입니다. 그리고 먼저 일부 요청 ID, 버전, 유형 및 기타 정보(헤더와 본문은 각각 8바이트를 차지함)를 포함하는 요청 패킷을 보낸 다음 매개변수 및 환경 변수 가져오기(헤더는 8바이트를 차지함)를 전달하기 위해 params 패킷을 보냅니다. bytes), 패킷 본문이 길어짐), 마지막으로 패킷 본문이 없고 패킷 헤더만 있는 params 데이터 패킷이 전송되어 매개변수 전송이 끝났음을 나타냅니다(패킷 헤더 8바이트). 따라서 처음 세 번의 읽기는 요청 패키지의 헤더와 본문은 물론 params 패키지의 헤더를 읽는 데 사용됩니다. 네 번째 읽기는 실제 데이터를 읽는 것이고 마지막 읽기는 마지막 읽기의 헤더를 읽는 것입니다. 매개변수 패키지. 따라서 nginx가 전송하는 데이터는 8+8+8+856+8=896바이트여야 합니다(아래 nginx의 전송 바이트에 해당할 수 있음). 포스트 모드인 경우 stdin 데이터 패킷도 전송됩니다.
3. php 프로그램에서 sleep(20)을 20초로 설정하면 그 이후에는 프로세스가 종료되므로 더 이상은 발생하지 않습니다. strace 프로그램도 종료되었습니다.
nginx:
1. 브라우저에 대한 요청을 수락합니다. 브라우저의 포트는 56434, IP는 192.168.1.105, 설정된 연결의 fd는 3입니다.
2. fd3, http 프로토콜에서 데이터를 받습니다.
3. PHP와 연결을 설정하기 위해 소켓 fd21을 만듭니다.
4. fd21에 연결하면 연결이 로컬 머신의 9000 포트임을 알 수 있습니다. 여기서 nginx와 php-fpm은 ip 소켓 연결 방법을 사용합니다. nginx와 php-fpm이 하나의 머신에 배포되면 unix 도메인 소켓입니다. 고려될 수 있다.
5. fast-cgi 프로토콜 형식인 fd21에 데이터를 작성하면 작성된 길이가 896이며 이는 위의 PHP에서 수신한 길이에 해당합니다.
6. recvfrom 함수는 fd21에서 ennreset(피어에 의한 연결 재설정)을 반환합니다.
7. fd9가 nginx 오류 로그의 파일 설명자임을 추론할 수 있습니다.
8.fd21과의 연결을 종료합니다.
9. fd3에 502 bad Gateway를 작성하는데, 이는 브라우저에 반환되는 정보입니다.
10. fd8에 액세스 로그를 작성합니다. fd8이 nginx 액세스 로그의 파일 설명자임을 유추할 수 있습니다.
nginx 액세스 로그와 오류 로그의 추론을 검증해 보겠습니다. 실제로 fd8, fd9 및 쓰기 모드임을 알 수 있습니다.
그런 다음 이 프로세스에서 전체 네트워크 패킷의 전송을 살펴볼 수도 있습니다.
tcpdump를 통해 패킷을 캡처합니다. 이를 보려면 아티팩트를 사용하는 것이 더 편리합니다.
nginx와 php 사이의 통신만 보고 싶고 위에서 nginx의 포트가 47039라는 것을 알고 있기 때문에 tcp.srcport==47039를 통해 해당 패키지를 필터링할 수 있습니다.
nginx와 php-fpm 사이의 데이터 상호 작용 과정을 볼 수 있습니다. 47039->9000은 3방향 핸드셰이크를 설정한 다음 9000으로 데이터를 보내고, 3초 후에 ack로 9000개의 응답, 첫 번째로 9000개의 응답을 보냅니다. . 아무 잘못 없음.
참고:
syn, fin은 각각 시퀀스 번호를 차지합니다.
ack, rst는 시퀀스 번호를 차지하지 않습니다(두 패키지 28과 29의 reqnum과 acknum은 동일합니다)
시퀀스 번호는 1씩 추가됩니다. 각 바이트에 대해(29개 패킷은 896바이트를 전송하고, 29개 패킷의 seq는 4219146879이고, 30개 패킷의 ack는 4219147775이며, 이는 정확히 896의 차이입니다.)
rst에는 응답이 필요하지 않습니다.
위 내용은 nginx+php-fpm 서비스 HTTP 상태 코드 502를 해결하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!