Php 进程异常退出导致 502 的问题排查
502 是什么?
The server, while acting as a gateway or proxy, received an invaild response from the upstream server it accessed in attempting to fulfill the request.
从概念上来看:上游服务无响应导致 502,那什么是上游服务?以 LNMP 架构为例,MySQL 是 Php 的上游服务,Php 是 Nginx 的上游服务。一般情况下,发生 502 都不是 Nginx 的问题,问题主要集中在上游服务 Php 中,进程数的设置、内存设置、超时设置等都可能会导致 502。
我们可以先看看整个请求流程中,哪里出问题可能会出现 502?

从路径上来看,发生 502 有两大类场景:
- Nginx 服务不可用;负载均衡通过健康检查发现上游服务不可用,会直接返回 502,不会转发请求到后端服务
- fpm 异常;Nginx 接收不到上游服务 Php 的返回,会返回 502
回到线上业务现象,只有某个特定的接口出现了 502,只要一访问就会 502,但是其他接口是好的。这样的现象就比较诡异,通常我们碰到的 502 都是整体服务不可用导致或者偶发的不同业务 502,与我们出现的现象都不一样。那么我们如何定位呢?
-
从主要路径排查,排查路径
- 根据现象,我们可以首先排除 Nginx 的问题
- 其次是 fpm 的问题,因为之前线上没有出现配置导致 502 的问题,并且其他的接口访问都是正常的,与我们的场景不一致,也可以排除是 fpm 配置的问题
- Nginx 与 fpm 配置均无更新,无异常, ECS实例也没有任何异常,确认了运行环境没有任何变动与问题存在,这个时候我们通常可以猜测是代码的问题
我们猜测
代码的问题导致了 fpm 进程异常退出,从而导致特定接口出现了 502,但其他接口都是好的。那如何验证我们的这个猜测呢? -
看日志
fpm 进程异常退出,一般情况下都是有日志输出的,首先我们可以查看 fpm 日志:
WARNING: [pool www] child 30296 exited on signal 11 (SIGSEGV) after 136.678208 seconds from start
从 fpm 日志可以看到进程是执行了一个
无效的内存引用而退出的。我们可以继续查看/var/log/message日志,确认一下原因:
发生 502 的时候都有对应的日志,从日志信息中可以看到是
内存访问越界导致的。/var/log/message日志中 error 后面的数字是比较有用的,上面的 4,转换成二进制就是 100,可以根据以下解释来查看具体含义。bit2: 值为 1 表示是用户态程序内存访问越界,值为 0 表示内核态程序内存访问越界
bit1: 值为 1 表示是写操作导致内存访问越界,值为 0 表示读操作导致内存访问越界
bit0: 值为 1 表示没有足够的权限访问非法地址的内容,值为 0 表示访问的非法地址根本没有对应的页面,也就是无效地址
所以,出现问题的根本原因就是
用户态度读取的内存地址无效。结合 fpm 的日志,我们可以通过以下两种方式来定位具体问题所在:
-
直接看代码;因为是特定的接口,我们只需要查看该接口的代码。因为是新代码上线后导致的问题,这样看变更代码可以更快速的定位问题。我们遇到的问题是yaf框架的问题,复现代码及环境如下:
CentOS Linux release 7.6.1810(core)
Php 7.1.12
Yaf 3.0.7
<?php class Test502Controller extends Controller { public function testRunAction() { $productId = $_REQUEST['product_id']; $params = [ 'timestamp' => time(), ]; $this->mergeArr($params, $_REQUEST); // 此处使用$this->post()或$_REQUEST是正常的,不会发生502 $regionId = $this->request('region_id', 0); var_dump($productId, $regionId); } private function mergeArr(array $params = [], array &$merger = []):void { $data = &$merger; $data = array_merge($data, $params); } } -
core dump;适用于偶发性的不确定的进程异常退出,我们可以从core文件中看到整个调用,进而定位到问题所在
-