背景
周末公司发生一个严重的问题,房源单页输出导航栏后,下面是空白的(这种情况一定是出现了 PHP Fatal error,查看 /var/log/user.log ,确实有 PHP Fatal error 错误),但是返回的HTTP code 确实 200,不是我们想象的 500。由于 HTTP 200 是一个正常的状态码,因此为我们排查错误埋下了一个大坑,导致我们不能马上发现问题。
如何诊断
发生 PHP Fatal error 时,却返回 HTTP 200 ,很难排查错误,所以这个问题一定要解决。 可以使用 strace 命令 查看 PHP 进程在这个会话期间发生了什么。
// 模拟当时的情况 <?php ini_set("display_errors",0); // $str=str_repeat("a",20000); http return 200 $str=str_repeat("a",200); // http return 500 echo $str; // 不存在的方法 foo();
当发生 http 200 的时候 使用 strace 抓取的结果,可以看到当发生 http 返回 200 的时候,PHP 已经向 Nginx 输出了前面的内容Content-type: text/html <p>aaaaaaaaaaaaaaaaaaaaaa... ,
write(4, "16137370Content-type: text/html aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 8192) = 8192 sendto(6, "Jul 20 13:14:45 ool www: PHP Fatal error: Call to undefined function foo() in /home/wbsong/projects/phptest/error.php on line 8", 132, MSG_NOSIGNAL, NULL, 0) = 132
当发生 500 的时候,使用 strace 抓取的结果,看以看出区别,PHP 向 Nginx 输出了 Status: 500 Internal Server Error 的信息。
write(4, "161162Status: 500 Internal Server Error Content-type: text/html aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa13110aaa", 288) = 288 shutdown(4, SHUT_WR)
所以总结起来,返回200 & 500 的区别就是在发生错误前,PHP 是否已经输出了 Header信息,是否输出 Header 取决于 output_buffering 这个参数的大小,一般默认是 4096。
总结
- Nginx 返回的 Http 状态是PHP 决定的(200 的时候,PHP 不用明确指出)
- 当PHP 发现缓冲区满了以后,就要向 Nginx 输出,第一次会输出 使用 header 函数设置的内容。
- 之后如果设置 Header 是没有用的 (HTTP 协议里面规定,Header 只能出现在最前面)
- 发生严重错误时,PHP 只能打印错误,并且向Nginx 发生终止请求(页面就会出现不全的情况)
- 之前很少看到这种情况的原因是,错误绝大部分是发生在 Controller 层面,这个时候PHP 还没有向 Nginx 发出任何数据,所以 PHP 是可以通知 Nginx 发生了什么事情,而这次事故是 Compontent 渲染的时候发生的错误,已经有很多的输出。
- 解决的办法最简单的方式就是调整 output_buffering ,调整成一个比较大的值,或者改代码使用 ob_start() + ob_get_clean() + error_get_last() 来解决
注
output_buffering 这个参数一定要在 php.ini 里面设置,通过 ini_set 是没有效果的