Nodejs, MemCacheD 在实际项目中的使用

今天试着把MemCacheD集成到Nodejs的项目,总的来说还是比较顺利的。

我先尝试了最近刚更新的Cacher库,作者的想法非常好,以express中间件的形式,Cache所有的HTTP 请求,但是实际用下来,发现错误率比较多。粗略看了下,作者的源代码,应该是提供了一个自定义的res.write函数,截获了next rounter生成的response内容,然后把body, header全部cache起来,但那个作者用到了2个key,一个正常一个带stale后缀,没太搞懂,而且这样做,问题多多,所以也就不再研究下去了。接着尝试了稍微成熟一点的memcached库,(其实cacher也依赖于这个库的)。

这个库提供的功能主要是get/set操作,外加cas, increase, decrease, create。作者考虑的比较周到的是对connection进行了管理。虽然使用下来也不是很顺畅,但总之是可以用用了。(作者原版在connection管理上有重大缺陷,我通过npm安装的版本不能解决那些问题,后来跑到github上(https://github.com/3rd-Eden/node-memcached),看到有2个approved pull reqeust,下载下来更新下,用起来就舒服多了)。

好,现在就看代码,代码很简单,我自己写了一个简单的wrapper:

var Memcached = require('memcached');

dummy: function dummy(error, ok) { }

module.exports = {

    init: function ctor(server, opt) {
        console.log("memcached is initialized");
        memcached = new Memcached('localhost:11211', { debug: true });

        //memcached.on("failure", function (detail) {})
        //         .on('connect', function (detail) {})
        //         .on('reconnect', function (detail) {})
        //         .on('reconnecting', function (detail) {})
        //         .on('remove', function (detail) {})
        //         .on('issue', function (detail) {});
    },

    set: function addToCache(key, val, expire, callback) {
        if (!expire) expire = 5 * 60; //sec
        if (!callback) callback = dummy;
        memcached.set(key, val, expire, callback);
    },

    get: function getFromCache(key, callback) {
        if (!callback) callback = dummy;
        memcached.get(key, callback);
    },
};

注意,上面那个dummy不能省略,如果不写,set不会起作用,也不报错,一开始碰到这个问题,浪费了不少我时间。

上面注释掉的是,作者用以监控connection状态的事件,在处理你的请求的时候,假如server断了,"issue"事件会被触发,然后他会尝试重新连接memcached server,照他文档说,一旦超过最大重试次数,会触发failure事件,但是我没截获到。还有一个问题就是,在服务失效之后的第一次请求,callback不会被触发,所以这里会有一个小问题。另外,我的web server很有可能运行在一台没有安装MemCacheD的服务器上,那么每次server起来,用户的第一次请求都失败,不是很好,所以我在程序里面,自己触发一次,代码如下,里面的5是对象的生存期是5秒的意思。注意,这里用的是我封装过的memcached对象,不是库里面的那个:

http.createServer(app).listen(app.get('port'), function () {
    console.log('Express server listening on port ' + app.get('port'));

    // Trigger a MemCaheD to test the MemCacheD server
    memcahced.set("dummy", "dummy", 5);
});

在实际函数中的使用:

/*private*/
function getServiceData(partialUrl, params, useCache, callback) {
    assert(params.productId, "Missing parameter: productId");
    assert(params.weekId, "Missing parameter: weekId");
    var url = cerRestApi.getRequestUrl(partialUrl, params);
    console.log(url);

    if (!useCache) {
        cerRestApi.getCall(url, callback);
        return;
    }

    memcahced.get(url, function (errCache, result) {
        if (!result) {
            cerRestApi.getCall(url, function (err, data) {
                if (!err && !errCache) {
                    memcahced.set(url, data);
                }
                console.log(data);
                callback(err, data);
            });
        }
        else {
            callback(false, result);
        }
    });
}

首先尝试从cache中取数据,如果失败,即result是false或者undefined,就从backend的WebService里面取数据, 数据取得成功给后,就写入cache。下一次,就可以直接从cache里面取得数据了。

总的来说,MemCacheD用起来还是很方便的,就是nodeJs的库不太给力,哈哈。