사업 배경
프레임워크 및 해당 환경
laravel5.7, mysql5.7, redis5, nginx1.15
센토스 7.5 bbr
docker, docker-compose
Alibaba Cloud 4C 및 8G
Problem background
php는 opcache를 활성화했고, laravel은 최적화를 위해 최적화 명령도 실행했으며, 작곡가는 dump-autoload 명령도 수행했습니다.
먼저 언급해야 할 점은 시스템 환경에 작은 문제가 있을 것임에 틀림없지만(이렇게 큰 성능을 문제 없이 개선하는 것은 불가능합니다) 이러한 문제는 적절한 도구를 사용하지 않으면 해결되지 않을 수 있습니다.
이 기사에서는 이러한 문제를 찾는 방법과 문제를 찾는 아이디어에 중점을 둡니다.
먼저 문제를 증폭시키기 위해 시스템에서 적합한 API나 기능을 찾습니다.
이 API는 원래 다음을 수행하도록 설계되었습니다. nginx 로드 밸런싱에 대한 상태 확인. 스트레스 테스트를 위해 ab -n 100000 -c 1000 을 사용하면 qps가 초당 140번만 도달할 수 있음을 알 수 있습니다.
우리는 Laravel의 성능이 악명 높다는 것을 알고 있지만 API 작성을 보면 이 정도는 아닙니다. 그래서 알아보기로 했습니다.
public function getActivateStatus() { try { $result = \DB::select('select 1'); $key = 1; if ($result[0]->$key !== 1) { throw new \Exception("mysql 检查失败"); } } catch (\Exception $exception) { \Log::critical("数据库连接失败: {$exception->getMessage()}", $exception->getTrace()); return \response(null, 500); } try { Cache::getRedis()->connection()->exists("1"); } catch (\Exception $exception) { \Log::critical("缓存连接失败: {$exception->getMessage()}", $exception->getTrace()); return \response(null, 500); } return \response(null, 204); }
문제 발현 및 문제 해결 아이디어
top
top 명령으로 시스템 CPU가 100%를 점유하고 있음을 발견했습니다. 그 중 사용자 모드가 80%를 차지하고 커널 모드가 20%를 차지하는데, 이상하게 보이는 부분이 하나 있습니다. top 명령
을 실행한 결과 일부 php-fpm 프로세스가 절전 상태에 있지만 CPU 사용량은 여전히 거의 30%에 이릅니다. 프로세스가 Sleep 상태에 있어도 여전히 CPU를 많이 점유합니다. 프로세스에 문제가 있는지 의심하지 마세요. Ttop 명령의 맨페이지를 살펴보겠습니다.
%CPU -- CPU usage The task's share of the elapsed CPU time since the last screen update, expressed as a percentage of total CPU time.
대략 이런 의미입니다. 점유는 화면을 마지막으로 새로 고칠 때, 프로세스 CPU 점유입니다. (화면이 새로고침되는 순간) some php-fpm 프로세스가 절전 상태이므로 이해할 수 있으므로 php-fpm에는 문제가 없습니다.
pidstat
먼저 php-fpm 프로세스를 선택하고, 그런 다음 pidstat를 사용하여 프로세스의 자세한 실행 상태를 확인하세요
프로세스 중에 아무 것도 발견되지 않았습니다. 이상할 것도 없고 결과는 기본적으로 상단 명령과 동일합니다.
vmstat
계속 압력을 측정하고 실행하세요. vmstate를 확인해 보니 컨텍스트 스위치(context switch)가 좀 높은 것 빼고는 별다른 이상은 보이지 않습니다. 우리가 사용하는 docker, redis, mysql이 모두 동일한 머신에서 실행되기 때문에 CS는 7,000 정도가 여전히 합리적인 범위이지만 IN(중단)이 약간 높아 약 14,000에 도달합니다.
인터럽트에는 하드 인터럽트와 소프트 인터럽트가 포함된다는 것을 알고 있습니다. 하드 인터럽트는 네트워크 카드 및 마우스와 같은 하드웨어에 의해 발생하며 CPU는 수행 중인 작업을 즉시 중지하고 인터럽트 신호를 처리합니다.
vmstat와 pidstat는 모두 새로운 감지 도구일 뿐이며 특정 인터럽트를 발생한 사람을 확인할 수 없습니다. 읽기 전용 파일인 /proc/interrupts에서 시스템의 인터럽트 정보를 읽어 인터럽트 증가의 정확한 원인을 파악합니다. watch -d 명령을 사용하여 가장 자주 변경되는 인터럽트를 확인합니다.
watch -d cat /proc/interrupts
재스케줄링 인터럽트는 가장 빠르게 변경됩니다. 이는 재스케줄링 인터럽트(RES)입니다. 이 인터럽트 유형은 유휴 CPU를 깨워 실행할 새 작업을 예약하는 것을 의미합니다. 이는 다중 프로세서 시스템(SMP)의 여러 CPU에 작업을 분배하기 위해 스케줄러가 사용하는 메커니즘으로, 일반적으로 IPI(Inter-Processor Interrupt)라고도 합니다. vmstat의 명령을 결합하면 QPS가 낮은 이유 중 하나가 CPU를 놓고 경쟁하는 너무 많은 프로세스로 인해 발생한다는 것을 확인할 수 있습니다. 아직은 그것이 무엇인지 확실하지 않으므로 추가 조사가 필요합니다.
strace
strace는 시스템 호출을 볼 수 있습니다. 시스템 호출을 사용하면 이 프로세스가 소프트 인터럽트를 생성한다는 것을 알 수 있습니다. php-fpm의 시스템 호출을 보면 우리의 추측을 확인할 수 있습니다.
果然, 发现大量的stat系统调用, 我们猜想, 是opcache在检查文件是否过期导致的. 我们通过修改opcache的配置, 让opcache更少的检查文件timestamp, 减少这种系统调用
opcache.validate_timestamps="60" opcache.revalidate_freq="0"
再次执行ab命令进行压测
果然qps直接涨到了205, 提升非常明显, 有接近 46% 的提升
perf
现在任然不满足这个性能, 希望在更多地方找到突破口. 通过
perf record -g perf report -g
看到系统的分析报告
我们看到, 好像这里面有太多tcp建立相关的系统调用(具体是不是我还不清楚, 请大神指正, 但是看到send, ip, tcp啥的我就怀疑可能是tcp/ip相关的问题).
我们怀疑两种情况
与mysql, redis重复大量的建立TCP连接, 消耗资源
大量请求带来的tcp连接
先说第一个, 经过检查, 发现数据库连接使用了php-fpm的连接池, 但是redis连接没有, redis用的predis, 这个是一个纯PHP实现, 性能不高, 换成了phpredis:
打开laravel的config/database.php文件, 修改redis的driver为phpredis, 确保本机已安装php的redis扩展. 另外由于Laravel自己封装了一个Redis门面, 而恰好redis扩展带来的对象名也叫Redis. 所以需要修改Laravel的Redis门面为其他名字, 如RedisL5.
再次进行压测
达到了喜人的286qps, 虽然和其他主打高性能的框架或者原生php比, 还有很高的提升空间(比如Swoole), 但是最终达到了104%的提升, 还是很有意义的
总结
我们通过top, 发现系统CPU占用高, 且发现确实是php-fpm进程占用了CPU资源, 判断系统瓶颈来自于PHP.
接着我们通过pidstat, vmstat发现压测过程中, 出现了大量的系统中断, 并通过 watch -d cat /proc/interrupts 发现主要的中断来自于重调度中断(RES)
通过strace查看具体的系统调用, 发现大量的系统调用来自于stat, 猜测可能是opcache频繁的检查时间戳, 判断文件修改. 通过修改配置项, 达到了46%的性能提升
最后再通过perf, 查看函数调用栈, 分析得到, 可能是大量的与redis的TCP连接带来不必要的资源消耗. 通过安装redis扩展, 以及使用phpredis来驱动Laravel的redis缓存, 提升性能, 达到了又一次近50%的性能提升.
最终我们完成了我们的性能提升104%的目标
推荐教程:网站高并发架设基础教程
위 내용은 PHP 동시성 성능 튜닝 실습(성능 104% 향상)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!