记录性能优化的小事情-开篇

最近公司有任务,要优化一下H5页面的访问速度,所以制定了一下优化的短期计划,大概如下:

  • 1,现有代码优化,降低接口速度
  • 2,制定开发流程,防止代码继续恶化
  • 3,hhvm/php7,数据压缩,异步化,预加载,http缓存(如etag),提升端到端速度

下面是本周优化过程中遇到的各种小事情,简单罗列一下,同时说说感受吧。

碰到的问题 解决方法 总结
代码封装redis竟然没有单例,一个php生命期建立了多次连接,耗时严重 改单例 底层不做好,上层就会乱用
循环调用redis的get命令,串行请求,耗时严重 改用mget一次性获取 不要认为redis快就可以忽略网络链路上的耗时
http调用第三方服务较多,串行请求,耗时严重 curl_multi_perform改并行 改造过程涉及诸多服务sdk,所有sdk底层均基于封装的HttpClient发起RPC。

通过改造HttpClient增加并发请求开关,可以实现跨sdk的HTTP请求聚合。

但是个别sdk内部包含了业务处理逻辑,这将粘连HttpClient和业务之间的边界,阻碍上述实现。

建议:保持sdk纯粹,业务功能在sdk之上处理,分层明确,便于抽象。

调优不要盲目动手,先日志打点或者用工具分析,看到具体耗时分布后,再去针对性的优化 这是最重要的,千万不要埋头苦干
测试中实测的数据指标,mysql,redis一次访问在2ms左右,建立网络连接在2ms左右 线下测试环境网络环境差,所以对应耗时均有放大。所以在线下分析性能,最重要的还是看耗时分布,而不是关注绝对数值
发现php请求第一条sql耗时20ms(正常的是2ms),一开始怀疑sql写的不好,后来发现是连接mysql引起。

tcpdump抓包发现初始化PDO过程有多次通讯,比较怀疑。

分析代码发现yii2框架除了new PDO外还会执行一次set NAMES utf-8导致耗时增长。

实际上成熟配置的数据库一般都配置好了utf-8,不需要客户端再次指定了,当然你需要确认一下的话就去mysql里执行命令:SHOW VARIABLES LIKE ‘character%’,看一下是否有非utf-8之类的关键字出现。

最终,我改了一下common/config/main-local.php,把db配置去掉了charset项,框架就不会去调用了。

分析的时候用到了curl去分析一个tcp的连接时间:curl -o /dev/null -s -w %{time_connect} http://url.com/api

通过tcpdump tcp port xxx and host yyy抓包分析tcp交互细节也可以发现一些问题。

经过一翻优化,统计action执行时间只需要80ms-100ms,基本达到了预期,但是nginx收到php的应答时间是130-160ms。

因为php-fpm和nginx同机部署,传输response不应该这么长。

经过分析,发现yii2.0框架性能极差,除去action部分,配置加载和路由解析花费30ms左右,框架收尾花费20ms左右

框架易用通用,牺牲的就是性能,因为所有类文件均自动加载,拆分粒度小,继承层次深,所以每次加载和解释执行的php文件都很多,花费时间就会增多。

针对这个问题,引入hhvm或者opcache都会有效果,php7公司还没支持,所以考虑hhvm提升效果最大,整个30ms+20ms压缩到2ms

yii2.0的log库嵌套使用了register_shutdown_function,但是公司hhvm版本太老不支持,导致没法在退出时自动flush缓存的日志到磁盘(hhvm官方已经修复) 配置一下yii2.0的log配置,flushInterval和exportInterval都改为1,不进行日志内存缓存,每一条日志立即落盘,保证不受影响。

以后公司hhvm升级再取消相关配置,当前因为接口日志打印不多(最多1-2条),所以去除缓存的性能损耗并不显著

发现切换hhvm后打印的日志时间不对了,测试发现是UTC时间了,原来hhvm是不会去解析php.ini的,所以只能框架里去配置一下时区,在common/config/main.php中第一级配置写一下即可:”timeZone” => ‘PRC’, 切换hhvm就不会加载php.ini了,所以可能会影响一些功能点,还是需要严格回归测试 common/config/main.php中添加了”timeZone”=>”PRC”的配置来指定时区。

如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~