本文发表于 北斗同构github, 转载请注明出处
注: 本文为第12届D2前端技术论坛《打造高可靠与高性能的React同构解决方案》分享内容,已通过数据脱敏处理。javascript
菜鸟物流大市场是菜鸟旗下的一条业务线,能够简单地理解为物流领域的淘宝,是为撮合物流需求方和物流提供方搭建的一个平台。其中搜索页、详情页、买家中心等页面是基于beidou同构框架开发的。随着node、react同构等技术愈来愈普遍地使用, 内存泄漏的事情时有发生,应当引发足够的重视。最近在作菜鸟物流市场的技术支持,就“中奖”了,把实践过程当中的经验和心得整理了下,供你们参考。前端
先介绍几个基本术语:java
回到以前说到的菜鸟物流大市场node
菜鸟物流大市场上线以后,常常收到alimonitor的告警通知,以下图react
因而打开了alinode查看慢日志, 果真有很多慢日志记录git
当时主要有如下几个现象:github
根据当时的现象作了简单的分析并制定了具体的action:web
从上面的推断来看,发生内存泄漏的可能性很是大,但仍然须要经过实际数据进行验证,因而根据制定的action进行数据采集chrome
再次发布以后,采集到了数据:api
从上图中能够看出, 随着时间的推移,进程1694的hsf调用耗时始终稳定,可是服务端渲染的时间却逐步飙升到3700多毫秒,而后在某个临界值以后瞬间下降到50毫秒左右。多是因为某某事件( 猜想是内存泄漏引发OOM )致使了进程崩溃,接下来beidou框架会自动重启进程又恢复良好的状态。打开sandbox一看进程生命周期,果真如此, 进程1694挂了,而后从新启动了一个29649进程。
从上图中也能够看到RSS(实际使用物理内存)高达1880.93MB,至此基本上能够肯定是内存泄漏了。查看内存占用曲线,内存呈现锯齿状,先一路飙升,到达零界点以后瞬间降低,如此周而复始。和咱们的推断彻底一致,这是典型的内存泄漏曲线。
最终结论: 访问速度慢是由于内存泄漏消耗了过多的资源
定位到是内存泄漏以后,还须要进一步排查具体是什么代码致使了内存泄漏。这时候就要用到排查神器 - alinode了。
先建立堆快照:
在分析页面打开对象簇视图
, 能够看到里面有大量的Window对象, 搜索下居然高达390个
采样了几个Window对象,经过GC Root
展开,发现挂载了无数个定时器。
分析代码找到了两处定时器的设置,看代码逻辑,该定时器在服务端根本不会被释放。
componentWillMount(){ let _this = this; window.handler = window.setInterval(function(){ if(typeof AMap){ _this.renderMap('', AMap); window.clearInterval(window.handler); } }, 300); }
注释掉以后在预发验证没有再出现window相关的内存泄漏。
PS.
后来的验证发现,除了定时器的问题,还有另外两处内存泄漏,再也不赘述, 贴上其中一处(高德地图)内存泄漏的代码供读者参考
componentWillMount(){ this.createAmapScript(); } createAmapScript(){ let script = document.createElement('script'), body = document.getElementsByTagName('body')[0]; script.type = 'text/javascript'; script.src = 'https://webapi.amap.com/maps?v=1.3&key=59699a8cfee7c52f58390357cbdbf27d'; body.appendChild(script); }
从上述两处代码能够看出,定时器无需在服务端执行, 而高德地图自己就不支持服务端渲染,所以可将两者放到客服端渲染便可。根据react的特性,componentDidMount生命周期函数在服务端不会执行,所以将上述代码从componentWillMount移到componentDidMount中便可。具体修复以下:
经过loadtest在本地压测验证下:
单个进程一样以10个QPS进行施压,对比下能够看出,修复前RT时间一路上升,而修复后RT始终稳定在200毫秒左右。
再看看线上数据, 内存占用率始终稳定,没有出现飙升现象。
至此,打完收工。
看完了案例,是时候系统化地总结下方法论了。
从刚才的案例中能够看出来,内存泄漏最典型的现象就是内存占用率会随着时间的推移而逐步上升,就算没有流量了,内存占用率也不会降低。而健康的应用是流量上升内存占用会上升,而流量降低以后内存占用率就会回到原水平。
一般形成内存泄漏的有如下几个因素
本文中的案例就属于做用域未释放
本地
线上
上医治未病,中医治欲病,下医治已病
,说的是医术最高明的医生并非擅长治病的人,而是可以预防疾病的人。让问题在开发阶段就暴露出来, 而不是等到线上告警了再抢救。constructor
中作事件绑定,建议放到componentDidMount生命周期中