暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

PHP进程异常退出导致502的问题排查

拖地先生 2020-08-15
1498

温旭峰好文:

PHP7升级的性能提升

五个套路帮你搞定慢接口

PhpStorm使用技巧

Go Module 在项目中的应用

Go语言开发规范(更新)

自定义监控阿里云RDS - Prometheus使用Golang自定义Exporter

Golang HTTP服务平滑重启及升级

使用openresty打造接口校验服务

maxRt——看看你的Redis有没有慢查询


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有两大类场景:

  1. nginx服务不可用;负载均衡通过健康检查发现上游服务不可用,会直接返回502,不会转发请求到后端服务

  2. fpm异常;Nginx接收不到上游服务(php)的返回,会返回502


回到线上业务现象,只有某个特定的接口出现了502,只要一访问就会502,但是其他接口是好的。这样的现象就比较诡异,通常我们碰到的502都是整体服务不可用导致或者偶发的不同业务502,与我们出现的现象都不一样。那么我们如何定位呢?


从主要路径排查

排查路径:

  1. 根据现象,我们可以首先排除Nginx的问题

  2. 其次是php-fpm的问题,因为之前线上没有出现配置导致502的问题,并且其他的接口都是正常的,与我们的场景不一致,也可以排除php-fpm配置的问题

  3. Nginx与fpm配置均无异常,ECS也没有异常,那么我们可以猜测是代码的问题。

我们猜测是 代码的问题导致了fpm进程异常退出 ,从而导致特定接口出现了502,但其他接口都是好的。那如何验证这个猜测呢?

看日志

进程异常退出,一般都有日志打印

首先查看php-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的日志,我们可以通过以下两种方式来定位具体问题所在:

  1. 直接看代码;因为是特定接口我们只需要查看接口的代码,因为是新代码上线后导致的问题,这样看变更代码可以快速定位到问题。我们遇到的问题是yaf框架的问题,附有详细的测试环境与测试代码,可自行尝试复现并使用core dump分析 

  2. Core dump;适用于偶发性的不确定的进程异常退出,我们可以从core文件中看到整个调用,进而定位到问题所在

附:

测试环境

  • 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);
}
}



拖地先生,从事互联网技术工作,在这里每周两篇文章,聊聊日常的实践和心得。往期推荐:

说说这个公众号

平均响应1000ms到200ms,PHP和Go那家强?

崩溃率从1%到0.02%,iOS稳定性解决之道

七招优化Android包体减少30%

技术产品职业瓶颈?29份腾讯通道材料教你成长

低头赶路,也别忘了抬头看天

加班能解决交付的期望么?

如果对你有帮助,让大家也看看呗~

文章转载自拖地先生,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论