• 首页
  • nodejs
  • node-cache 模块 node.js 轻量级缓存管理使用及源码分析

node-cache 模块 node.js 轻量级缓存管理使用及源码分析

随着前端越来越重,已经不是当年的一个页面配几个第三方js库实现一些花俏的效果,而是出现了各种各样的框架解决方案.

当我们项目实现前后端分离时,甚至一些逻辑处理也会放到前端来处理.


所以随随便便打开一个页面上百k应该也是正常,甚至更大的页面在考验的前端.


但是伴随成长的后端,尤其数存储方面貌似发展缓慢,总有跟不上的赶脚!


想当年我们使用关系型数据库,当达到一个瓶颈时,解决起来是不计后果的,读写分离,拆表拆库,做service中间件,然后给中间件负载均衡等等来减轻前端并发造成的后端数据压力.


如今的mongodb 做一个分布式貌似轻轻松松, redis 做一层缓存让你有飞一样的感觉.我们终于谈到了缓存.


缓存应用场景很多,空间换时间的原理,会占用一点点内存,但是给你的web服务提升明显的性能.


并不是所有的数据都要放到缓存.不长更新的,没有实时性的数据可以暂时用缓存来处理.比如我的博客首页大概一天更新一次,变化并不是很大.

就可以把首页数据放到缓存来处理.


虽然我的数据库用的LevelDB ,号称也是处理海量数据的本地数据库,关于高性能LevelDB使用,请点击查看另外一篇博文

就算性能再高也是走IO,毕竟不如直接命中内存来的简单粗暴.


需求:

我只缓存首页,所以一定要轻量级,默认缓存一天,当发布新博客时,清楚缓存即可.


解决方案:

node-cache

官方地址:https://github.com/ptarjan/node-cache

绝对够小巧,还满足我简单的需求.


node-cache API 使用请移步到github 查看.


源码如下:

'use strict';

var cache = Object.create(null);
var debug = false;
var hitCount = 0;
var missCount = 0;
var size = 0;

exports.put = function(key, value, time, timeoutCallback) {
  if (debug) {
    console.log('caching: %s = %j (@%s)', key, value, time);
  }
  var oldRecord = cache[key];
  if (oldRecord) {
    clearTimeout(oldRecord.timeout);
  } else {
    size++;
  }

  var expire = time + Date.now();
  var record = {
    value: value,
    expire: expire
  };

  if (!isNaN(expire)) {
    var timeout = setTimeout(function() {
      exports.del(key);
      if (typeof timeoutCallback === 'function') {
        timeoutCallback(key);
      }
    }, time);
    record.timeout = timeout;
  }

  cache[key] = record;
};

exports.del = function(key) {
  var canDelete = true;

  var oldRecord = cache[key];
  if (oldRecord) {
    clearTimeout(oldRecord.timeout);
    if (!isNaN(oldRecord.expire) && oldRecord.expire < Date.now()) {
      canDelete = false;
    }
  } else {
    canDelete = false;
  }

  if (canDelete) {
    size--;
    delete cache[key];
  }

  return canDelete;
};

exports.clear = function() {
  for (var key in cache) {
    var oldRecord = cache[key];
    if (oldRecord) {
      clearTimeout(oldRecord.timeout);
    }
  }
  size = 0;
  cache = Object.create(null);
  if (debug) {
    hitCount = 0;
    missCount = 0;
  }
};

exports.get = function(key) {
  var data = cache[key];
  if (typeof data != "undefined") {
    if (isNaN(data.expire) || data.expire >= Date.now()) {
      if (debug) hitCount++;
      return data.value;
    } else {
      // free some space
      if (debug) missCount++;
      size--;
      delete cache[key];
    }
  } else if (debug) {
    missCount++;
  }
  return null;
};

exports.size = function() {
  return size;
};

exports.memsize = function() {
  var size = 0,
    key;
  for (key in cache) {
    size++;
  }
  return size;
};

exports.debug = function(bool) {
  debug = bool;
};

exports.hits = function() {
  return hitCount;
};

exports.misses = function() {
  return missCount;
};

exports.keys = function() {
  return Object.keys(cache);
};


var cache = Object.create(null);

加载此模块时,初始化 cache 对象,


从上到下 首先来看一下 put api

exports.put = function(key, value, time, timeoutCallback) {
  if (debug) {
    console.log('caching: %s = %j (@%s)', key, value, time);
  }
  var oldRecord = cache[key];
  if (oldRecord) {
    clearTimeout(oldRecord.timeout);
  } else {
    size++;
  }

  var expire = time + Date.now();
  var record = {
    value: value,
    expire: expire
  };

  if (!isNaN(expire)) {
    var timeout = setTimeout(function() {
      exports.del(key);
      if (typeof timeoutCallback === 'function') {
        timeoutCallback(key);
      }
    }, time);
    record.timeout = timeout;
  }

  cache[key] = record;
};

接受4个参数 ,key,value,time(过期时间) , timeoutCallback (到期时回调函数)

var oldRecord=cache[key];

if(oldRecord)

如果此缓存已经存在:

clearTimeout(oldRecord.timeout);

这个方法大家应该明白, 清楚 setTimeout 定时任务的.(这里的作用就是,如果此缓存已经存在,接着你有调用了put 函数,说明你要更新此缓存,那么就把之前的定时器清除掉)


esle

size ++;

这里的 size 是维护当前缓存个数的一个变量,如果从来没有缓存过此对象,说明是新加的缓存,size 也相应加一.


var expire=time+Date.now();

过期时间=我们传入的时间间隔+当前时间.


var record 此对象包含了缓存 value 和 过期时间.


接着

 if(!isNaN(expire)){

//如果设置了缓存时间,创建一个定时器(当定时器过期失效时,清楚此缓存)

var timeout=setTimeout(function(){

            //调用 del() 函数清楚缓存

},time);

//record 对象上添加了一个属性 timeout

record.timeout=timeout;

}


//cache 对象添加了一个属性 key ,和 value ( record )

cache[key]=record;


看完这个大概就明白了.这个模块其实维护了一个对象 cache={};

添加一个 key='index_page' value=[a,b,c,d,e,f,g]; 的缓存时.

相当于

cache={

    'index_page':

    [a,b,c,d,e,f,g]

}


接着我添加一个带过期时间的缓存: 3000毫秒后过期 key='list'   value=[1,2,3,4,5];

相当于

cache={

    'index_page':[a,b,c,d,e,f,g],

    'list':{

                timeout:当前时间+3000,

                value:[1,2,3,4,5]

            }

}


删除一个缓存,其实就是在 cache 对象上删除一个属性为 key 的字段.

如 : 删除 'index_page' 缓存

delete cache.index_page


如果我要更新 list 缓存:

首先要清楚 list 定义的定时器 

clearTimeout(cache.list.timeout);  //此timeout 就是定时器ID,根据ID可以直接清除掉定时器

接着判断新更新的缓存有没有设置过期时间

如果有,继续设置一个新的定时器.


如果没有,直接用最新的value 覆盖之前的 value 即可达到更新缓存效果.


非常轻量级,使用起来也非常简单!

回到顶部