情景再现
近期发现网站访问变慢,经常会出现请求无法响应的问题,一个请求长时间没有返回,导致页面出现504(Gateway Timeout),我们使用的nodejs+ngnix(反向代理)。
猜测原因
服务器内存使用过高,导致服务器处理缓慢?
并发请求过多导致请求缓慢?
...
定位问题
查看服务器cpu和内存使用情况:发现服务器的cpu空闲率为95%左右,内存使用率在40%~60%。会不是内存使用过高导致的呢?我们重启了nodejs服务,此时我们观察到内存使用率有所减少在20%~40%左右,结果并没有什么用,访问依旧很慢。
是不是请求数过多导致的呢?查看服务器日志,发现每分钟的请求量级并没有特别高,应该不是这个问题。
因为我们使用了nginx反向代理,就像看看是不是nginx服务器的配置问题。经过抓包查看,发现部分请求的时间有几十毫秒还有几十秒的,当达到60秒的时候,服务器出现504错误。(proxy_connect_timeout,proxy_read_timeout,proxy_send_timeout,在nginx中,这些的默认配置是60s,所以一旦请求超过一分钟没有响应,服务器就直接返回504)再三检查nginx配置,没有发现能够改变这个问题。
查看nodejs服务,检查是否我们代码层的逻辑导致服务器没有响应,触发nginx的超时。经过对代码的测试,发现我们有一个发布上线的逻辑比较耗时,大概时间为5到6秒钟,这个过程需要依赖第三方的服务,所以处理时间长度过长,在用户发送请求时,部分用户需要经历这个过程。此时,访问其他页面的时候,就会出现这种情况。我们的做法是,将发布上线的逻辑,只在编辑的时候使用。普通用户访问的时候,使用上线生成的线上文件。你们一定觉得这样问题就可以解决了,其实这是一个导火索,与另外的一个问题同时导致了线上的pedding问题。
上面这个问题解决之后,发现向上并没有任何太大的改观。同时发现还有一个请求,依赖其他的服务器,那就是数据库请求。我们重新审查了一下什么原因导致数据库查询请求过慢。我们查看了我们所有的数据库慢查询,发现一个共性,这些慢查询的表数据量都很大,同时我们并没有对这些表建立索引,导致查询很慢。经过简单的本地测试:设置过索引的查询,在一个几十万条数据的表中,查询时间1ms以内;未设置索引的情况下,同一个查询语句需要的时间是200ms左右,结果令人差异,竟然差距这么大。我们对线上的数据库表根据查询需求,设置了索引,结果得到了很大的改观,访问速度有了显著的提高,基本上很快就有了响应。
此时,线上的问题得到了初步的解决。
思考
通过上面的探索我们发现,一般出现一个请求pedding或者是504的情况,是因为服务器处理的太慢,无法快速响应,如果服务器再不能并发处理的话,可能就会出现这种情况,所以我们需要优化我们服务端处理的速度。
针对这种情况,优化我们线上的逻辑:
优化建表逻辑:考虑主键、外键、索引等情况,优化数据库请求
优化服务端逻辑:优化发布上线逻辑,不过度依赖第三方服务,如果需要依赖的话,尽量采用子进程完成,不影响主进程的响应;与此同时,完成多服务器同步问题,不需要用户在访问时,再进行生成,导致请求阻塞。
其他性能优化:资源静态化,资源加载等等问题
参考资料:
https://nodejs.org/api/http.html#http_class_http_agent
http://melon.github.io/blog/2014/12/08/nodejs-agent-and-size-limit-of-get-method/