Skip to content

Latest commit

 

History

History
131 lines (75 loc) · 5.65 KB

记一次Bug排查(Spider).md

File metadata and controls

131 lines (75 loc) · 5.65 KB
title date
记一次Bug排查(Spider)
2017/02/21 14:47:11

0、写在之前

Spider是什么?

Spider是基于Express框架结合socket.io(现已切换为ws)和Echarts2实现的即时性能监视工具。整体结构为前后端分离,通过express的static中间件来处理静态请求,Express本身则处理api请求。

用法上,只需要按照指定的格式post数据给Spider,那么当请求Spider展示时,将会自动将数据展示为图表。

项目地址:http://trgit2/backend_framework/spider

展示地址:http://developer.newegg.org/spider

Spider能做什么?

Spider可以提供一段时间(最大30分钟)内特定操作的消耗时间统计图(散点图)。大概展示效果如下图:

散点图

1、问题初现

该项目从15年3月发布,由于用户量较少,一直稳定运行。

近期,MPS团队使用它来监控业务请求各个阶段的耗时情况,用于定位性能问题。此时,问题出现,spider运行一段时间(10~30分钟)后将会消耗大量服务器内存,导致内存不足而崩溃。

此时,用户使用场景:

  1. 在c#程序中,收集各种类型操作的耗时数据,累计起来。
  2. 每隔1s将数据提交到spider后端。
  3. 打开监控页面,查看数据(可能会有多个监控端)

spider处理过程:

  1. 接收到请求数据之后,将数据进行处理,并存入内存;
  2. 如果发现有监控端运行,那么推送数据给监控端(每次收到请求数据都会触发一次推送)
  3. 监控端根据最新的数据绘制图表展示。

2、问题分析

猜想1:Node代码写法问题,导致内存泄露

因为JS的闭包容易内存泄露,首先怀疑代码写法问题。

**验证:**经过仔细排查代码,并对有可能有泄露代码进行改写

**结果:**问题未解决,未缓解

猜想2:缓存数据的代码有内存泄露

由于客户端提交的数据是直接写入内存的,而刚好又是消耗内存,那么开始怀疑缓存数据的代码。

**验证:**重写了数据缓存代码(参考正常运行的缓存代码实现)

**结果:**问题未解决,未缓解

此时,把最常见的情况都验证了,无解。思绪中断,接着做了哪些事情呢?

  1. 查找有可能导致JS内存泄露的情况,一一确认。
  2. 安装JS的内存分析工具(条件苛刻,未安装成功)。
  3. 安装Node程序的监控包,进行大量本地测试(很难复现问题)。
  4. 此时陷入困境...
  5. 再次想方设法安装内存分析工具,在本地测试,未果(问题很难复现)。
  6. 线上测试,问题依旧,根据结果,发现大量字符串占用内存,导致内存不足。
  7. 分析代码,查找该字符串初现的位置。
  8. 开始新的猜测

###猜想3:socket.io内存泄露

从内存分析的结果来看,大量字符串占用了内存,该格式的字符串,是由socket.io分发到客户端的时候产生的。所以怀疑到了socket.io头上。

通过查资料,发现socket.io确实有内存泄露的前科,一度认为这就是原因。

**验证1:**升级socket.io到最新的版本

**结果1:**有一定的好转,但不明显(坚持的时间稍微久一点,内存偶尔能回收一下)

再次各种查资料,发现有提到强制node使用gc回收,可以处理非托管内存。

**验证2:**增加gc回收代码,定时执行

**结果2:**基本上看起来无效,和结果1并没有太大区别

此时,有点穷途末路了。问题还在,继续探索

**验证3:**考虑到项目本身不大,重新使用了一个新的WebSocket(ws)包来推送数据

**结果3:**有明显好转,坚持的时间更长了(一度认为解决了问题)

可是好景不长,较长时间(几小时)后,问题再次出现

猜想4:再度怀疑缓存代码

此时未直接验证,切换到业界一个比较流行的缓存库(lru-cache)。

猜想5:每次提交数据都会触发一次推送,推送频率较高,导致数据阻塞

这个猜想,算来后面总结的。一开始怀疑过这个问题,想过降低推送频率,但没有实施。经@James分析也猜测问题可能会出现在此处,因为大量字符串占用内存,也满足这个猜想。想到就做:

**验证:**将每次收到数据触发推送,修改为间隔一定时间(5s)推送一次。

**结果:**内存趋于平稳,及时性在接收范围内。

至此,问题基本上算是解决了。

3、Bug分析

此次问题,是由于服务端推送数据量较大,而且频率较高,上次推送未完成,就触发了下一次推送。导致消息阻塞,越积越多,所以就耗尽内存而亡。

4、后续

之后,spider运行一天多之后死掉过一次,但没有看到具体原因。重启之后,稳定运行至今。暂时性怀疑中途死掉的一次为意外(或者瞬时流量大增)

5、总结

从发现该问题,到解决该问题,耗时大概在一周。虽然期间还处理了其他任务,但整体来说解决这个问题还是消耗了很长的时间。

究其原因:

  1. 对TCP底层传输数据不太熟悉
  2. 没有处理大量数据高频率推送的经验
  3. 发布时,只对程序进行了简单的本地测试,没有进行压力测试。

学到了什么:

  1. 对代码负责,以更严谨的态度来对待,减少出问题
  2. 对WebSocket的负载和相关设计有了更深刻的理解
  3. 多做一些测试,更稳定的保持代码质量