我有一个使用模块stats
的NodeJS应用程序。 stats
模块旨在跟踪基本上只是速率:每秒请求数等等。在我尝试将此代码投入生产之前,这一切看起来都相当简单。在部署环境中,此代码泄漏,并且非常糟糕。删除此代码并且泄漏消失。我很困惑为什么会发生这种情况,因为没有明显的内存泄漏给我。请记住,我是一个不错的新手,所以这里可能会有一些细微差别,因为我与内存分配和回收有关。
这是统计模块:
// stats.js
const meters = {};
class ExponentiallyWeightedMovingAverage {
constructor(timePeriod = 1000 * 60, tickInterval = 1000 * 5) {
this.timePeriod = timePeriod;
this.tickInterval = tickInterval;
this.alpha = 1 - Math.exp(-this.tickInterval / this.timePeriod);
this.count = 0;
this.rate = 0;
this.update = this.update.bind(this);
this.tick = this.tick.bind(this);
}
update(n) {
this.count += n;
}
tick() {
const instantRate = this.count / this.tickInterval;
this.count = 0;
this.rate += (this.alpha * (instantRate - this.rate));
}
getRate(timeUnit) {
return this.rate * timeUnit;
}
}
function meter(name) {
if (name in meters) {
return meters[name];
}
const rateUnit = 1000;
const tickInterval = 5 * 1000;
const m1Rate = new ExponentiallyWeightedMovingAverage(1 * 60 * 1000, tickInterval);
const m5Rate = new ExponentiallyWeightedMovingAverage(5 * 60 * 1000, tickInterval);
const m15Rate = new ExponentiallyWeightedMovingAverage(15 * 60 * 1000, tickInterval);
let count = 0;
function mark(n = 1) {
count += n;
m1Rate.update(n);
m5Rate.update(n);
m15Rate.update(n);
}
function tick() {
m1Rate.tick();
m5Rate.tick();
m15Rate.tick();
}
setInterval(tick, tickInterval);
function meterToJSON() {
return {
count,
'1MinuteRate': m1Rate.getRate(rateUnit),
'5MinuteRate': m5Rate.getRate(rateUnit),
'15MinuteRate': m15Rate.getRate(rateUnit),
};
}
meters[name] = { mark, toJSON: meterToJSON };
return meters[name];
}
function toJSON() {
const objs = {};
Object.entries(meters).forEach(([name, storedMeter]) => {
const obj = {};
obj[name] = storedMeter.toJSON();
Object.assign(objs, obj);
});
return objs;
}
const global = { meter, toJSON };
module.exports = { global };
现在,这在我的Node应用程序中的Connect中间件中使用。基本上是这样的:
// middleware/collect-stats.js
import stats from '../stats';
...
const host = 'www.example.com';
stats.global.meter(`originLatency${host}`).mark(time);
稍后在中间件链中再次出现:
// middleware/proxy-req.js
import stats from '../stats';
...
const currentStats = stats.global.toJSON();
const latency = currentStats['originLatencywww.example.com'];
console.log(latency['1MinuteRate']);
我在这里简化了延迟实际消耗的方式,但这是所发生的事情的要点。
此代码是否有任何泄漏原因?为什么这会最大化我的EC2实例上的内存,导致它们最终崩溃?
请注意,在生产中,它通过FROM节点:7.4(即节点7.4)在Docker容器中的EC2上运行。 (7.6的泄漏情况要差得多。)