开头语
- 过了三年,终于我也从看别人写这种文章,到自己写下经验来分享,很有感触
- 虽然负责的项目的模块的大小远远不及网络上的众位,但对我来说还是一个很宝贵的经验和历程。
-
所以这篇的题目叫做 从 50s到250ms
,记录我的第一个
PHP
项目的优化过程,实际上很简单,一般人大概都会想到,但还是需要记录。
项目
- 整个事情的开始,是一个数据报表类型的项目,前端需要一个下载接口
-
这个接口需要能够将数据导出成
xlsx
格式,进行下载。
- 看起来十分简单的事情,我也是这么觉得的,但是过程中着实遇到了一些坑,又因为自己是个新手,所以解决起来也是磕磕绊绊的。
- 这个接口在进行联调的时候,发现响应实在是太慢了,并且对于某些数据量大的(8000)条左右,竟然直接超时 504 报错。
-
这让我这个初次上手的人表示迷茫, 超时?
第一次,我首先怀疑是不是浏览器直接断开了连接,但是盘查了一下,发现并不是浏览器的问题,紧接着我抱着怀疑的态度,看了一下
php-fpm
日志,果然有一个超时的错误,竟然是 PHP
的超时!真是我写静态语言闻所未闻的现象。
-
经过查阅,发现这是一个默认设定,默认 30s超时
,我就想总算找到问题所在了,网上提供的做法当然就是很直接暴力
set_time_limit
一个更高的限制就行了
- 我在此处顿了一下,因为这个系统并非只有我一个人使用,如果直接修改这个限制,很可能会影响到别人!
-
但是后来我想先把服务跑通再说,于是我加了一对操作:
set_time_limit(60), set_time_limit(30)
。
-
行啦,这下可以了,再联调,又发现了一个新问题。接口跑着跑着,莫名其妙的就返回 500 Server Internal Error
!
-
不信邪的我又跑去
php-fpm
查了日志,发现这回的 fatal error
变成了 内存超出限制
!
-
当时我就不好了,这是什么鬼,还能超出限制啦?而且才 126M
左右。思考了一下代码,觉得问题应该是出在从数据库抽数据时候,是一次性把所有数据都抽出来,并且用 PHPExcel
全部写入的原因,但是由于找不到 PHPExcel
的部分写功能,放弃了将数据库改为部分读的逻辑模式。
-
虽惊然卵,Bug还是要继续修的,一查,又是
PHP
这个家伙搞的鬼,有一个默认内存限制!不出所料,网上给出的又是很暴力的解决办法, init_set
,当时我有过一丝犹豫,但还是照着做了
-
终于,又和前端联调了一下,再经过 50s
的的漫长煎熬,终于这个文件姗姗地从服务器吐了出来。
- 我稍微松了一口气,因为我知道事情没完,谁能受得了点击一个下载按钮,等了50s才开始下载啊!
办法
-
交代了始末,终于可以开始进入正题了:
-
下载接口需要50秒才能准备好数据( 取数据,处理数据,写入xlsx
)。
- 数据都是历史数据的统计。
- 当月消息是每天一更新。
- 以上的事实是没有办法改变了。那么我当时便想,是不是可以在用户来访一次,留下缓存,后面再次访问的时候,就不必重新做一次相同的动作了?
- 于是我在每次进入数据源之前,加了一层处理,如果有缓存,就检查缓存的有效性,如果有效则直接吐数据给用户,这样大大节省了时间。
- 这个功能,的确是很好写,实际上想到这里,我就差不多都写好了。
- 因为这么一来,我的访问时间,除了每天第一次访问这个接口,当天其他任意访问都将直接取缓存,并且还有一个好处就是前面说的,强行修改限制的隐患被大大减小的发生的可能,甚至是不会发生。
-
但是还有一个问题,最根本的问题,那就是,第一次访问这个接口的用户, 还是要等50s
。
-
想着想着,突然想到,可以用
cronTab
跑定时任务,每天在数据源更新之后,进行 curl
模拟浏览器主动缓存,这样就好啦!
- 终于看到了大功告成的希望,于是按照思路写了一个脚本,跑了起来!
解决
-
哈,隔天过来检查,运行的十分良好!下载接口保持在
250ms
地响应速度
-
至此从最开始的
50s
降到现在的 250ms
这之间的差距有惊人的 200
倍提升,而且大大降低了服务器的资源消耗。
结束语
- 麻雀虽小,五脏俱全
- 全方面地考虑这个事情,将每个问题都详细的讲述出来,一定能够找到更好的方案。
原文
http://www.wushxin.top/2016/08/15/从50s到250ms.html