微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在node.js中使用redis支持的“kue”库 – 为什么我的redis内存使用量不断增加?

一个node.js应用程序中,我正在使用由redis支持的kue排队库.作业完成后,将其从队列中删除.在过夜运行约70,000个工作之后,重新启动内存使用量大约为30MB.数据库中还有18个失败的作业,队列长度当前为零 – 作业的处理速度比排队的速度快. Redis没有以任何其他方式使用.

任何想法为什么redis内存使用量不断增加,即使我删除已完成的工作?参考代码

gaemodel.update = (params) ->
  job = jobs.create "gaemodel-update",params 
  job.attempts 2
  job.save()
  job.on "complete",->
    job.remove (err) ->
      throw err if err
      console.log 'completed job #%d',job.id

解决方法

当您排队系统出现内存消耗问题时,您所有已排队的项目已从存储中删除并且不会进入异常/错误队列,您是100%积极的,那么最可能的原因是排队率远高于出库率.

Redis使用通用内存分配器(jemalloc,ptmalloc,tcmalloc等).这些分配器不一定将内存返回给系统.当一些内存被释放时,分配器往往保留它(以便将其重新用于将来的分配).当许多小对象被随机分配时,尤其如此,Redis通常是这样.

结果是在给定时间点的记忆消耗峰值将导致Redis累积记忆并保持记忆.这个内存不会丢失,如果出现内存消耗的另一个峰值,它将被重新使用.但是从系统的角度来看,内存仍然分配给Redis.对于排队系统,如果您将项目排队的速度比您能够将其排队的速度更快,则内存消耗将达到峰值.

我的建议是为了调整应用程序以定期的时间间隔获取和记录队列长度,以检查队列中项目数量的变化(并识别峰值).

更新:

我用kue测试了几件东西来了解它在Redis中的存储.实际上,数据结构是相当复杂的(混合的字符串,集合,zsets和哈希).如果您查看Redis,您会发现以下内容

q:job:nnn             (hash,job deFinition and properties)

q:search:object:nnn   (set,Metaphone tokens associated to job nnn)
q:search:word:XXXXX   (set,reverse index to support job full-text indexing)

q:jobs:inactive       (zset,all the unprocessed jobs)
q:jobs:X:inactive     (zset,all the unprocessed jobs of job type X)

q:jobs:active         (zset,all the on-going jobs)
q:jobs:X:active       (zset,all the on-going jobs of job type X)

q:jobs:complete       (zset,all the completed jobs)
q:jobs:X:complete     (zset,all the completed jobs of job type X)

q:jobs:Failed         (zset,all the Failed jobs)
q:jobs:X:Failed       (zset,all the Failed jobs of job type X)

q:jobs:delayed        (zset,all the delayed jobs)
q:jobs:X:delayed      (zset,all the delayed jobs of job type X)

q:job:types           (set,all the job types)
q:jobs                (zset,all the jobs)

q:stats:work-time     (string,work time statistic)
q:ids                 (string,job id sequence)

我根本不知道Coffeescript,所以我试图用简单的旧的Javascript来重现这个问题:

var kue = require('kue'),jobs = kue.createQueue();

jobs.process( 'email',function(job,done) {
  console.log('Processing email '+JSON.stringify(job) )
  done();
});

function create_email(i) {
  var j = jobs.create('email',{
    title: 'This is email '+i,to: 'didier',template: 'Bla bla bla'
  });
  j.on('complete',function() {
    console.log('complete email job #%d',j.id);
    j.remove(function(err){
      if (err) throw err;
      console.log('removed completed job #%d',j.id);
    });
  });
  j.save();
}

for ( i=0; i<5; ++i )
{
   create_email(i);
}

kue.app.listen(8080);

我运行了这个代码,检查在处理后剩下的Redis

redis 127.0.0.1:6379> keys *
1) "q:ids"
2) "q:jobs:complete"
3) "q:jobs:email:complete"
4) "q:stats:work-time"
5) "q:job:types"
redis 127.0.0.1:6379> zrange q:jobs:complete 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"

所以似乎完成的工作保留在q:作业:完成和q:作业:X:尽管作业被删除完成.我建议你检查这些zsets在你自己的Redis实例的基数.

我的解释是这些zset的管理发生在“完成”事件发生之后.所以作业被正确删除,但它们的id被插入到这些zsets之后.

解决方法是避免依赖每个作业的事件,而是使用每队列事件来删除作业.例如,可以进行以下修改

// added this
jobs.on('job complete',function(id) {
  console.log('Job complete '+id )  
  kue.Job.get(id,function(err,job) {
     if (err) return;
     job.remove(function(err){
        if (err) throw err;
        console.log('removed completed job #%d',job.id);
     });
  });  
});

// updated that
function create_email(i) {
  var j = jobs.create('email',template: 'Bla bla bla'
  });
  j.save();
}

修复程序后,Redis中的内容好多了:

redis 127.0.0.1:6379> keys *
1) "q:stats:work-time"
2) "q:ids"
3) "q:job:types"

您可以使用类似的Coffescript策略.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐