今天试着把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的库不太给力,哈哈。