0

我正在尝试使用 nodejs 将来自外部 API 的数据保存到 mongodb 中。该脚本对我来说感觉非常轻巧,但由于某种原因它使用了大量 RAM(来自top):

 PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND 
 2626 root      20   0  756m 113m 7148 S  6.5 11.4   3:11.74 nodejs  

这是脚本在伪代码中所做的:

each 5 seconds
  fetch 3 JSON lists through an API
    for all new items in list
      store in mongo

[编辑] JSON 列表是 aprox。每个 10kb。所以我认为在处理这些项目之前,它与将其保存在内存中无关。[/编辑]

(轻)依赖项是:

  • 请求参数
  • https
  • 下划线
  • mongodb(本机客户端)
  • 片刻

我把它写成简单的函数,返回时它们应该归还他们使用的所有内存,对吗?

这是整个脚本:

var querystring = require("querystring");
var https = require('https');

var fetch = function(cur, callback) {
  cur = cur.toLowerCase().replace('/', '_');
  var options = {
    host: 'data.fxbtc.com',
    path: '/api?op=query_last_trades&count=100&symbol=' + cur,
    method: 'GET',
    headers: {
      'User-Agent': 'Mozilla/4.0 (compatible; node.js client)'
    }
  };

  var req = https.request(options, function(res) {
    res.setEncoding('utf8');
    var buffer = '';
    res.on('data', function(data) {
      buffer += data;
    });
    res.on('end', function() {
      try {
        var json = JSON.parse(buffer);
      } catch (err) {
        return callback(err);
      }
      callback(null, json);
    });
  });
  req.end();
}

var currencies = [
  'BTC/CNY',
  'LTC/CNY',
  'LTC/BTC'
];

var LAST_TRADE = {
  'BTC/CNY': 0,
  'LTC/CNY': 0,
  'LTC/BTC': 0
}

var _ = require('underscore');
var mongo = require('mongodb');
var moment = require('moment');

var init = function(next) {
  mongo.connect('mongodb://127.0.0.1:27017/coindata', next);
}

var now = function() {
  return moment().format('YYYY-MM-DD HH:mm:ss');
}

console.log(now(), 'STARTING');

setInterval(function() {
  console.log(now(), 'alive')
}, 60000)

var collections = {};

var forever = function(err, db) {
  if(err) throw err;

  _.each(currencies, function(cur, i) {
    collections[cur] = db.collection('fxbtc_' + cur);
    collections[cur].ensureIndex({fid: 1}, {unique: true}, console.log);

    setTimeout(function() {
      console.log(now(), 'registering', cur);
      setInterval(check(cur), 5 * 1000);
    }, i * 1000);
  });
}

var check = function(cur) {
  return function() {
    fetch(cur, function(err, trades) {
      if(err) return console.log(now(), 'ERROR-FETCH', err);

      trades = _.map(trades.datas, function(trade) {
        return {
          date: new Date(trade.date * 1000),
          price: parseFloat(trade.rate),
          amount: parseFloat(trade.vol),
          fid: parseInt(trade.ticket)
        }
      });

      trades = _.filter(trades, function(trade) {
        return trade.fid > LAST_TRADE[cur];
      });

      var fids = _.pluck(trades, 'fid');
      fids.push(LAST_TRADE[cur]);

      LAST_TRADE[cur] = _.max(fids);

      if(!trades.length)
        return;

      console.log(now(), 'storing:', trades.length, 'in', cur);

      collections[cur].insert(trades, function(err, docs) {
        if(err && err.code !== 11000) console.log(now(), 'ERROR-STORE', err);
      });

    });
  }
}


init(forever);

此脚本中是否有任何明显的内存泄漏?如何找到所有已用内存的来源?

4

1 回答 1

0

我正在进行的项目是轮询许多不同的 API 服务(15+)并存储所有最新的更改。

我最初的想法是为每个不同的服务编写一个小脚本,其中有一个应该永远保持的循环。问题(如上所述)是每个服务的内存会以某种方式增长到 40 - 120mb(取决于几件事),并且我的系统会耗尽 RAM。

这就是我现在解决的方法:

我没有让每个服务的进程保持活动状态,而是将所有脚本重写为只运行一次,并编写了一个主脚本,负责在 x 时间后运行每个服务脚本的每个脚本:

var cp = require('child_process');
var moment = require('moment');

var i = 0;

var watch = function(options) {
  i++;
  setTimeout(function() {
    var fid = 0;
    setInterval(function() {
      var worker = cp.fork('./process_' + options.exchange + '.js');
      worker.send(fid);
      worker.once('message', function(new_fid) {
        fid = new_fid;
        worker.kill();
      });
    }, options.interval);  
  }, i * 3000);

}

然后我像这样注册所有不同的服务:

watch({exchange: 'bitcurex', interval: +moment.duration(9, 'minutes')});

它已经运行了 10 多个小时,或者现在几乎没有内存占用(我在顶部找不到它)。

于 2013-08-01T11:59:32.813 回答