在redis的node客戶端中官方文檔上描述客戶端斷開與redis的連接的方式有兩種:
- client.end()所謂的‘二話不說直接暴力斷開’;官方文檔上是這樣描述end()方法的:
Forcibly close the connection to the Redis server. Note that this does not wait until all replies have been parsed. If you want to exit cleanly, call client.quit() as mentioned above.
You should set flush to true, if you are not absolutely sure you do not care about any other commands. If you set flush to false all still running commands will silently fail.
大體上的意思就是說end()方法不會等到所有的答復都被解析之后才斷開和redis的連接,他會立刻斷開與數(shù)據(jù)庫的連接竣稽,若是你想要優(yōu)雅的退出你應該選擇quit()敞掘。
- quit()方法在官方文檔上是這樣描述的:
This sends the quit command to the redis server and ends cleanly right after all running commands were properly handled. If this is called while reconnecting (and therefore no connection to the redis server exists) it is going to end the connection right away instead of resulting in further reconnections! All offline commands are going to be flushed with an error in that case.
主旨是說調用quit命令將所有運行的命令正確處理后,將quit命令發(fā)送到redis服務器,并將其完全正確地結束辛臊。就是所謂的優(yōu)雅退出
如果在重新連接時調用(即redis服務器的連接不存在)疹味,則它將立即結束連接仅叫,而不會導致進一步的重新連接,在這種情況下糙捺,所有脫機命令將被刷新诫咱,并出現(xiàn)錯誤。
因為有所謂的優(yōu)雅退出才會有如下的方式寫法:
var redis = require("redis");
var client = redis.createClient({detect_buffers: true});
client.set("foo_rand000000000000", "OK");
// This will return a JavaScript String
client.get("foo_rand000000000000", function (err, reply) {
console.log(reply.toString()); // Will print `OK`
});
// This will return a Buffer since original key is specified as a Buffer
client.get(new Buffer("foo_rand000000000000"), function (err, reply) {
console.log(reply.toString()); // Will print `<Buffer 4f 4b>`
});
client.quit();
這段代碼中的client.set()和client.get()無疑都是異步的代碼洪灯,若是此處用的是client.end()替換掉client.quit()無疑會在命令未能正確處理之前就會于數(shù)據(jù)庫斷開從而出現(xiàn)錯誤坎缭,而quit()就會處理完所有命令回復之后才安全的斷開與數(shù)據(jù)庫的連接。
Redis中的quit()和end()就相應類比為在node-mysql中的兩種與數(shù)據(jù)庫斷開的方式end()和destroy()方法签钩。
接下來就是我在寫代碼的時候自以為quit優(yōu)雅退出遇到的問題掏呼,原始代碼如下:
/**
* 往某一個有序集合里面增加一個成員,如果這個成員存在則將其分數(shù)加1铅檩,如果不存在則直接加入一個成員憎夷;
* 判斷members是否已經在sortedSet中存在了
* 由于有序集合中不像簡單集合中有SISMEMBER直接判斷是否存在的命令
* 這里我使用 ’zscore key memberName‘ 的方式如果返回為null則表明他不存在
* @param {String} 鍵的keyName
* @param {Array}
* @param {Function}
*/
redisDB.addToSortedSet = (keyName, args, callback) => {
let client = redisDB.connect();
args.forEach((element, index) => {
let params = [];
params.push(keyName, element);
client.zscore(params, (err, results) => {
if (err) {
logger.writeDebug(err);
callback(err);
} else {
if (results) { //成員已經存在
let params = [];
params.push(keyName, 1, element);
client.zincrby(params, (err, res) => {
if (err) {
logger.writeDebug(err);
callback(err);
} else {
callback(null, res);
}
})
} else { //成員不存在直接插入,初始分數(shù)為0
let params = [];
params.push(keyName, 0, element);
client.zadd(params, (err, res) => {
if (err) {
logger.writeDebug(err);
callback(err);
} else {
callback(null, res);
}
})
}
}
})
});
client.quit();
}
這段代碼主要的結構就是我在建立了node與Redis的連接之后跟了一個對數(shù)組的forEach循環(huán),然后再循環(huán)體中有嵌套兩層的異步回調函數(shù)昧旨;(Tips:在寫這段的時候我是需要去判斷數(shù)組內的元素在redis的有序集合中是不是已經存在拾给,由于有序集合不像是Set結構那樣有一條命令去直接判斷元素是否是Set的成員(sismember命令
)祥得,所以我用了zscore key memberName
如果成員存在則會返回這個成員對應的score,如果這個成員不存在則就會返回null蒋得,以此判斷是否存在)
這段代碼在我測試調用插入兩條數(shù)據(jù)的時候報出了這樣的錯誤:
{ AbortError:
Stream connection ended and command aborted.
It might have been processed.
code: 'NR_CLOSED',
command: 'ZINCRBY',}
說我的連接斷了级及,執(zhí)行到ZINCRBY的時候命令中斷了........
原來“優(yōu)雅退出”也并不是完全讓人省心......(我好想抖段子。额衙。饮焦。)
我仔細看了一下報錯,我插入了兩條數(shù)據(jù)庫里都存在數(shù)據(jù)窍侧,應該到最后走的都是最里層ZINCRBY
的那個異步分支县踢,而報錯的就是forEach中兩條數(shù)數(shù)據(jù)都是執(zhí)行到ZINCRBY
報出得Stream connection ended and command aborted.
當時我的腦子里過的第一個單純的想法是forEach會不會是個異步的,因為它長得像疏之,而quit()優(yōu)雅退出的是redis自己的命令回調殿雪,并不會管別的異步回調,然而forEach雖然傳了一個函數(shù)但是它本身并不是異步的,測試代碼如下:
let args=[2,3,4];
args.forEach( (element, index)=> {
console.log(element);
});
console.log(1);
打印結果:
2
3
4
1
并不是想象中的異步的锋爪;必然還是要試一下forEach中只有一層異步的情況的丙曙,試完問題就必然都對上號了
redisDB.addToSortedSet = (keyName, args, callback) => {
let client = redisDB.connect();
args.forEach((element, index) => {
let params = [];
params.push(keyName, element);
client.zscore(params, (err, results) => {
if (err) {
logger.writeDebug(err);
callback(err);
} else {
console.log(results);
}
})
});
client.quit();
}
這一次執(zhí)行之后沒毛病,傳進去有兩個元素的args其骄,因為存在所以就很優(yōu)雅的返回了兩個成員的分數(shù)亏镰,看來優(yōu)雅的照顧也是有限的,我只負責你一層所有的命令全部給你結果拯爽,再嵌套一層就不會管你了:)
遂...
代碼改為如下
redisDB.addToSortedSet = (keyName, args, callback) => {
let client = redisDB.connect();
args.forEach((element, index) => {
let params = [];
params.push(keyName, element);
client.zscore(params, (err, results) => {
if (err) {
logger.writeDebug(err);
callback(err);
} else {
if (results) { //成員已經存在
let params = [];
params.push(keyName, 1, element);
client.zincrby(params, (err, res) => {
client.quit();
if (err) {
logger.writeDebug(err);
callback(err);
} else {
callback(null, res);
}
})
} else { //成員不存在直接插入,初始分數(shù)為0
let params = [];
params.push(keyName, 0, element);
client.zadd(params, (err, res) => {
client.quit();
if (err) {
logger.writeDebug(err);
callback(err);
} else {
callback(null, res);
}
})
}
}
})
});
}
這下沒毛病了.......在最里層異步函數(shù)的回調里再調用client.quit()索抓,就能保證請求都被處理完了之后再“優(yōu)雅斷開”
(最后很想說,第一次在博客網站上輸出毯炮,可能還是跟以前在wiki上記的片面的不太一樣逼肯,以后也會養(yǎng)成經常寫博客記錄的習慣,因為水平有限表達上可能也有待提高桃煎,更希望能夠得到大家的批評指正篮幢,大家一起在學習路上共同進步!)