转载

从50s到250ms

开头语

  1. 过了三年,终于我也从看别人写这种文章,到自己写下经验来分享,很有感触
  2. 虽然负责的项目的模块的大小远远不及网络上的众位,但对我来说还是一个很宝贵的经验和历程。
  3. 所以这篇的题目叫做 从 50s到250ms ,记录我的第一个 PHP 项目的优化过程,实际上很简单,一般人大概都会想到,但还是需要记录。

项目

  1. 整个事情的开始,是一个数据报表类型的项目,前端需要一个下载接口
  2. 这个接口需要能够将数据导出成 xlsx 格式,进行下载。
  3. 看起来十分简单的事情,我也是这么觉得的,但是过程中着实遇到了一些坑,又因为自己是个新手,所以解决起来也是磕磕绊绊的。
  4. 这个接口在进行联调的时候,发现响应实在是太慢了,并且对于某些数据量大的(8000)条左右,竟然直接超时 504 报错。
  5. 这让我这个初次上手的人表示迷茫, 超时? 第一次,我首先怀疑是不是浏览器直接断开了连接,但是盘查了一下,发现并不是浏览器的问题,紧接着我抱着怀疑的态度,看了一下 php-fpm 日志,果然有一个超时的错误,竟然是 PHP 的超时!真是我写静态语言闻所未闻的现象。
  6. 经过查阅,发现这是一个默认设定,默认 30s超时 ,我就想总算找到问题所在了,网上提供的做法当然就是很直接暴力 set_time_limit 一个更高的限制就行了
  7. 我在此处顿了一下,因为这个系统并非只有我一个人使用,如果直接修改这个限制,很可能会影响到别人!
  8. 但是后来我想先把服务跑通再说,于是我加了一对操作: set_time_limit(60), set_time_limit(30)
  9. 行啦,这下可以了,再联调,又发现了一个新问题。接口跑着跑着,莫名其妙的就返回 500 Server Internal Error
  10. 不信邪的我又跑去 php-fpm 查了日志,发现这回的 fatal error 变成了 内存超出限制
  11. 当时我就不好了,这是什么鬼,还能超出限制啦?而且才 126M 左右。思考了一下代码,觉得问题应该是出在从数据库抽数据时候,是一次性把所有数据都抽出来,并且用 PHPExcel 全部写入的原因,但是由于找不到 PHPExcel 的部分写功能,放弃了将数据库改为部分读的逻辑模式。
  12. 虽惊然卵,Bug还是要继续修的,一查,又是 PHP 这个家伙搞的鬼,有一个默认内存限制!不出所料,网上给出的又是很暴力的解决办法, init_set ,当时我有过一丝犹豫,但还是照着做了
  13. 终于,又和前端联调了一下,再经过 50s 的的漫长煎熬,终于这个文件姗姗地从服务器吐了出来。
  14. 我稍微松了一口气,因为我知道事情没完,谁能受得了点击一个下载按钮,等了50s才开始下载啊!

办法

  1. 交代了始末,终于可以开始进入正题了:
    1. 下载接口需要50秒才能准备好数据( 取数据,处理数据,写入xlsx )。
    2. 数据都是历史数据的统计。
    3. 当月消息是每天一更新。
  2. 以上的事实是没有办法改变了。那么我当时便想,是不是可以在用户来访一次,留下缓存,后面再次访问的时候,就不必重新做一次相同的动作了?
  3. 于是我在每次进入数据源之前,加了一层处理,如果有缓存,就检查缓存的有效性,如果有效则直接吐数据给用户,这样大大节省了时间。
  4. 这个功能,的确是很好写,实际上想到这里,我就差不多都写好了。
  5. 因为这么一来,我的访问时间,除了每天第一次访问这个接口,当天其他任意访问都将直接取缓存,并且还有一个好处就是前面说的,强行修改限制的隐患被大大减小的发生的可能,甚至是不会发生。
  6. 但是还有一个问题,最根本的问题,那就是,第一次访问这个接口的用户, 还是要等50s
  7. 想着想着,突然想到,可以用 cronTab 跑定时任务,每天在数据源更新之后,进行 curl 模拟浏览器主动缓存,这样就好啦!
  8. 终于看到了大功告成的希望,于是按照思路写了一个脚本,跑了起来!

解决

  • 哈,隔天过来检查,运行的十分良好!下载接口保持在 250ms 地响应速度
  • 至此从最开始的 50s 降到现在的 250ms 这之间的差距有惊人的 200 倍提升,而且大大降低了服务器的资源消耗。

结束语

  • 麻雀虽小,五脏俱全
  • 全方面地考虑这个事情,将每个问题都详细的讲述出来,一定能够找到更好的方案。
原文  http://www.wushxin.top/2016/08/15/从50s到250ms.html
正文到此结束
Loading...