程序这么慢罪魁祸首竟是它
一、问题背景:
最近几天我们几位同事一起在做业务压测,TPS始终上不去,始终在100上下,平均响应时间也在5、6秒左右,性能差得可想而知,但是数据库层压测期间所有的SQL平均响应时间均在30ms以下,CPU使用率也才达到40%而已,RDS整体性能并没有达到瓶颈。
由于是PHP程序,没法用现有的压测平台进行全链路压测跟踪到具体每一步的耗时。在经历了各种linux调优、PHP参数调优以后,效果仍然没有起色,气氛一度陷入尴尬。
二、phptrace简介
其实利用之前文章《MySQL DBA如何利用strace/pstack/gdb来定位问题》中的提到的strace也可以跟踪到某个线程的全部调用情况,但是同事推荐了另外一个项目:phptrace,可以专门针对PHP程序做链路追踪,效果跟strace类似。
项目地址如下:https://github.com/Qihoo360/phptrace
它可以跟踪PHP在运行时的函数调用、请求信息、执行流程,并且提供有过滤器、统计信息、当前状态等实用功能。在任何环境下,它都能很好的定位阻塞问题以及在高负载下Debug,尤其是线上生产环境。
具有以下特性:
- 低开销,在只加载模块不开启Trace功能时对性能影响极低
- 稳定性,已经稳定运行在Qihoo 360线上服务中,并针对主流框架进行测试
- 易用性,对于未安装trace扩展的环境,也能够追踪运行状态
你可以利用如下方式来跟踪PHP程序
$ phptrace -p 3600
[pid 3600] > Me->run() called at [example.php:57]
[pid 3600] > Me->say("good night") called at [example.php:33]
[pid 3600] < Me->say("good night") = NULL called at [example.php:33] ~ 0.000s 0.000s
[pid 3600] > Me->sleep() called at [example.php:34]
[pid 3600] > Me->say("sleeping...") called at [example.php:27]
[pid 3600] < Me->say("sleeping...") = NULL called at [example.php:27] ~ 0.000s 0.000s
[pid 3600] > sleep(2) called at [example.php:28]
[pid 3600] < sleep(2) = 0 called at [example.php:28] ~ 2.000s 2.000s
[pid 3600] < Me->sleep() = NULL called at [example.php:34] ~ 2.000s 0.000s
[pid 3600] > Me->say("wake up") called at [example.php:35]
[pid 3600] < Me->say("wake up") = NULL called at [example.php:35] ~ 0.000s 0.000s
[pid 3600] < Me->run() = NULL called at [example.php:57] ~ 2.000s 0.000s
你也可以根据函数名或者url进行过滤
$ phptrace -p 3600 -f type=class,content=Me
[pid 3600]> Me->run() called at [example.php:57]
[pid 3600]> Me->say("good night") called at [example.php:33]
[pid 3600]< Me->say("good night") = NULL called at [example.php:33] ~ 0.000s 0.000s
[pid 3600]> Me->sleep() called at [example.php:34]
[pid 3600]> Me->say("sleeping...") called at [example.php:27]
[pid 3600]< Me->say("sleeping...") = NULL called at [example.php:27] ~ 0.000s 0.000s
[pid 3600]< Me->sleep() = NULL called at [example.php:34] ~ 2.000s 2.000s
[pid 3600]> Me->say("wake up") called at [example.php:35]
[pid 3600]< Me->say("wake up") = NULL called at [example.php:35] ~ 0.000s 0.000s
[pid 3600]< Me->run() = NULL called at [example.php:57] ~ 2.001s 0.000s
三、跟踪分析
在压测期间,我们利用phptrace做了跟踪,通过阅读trace日志,发现所有的调用时间基本上集中在了一个函数上,而其他部分消耗时间都很短。
[pid 22745] < file_put_contents("/var/www/cp-activity/activity/da"..., "2020-07-17 04:35:28 front_money_"..., 8) = 254 called at [/var/www/cp-activity/activity/framework/class/service/cache/fcache.class.php:140] ~ 1.338s 1.338s
经过仔细勘察,这个file_put_contents
函数的作用其实就是往nas上写日志,看到这里真的是有点内伤的感觉,写本地磁盘、写ES难道不香吗,为啥想不开写nas呢???
基于上述判断,我们在其中一台机器上注释掉了该函数的调用,然后再次做了压测(10000并发情况),压测期间监控到alive线程基本上保持在10左右,总的TPS峰值达到了接近400左右,这还是在只修改了一台的情况下的结果,相信如果6台应用全部注释掉该方法,TPS应该能够上升一个数量级(待继续验证)。
四、总结反思
- 进展到这里,问题基本上可以说非常明确了,只需要做进一步验证即可
- 那么如何来解决这个写日志慢的问题呢?最简单也是最稳妥的办法还是写本地文件,然后再通过ELK方法写入到ES做聚合统计以及日志查询等
- 再牛逼的泥工也需要一把水平尺来砌墙,所以我们要学会利用工具来帮助我们进行链路跟踪,有助于确定根因,例如
strace
、phptrace
、perf
等工具
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Ajax与Json的学习
- pytest文档48-切换环境(pytest-base-url)
- 打卡群刷题总结0826——组合总和
- 排序之简单排序
- 打卡群刷题总结0827——组合总和 II
- 快速学习-Gateway--服务网关
- 用Python写一个身份证号码校验系统
- 手摸手 Elastic Stack 使用教程 - 环境安装
- SAS-输出文档生成目录的方法
- 醒醒神,爱奇艺笔试真题
- Qt音视频开发22-通用GPU显示
- 通吃岛屿问题
- 堂妹问我:Dubbo的服务暴露过程
- LeetCode 657. 机器人能否返回原点
- ! [rejected] master -> master (fetch first)