node_redis 中文文档及node_redis 注释笔记(中文版)
node_redis 中文文檔及node_redis 注釋筆記(中文版)
https://github.com/NodeRedis/node_redis
redis - a node.js redis client
這是node.js的一個(gè)完整且功能豐富的Redis客戶端。它支持所有的Redis命令,并專注于高性能。
Install with:
npminstallredis
Usage Example
varredis=require("redis"),
client=redis.createClient();
//ifyou'dliketoselectdatabase3,insteadof0(default),call
//client.select(3,function(){/*...*/});
client.on("error",function(err){
console.log("Error"+err);
});
client.set("stringkey","stringval",redis.print);
client.hset("hashkey","hashtest1","somevalue",redis.print);
client.hset(["hashkey","hashtest2","someothervalue"],redis.print);
client.hkeys("hashkey",function(err,replies){
console.log(replies.length+"replies:");
replies.forEach(function(reply,i){
console.log(""+i+":"+reply);
});
client.quit();
});
This will display:
mjr:~/work/node_redis(master)$nodeexample.js Reply:OK Reply:0 Reply:0 2replies: 0:hashtest1 1:hashtest2 mjr:~/work/node_redis(master)$
請(qǐng)注意,該API完全是異步的。您需要使用一個(gè)回調(diào),從服務(wù)器獲取數(shù)據(jù)。自從2.6版本開(kāi)始,API支持在所有地方的選項(xiàng)、變量、事件等等使用駝峰和下劃線命名規(guī)范。不過(guò),建議使用Node.js默認(rèn)的風(fēng)格——駝峰式。
--------------------------------------------------
譯者注:
關(guān)于異步我做了一個(gè)如下demo
varredis=require("redis"),
client=redis.createClient();
client.set("stringkey",true,()=>{
console.log(1)
});
console.log(2)
client.get("stringkey",(err,key)=>{
console.log(3)
});
console.log(4)
返回了
2 4 1 3
--------------------------------------------------
Promises
你也可以通過(guò)如下方式使用bluebird模塊,將node_redispromises化:
varredis=require('redis');
bluebird.promisifyAll(redis.RedisClient.prototype);
bluebird.promisifyAll(redis.Multi.prototype);
它將在所有node_redis函數(shù)后添加一個(gè)Async (例如 return client.getAsync().then())
//Weexpectavalue'foo':'bar'tobepresent
//Soinsteadofwritingclient.get('foo',cb);youhavetowrite:
returnclient.getAsync('foo').then(function(res){
console.log(res);//=>'bar'
});
//Usingmultiwithpromiseslookslike:
returnclient.multi().get('foo').execAsync().then(function(res){
console.log(res);//=>'bar'
});
--------------------------------------------------
譯者注:
bluebird裝飾后可以結(jié)合ES6的await/async使用
varredis=require("redis"),
bluebird=require("bluebird");
bluebird.promisifyAll(redis.RedisClient.prototype);
bluebird.promisifyAll(redis.Multi.prototype);
letclient=redis.createClient();
(asyncfunction(){
awaitclient.setAsync("ip",1,"ex",10);
letip=awaitclient.getAsync("ip");
awaitclient.setAsync("ip",++ip,"ex",10);
ip=awaitclient.getAsync("ip");
console.log(ip)
})();
--------------------------------------------------
Sending Commands
發(fā)送命令
每個(gè)Redis命令都作為client對(duì)象上的一個(gè)函數(shù)暴露出來(lái)。所有函數(shù)都采用一個(gè)args數(shù)組加上可選的callback函數(shù),或者一個(gè)可變數(shù)量的的單獨(dú)參數(shù)跟隨一個(gè)可選的回調(diào)。例如:
client.hmset(["key","testkeys1","testval1","testkeys2","testval2"],function(err,res){});
//Worksthesameas
client.hmset("key",["testkeys1","testval1","testkeys2","testval2"],function(err,res){});
//Or
client.hmset("key","testkeys1","testval1","testkeys2","testval2",function(err,res){});
Care should be taken with user input if arrays are possible (via body-parser, query string or other method), as single arguments could be unintentionally interpreted as multiple args.
(TODO)
注意,無(wú)論哪種形式,回調(diào)都是可選的:
client.set("somekey","someval");client.set(["someotherkey","someval"]);
如果key不存在,將返回null。只有當(dāng)Redis Command Reference特別聲明,它才不會(huì)為空。
client.get("missingkey",function(err,reply){//replyisnullwhenthekeyismissing
console.log(reply);
});
Redis命令列表,請(qǐng)參見(jiàn)Redis Command Reference
在返回結(jié)果中進(jìn)行了最低限度的解析。命令中,integer 返回Javascript的Numbers,arrays 返回JavaScript Array.HGETALL返回hash keys作為key的Object .所有strings 將返回 string ,亦或者你特別設(shè)置返回類型為buffer類型 。請(qǐng)注意:null, undefined 和Boolean 在返回中將強(qiáng)制轉(zhuǎn)換為字符串。
--------------------------------------------------
譯者注:
關(guān)于Boolean 在返回中將強(qiáng)制轉(zhuǎn)換為字符串。我做了如下demo
varredis=require("redis"),
client=redis.createClient();
client.set("stringkey",true);
client.get("stringkey",(err,key)=>{
console.log(typeofkey);
});
返回
string
--------------------------------------------------
Redis Commands
Redis命令
這個(gè)庫(kù)跟Redis命令一一映射。請(qǐng)參照Redis命令頁(yè)獲取完整的使用細(xì)節(jié)。
例如使用SET command設(shè)置key值得自動(dòng)失效時(shí)間
//thiskeywillexpireafter10seconds
client.set('key','value!','EX',10);
API
Connection and other Events
客戶端將會(huì)發(fā)送一些關(guān)于連接到Redis服務(wù)器的狀態(tài)的事件。
"ready"
client當(dāng)連接建立后,將觸發(fā)一次ready事件. 在ready事件之前發(fā)出的命令將被排隊(duì),ready事件觸發(fā)后,隊(duì)列中的命令依次執(zhí)行。
"connect"
一旦stream連接到服務(wù)器,client立即觸發(fā)connect事件。
--------------------------------------------------
譯者注:
關(guān)于“ready“,”connect”執(zhí)行先后問(wèn)題,設(shè)計(jì)了如下demo
varredis=require("redis"),
client=redis.createClient();
client.on("ready",()=>{
console.log("ready");
});
client.on("connect",()=>{
console.log("connect");
});
結(jié)果
connect ready
--------------------------------------------------
"reconnecting"
在失去連接后,當(dāng)嘗試重新連接到Redis服務(wù)器,client將觸發(fā)reconnecting事件。監(jiān)聽(tīng)器將被傳遞一個(gè)包含delay(in ms) 和attempt(the attempt #) 屬性的對(duì)象。
"error"
當(dāng)遇到連接到Redis服務(wù)器錯(cuò)誤,或在node_redis中出現(xiàn)的任何其他錯(cuò)誤時(shí),client將觸發(fā)error事件。如果你使用一個(gè)沒(méi)有回調(diào)的命令,或者遇到返回異常時(shí),錯(cuò)誤監(jiān)聽(tīng)器將被觸發(fā)。
因此,請(qǐng)將錯(cuò)誤偵聽(tīng)器附加到node_redis上。
"end"
當(dāng)已和Redis服務(wù)器建立的連接被關(guān)閉時(shí),client將觸發(fā)end事件。
"drain" (deprecated棄用)
當(dāng)TCP連接到Redis server曾經(jīng)被緩存,但現(xiàn)在是可寫(xiě)的(譯者注:緩存池排干時(shí))。這個(gè)事件將被用于流命令進(jìn)入Redis,并適應(yīng)背壓。
當(dāng)流被緩存client.should_buffer被設(shè)置為 true. 否則變量始終被設(shè)置為false。這樣你能夠決定何時(shí)降低發(fā)送速率,當(dāng)觸發(fā)drain事件時(shí)再恢復(fù)傳輸。
您也可以檢查每個(gè)命令的返回值,因?yàn)樗矊⒎祷乇硥褐甘酒鳎ú唤ㄗh使用)。如果返回false,則流必須緩沖。
--------------------------------------------------
譯者注:
關(guān)于drain原理,可以參閱http://taobaofed.org/blog/2015/12/31/nodejs-drain/
我寫(xiě)了一個(gè)drain測(cè)試demo
'usestrict';
varredis=require('redis');
varclient=redis.createClient({
return_buffers:true
});
client.auth("g7845120");
varfs=require('fs');
varfilename='Funny-Cat-GIFs.jpg';
fs.readFile(filename,function(err,data){
if(err)throwerr;
console.log('Read'+data.length+'bytesfromfilesystem.');
client.set(filename,data,function(){
console.log("setend")
});//setentirefile
if(client.should_buffer){
client.stream.pause();
}
client.stream.on("drain",function(){
console.log("drain");
client.stream.resume();
});
});
--------------------------------------------------
"warning"
當(dāng)設(shè)置密碼時(shí),但不需要,并且使用了一個(gè)不贊成的選項(xiàng)/功能/類似。client將觸發(fā)warning事件。
"idle" (deprecated棄用)
在沒(méi)有正在等待響應(yīng)的未完成命令時(shí)發(fā)出時(shí),client將觸發(fā)idle事件
redis.createClient()
如果你的node服務(wù)器和redis服務(wù)器運(yùn)行在同一臺(tái)機(jī)器上,那么默認(rèn)設(shè)置的port和host或許是可用的,并且你不需要提供其他參數(shù)。createClient()返回一個(gè)RedisClient對(duì)象。否則,createClient()需要接受這些參數(shù):
redis.createClient([options])
redis.createClient(unix_socket[, options])
redis.createClient(redis_url[, options])
redis.createClient(port[, host][, options])
提示:如果Redis服務(wù)器與客戶端在同一臺(tái)計(jì)算機(jī)上運(yùn)行,請(qǐng)盡可能使用unix套接字來(lái)增加吞吐量。
optionsobject properties
| Property | Default | Description |
|---|---|---|
| host | 127.0.0.1 |
IP address of the Redis server |
| port | 6379 | Port of the Redis server |
| path | null | The UNIX socket string of the Redis server |
| url | null | The URL of the Redis server. Format:[redis:]//[[user][:password@]][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]](More info avaliable atIANA). |
| parser | javascript | DeprecatedUse either the built-in JS parserjavascriptor the nativehiredisparser.Notenode_redis< 2.6 uses hiredis as default if installed. This changed in v.2.6.0. |
| string_numbers | null |
設(shè)置為true,node_redis將返回String類型Redis的number值,以替代javascript的Nembers類型。當(dāng)你需要處理一個(gè)大數(shù)字時(shí)有用(超過(guò)Number.MAX_SAFE_INTEGER === 2^53)。Hiredis無(wú)能為力,因此設(shè)置此選項(xiàng) |
| return_buffers | false |
如果設(shè)置為true,那么所有的返回值將以Buffers的形式替代Strings發(fā)送到回調(diào)函數(shù). |
| detect_buffers | false |
如果設(shè)置為 |
| socket_keepalive | true |
如果設(shè)置為 |
| no_ready_check | false | When a connection is established to the Redis server, the server might still be loading the database from disk. While loading, the server will not respond to any commands. To work around this,node_redishas a "ready check" which sends theINFOcommand to the server. The response from theINFOcommand indicates whether the server is ready for more commands. When ready,node_redisemits areadyevent. Settingno_ready_checktotruewill inhibit this check. |
| enable_offline_queue | true | By default, if there is no active connection to the Redis server, commands are added to a queue and are executed once the connection has been established. Settingenable_offline_queuetofalsewill disable this feature and the callback will be executed immediately with an error, or an error will be emitted if no callback is specified. |
| retry_max_delay | null | DeprecatedPlease useretry_strategyinstead.By default, every time the client tries to connect and fails, the reconnection delay almost doubles. This delay normally grows infinitely, but settingretry_max_delaylimits it to the maximum value provided in milliseconds. |
| connect_timeout | 3600000 | DeprecatedPlease useretry_strategyinstead.Settingconnect_timeoutlimits the total time for the client to connect and reconnect. The value is provided in milliseconds and is counted from the moment a new client is created or from the time the connection is lost. The last retry is going to happen exactly at the timeout time. Default is to try connecting until the default system socket timeout has been exceeded and to try reconnecting until 1h has elapsed. |
| max_attempts | 0 | DeprecatedPlease useretry_strategyinstead.By default, a client will try reconnecting until connected. Settingmax_attemptslimits total amount of connection attempts. Setting this to 1 will prevent any reconnect attempt. |
| retry_unfulfilled_commands | false | If set totrue, all commands that were unfulfilled while the connection is lost will be retried after the connection has been reestablished. Use this with caution if you use state altering commands (e.g.incr). This is especially useful if you use blocking commands. |
| password | null | If set, client will run Redis auth command on connect. Aliasauth_passNotenode_redis< 2.5 must useauth_pass |
| db | null | If set, client will run Redisselectcommand on connect. |
| family | IPv4 | You can force using IPv6 if you set the family to 'IPv6'. See Node.jsnetordnsmodules on how to use the family type. |
| disable_resubscribing | false | If set totrue, a client won't resubscribe after disconnecting. |
| rename_commands | null | Passing an object with renamed commands to use instead of the original functions. For example, if you renamed the command KEYS to "DO-NOT-USE" then the rename_commands object would be:{ KEYS : "DO-NOT-USE" }. See theRedis security topicsfor more info. |
| tls | null | An object containing options to pass totls.connectto set up a TLS connection to Redis (if, for example, it is set up to be accessible via a tunnel). |
| prefix | null | A string used to prefix all used keys (e.g.namespace:test). Please be aware that thekeyscommand will not be prefixed. Thekeyscommand has a "pattern" as argument and no key and it would be impossible to determine the existing keys in Redis if this would be prefixed. |
| retry_strategy | function | 這是一個(gè)函數(shù),接受一個(gè)option對(duì)象作為參數(shù),其參數(shù)包含重試attempt,指示距離最后一次連接的時(shí)間total_retry_time,為什么連接失敗的error和總共重連次數(shù)的times_connected。如果在這個(gè)函數(shù)中你返回一個(gè)數(shù)字,那么將在這個(gè)數(shù)字的毫秒數(shù)的時(shí)間后嘗試重連。如果你返回一個(gè)非數(shù)字,則不會(huì)再發(fā)生重連,并且所有脫機(jī)命令將會(huì)刷新并顯示錯(cuò)誤。返回一個(gè)錯(cuò)誤,將特定錯(cuò)誤返回給所有脫機(jī)命令。下面的例子。 |
varredis=require("redis");
varclient=redis.createClient({detect_buffers:true});
client.set("foo_rand000000000000","OK");
//ThiswillreturnaJavaScriptString
client.get("foo_rand000000000000",function(err,reply){
console.log(reply.toString());//Willprint`OK`
});
//ThiswillreturnaBuffersinceoriginalkeyisspecifiedasaBuffer
client.get(newBuffer("foo_rand000000000000"),function(err,reply){
console.log(reply);//Willprint`<Buffer4f4b>`
//譯者注:官網(wǎng)的代碼是console.log(reply.toString());這會(huì)輸出ok而不是`<Buffer4f4b>`
});
client.quit();
retry_strategy example
varclient=redis.createClient({
retry_strategy:function(options){
if(options.error&&options.error.code==='ECONNREFUSED'){
//Endreconnectingonaspecificerrorandflushallcommandswith
//aindividualerror
returnnewError('Theserverrefusedtheconnection');
}
if(options.total_retry_time>1000*60*60){
//Endreconnectingafteraspecifictimeoutandflushallcommands
//withaindividualerror
returnnewError('Retrytimeexhausted');
}
if(options.attempt>10){
//Endreconnectingwithbuiltinerror
returnundefined;
}
//reconnectafter
returnMath.min(options.attempt*100,3000);
}
});
client.auth(password[, callback])
當(dāng)連接的Redis服務(wù)器需要安全認(rèn)證,AUTH命令必須在連接后作為第一條命令被發(fā)送。這可能需要與重新連接、就緒的檢查等進(jìn)行協(xié)調(diào)。為了更方便,client.auth()儲(chǔ)存password ,并在以后的每個(gè)連接,包括重連時(shí)發(fā)送它。回調(diào)只在對(duì)第一個(gè)AUTH命令發(fā)出的響應(yīng)之后才調(diào)用一次。注意:你調(diào)用client.auth() 不應(yīng)該在ready事件的處理函數(shù)中。如果你執(zhí)行了這個(gè)錯(cuò)誤,client將觸發(fā)類似這樣的錯(cuò)誤:Ready check failed: ERR operation not permitted.
--------------------------------------------------
譯者注:
varredis=require("redis")
letclient=redis.createClient();
client.on("ready",()=>{
console.log("ready")
client.auth("g7845120");
});
返回
ReplyError:Readycheckfailed:NOAUTHAuthenticationrequired.
--------------------------------------------------
backpressure
stream
client 在client.stream中暴露使用的stream并且在client.should_buffer中暴露流或者客戶端是否不得不被緩存。結(jié)合這些,你可以通過(guò)在發(fā)送一個(gè)命令前檢查buffer狀態(tài)并且監(jiān)聽(tīng)stream的drain事件實(shí)現(xiàn)背壓。
client.quit()
這會(huì)將退出命令發(fā)送到redis服務(wù)器,并在正確處理所有運(yùn)行的命令之后以干凈的方式結(jié)束。如果這是在重新連接時(shí)調(diào)用的(因此不存在與redis服務(wù)器的連接),它將立即終止連接,而不是產(chǎn)生進(jìn)一步的重新連接!在這種情況下,所有的離線命令都將被一個(gè)錯(cuò)誤所刷新。
client.end(flush)
強(qiáng)行關(guān)閉與Redis服務(wù)器的連接。注意,這不會(huì)等到所有的回復(fù)都被解析后才會(huì)出現(xiàn)。如果您想要干凈地退出,請(qǐng)調(diào)用上述client.quit()。
如果您不完全確定您不關(guān)心其他命令,那么您應(yīng)該將flush設(shè)置為true。如果您將flush設(shè)置為false,所有仍然運(yùn)行的命令將會(huì)無(wú)聲地失敗。
這個(gè)例子在讀取回復(fù)之前關(guān)閉了與Redis服務(wù)器的連接。你可能不想這樣做:
varredis=require("redis"),
client=redis.createClient();
client.set("foo_rand000000000000","somefantasticvalue",function(err,reply){
//Thiswilleitherresultinanerror(flushparameterissettotrue)
//orwillsilentlyfailandthiscallbackwillnotbecalledatall(flushsettofalse)
console.log(err);
});
client.end(true);//Nofurthercommandswillbeprocessed
client.get("foo_rand000000000000",function(err,reply){
console.log(err);//=>'Theconnectionhasalreadybeenclosed.'
});
client.end()如果不將flush參數(shù)設(shè)置為true,就不應(yīng)該在生產(chǎn)中使用!
Error handling (>= v.2.6)
當(dāng)前存在以下錯(cuò)誤子類:
RedisError:客戶端返回的所有錯(cuò)誤
ReplyErrorRedisError的子類: All errors returned byRedisitself
AbortErrorRedisError的子類: 由于某種原因所有的命令無(wú)法完成而中斷
ParserErrorRedisError的子類:返回解析錯(cuò)誤時(shí)返回(這不應(yīng)該發(fā)生)
AggregateErrorRedisError的子類:如果沒(méi)有回調(diào)的多個(gè)未解決的命令被釋放,在調(diào)試模式中會(huì)被rejected,而不是大量的AbortErrors。
所有的錯(cuò)誤類都是由模塊導(dǎo)出的。
Example:
varredis=require('./');
varassert=require('assert');
varclient=redis.createClient();
client.on('error',function(err){
assert(errinstanceofError);
assert(errinstanceofredis.AbortError);
assert(errinstanceofredis.AggregateError);
//Thesetandgetgetaggregatedinhere
assert.strictEqual(err.errors.length,2);
assert.strictEqual(err.code,'NR_CLOSED');
});
client.set('foo',123,'bar',function(err,res){//Toomanyarguments
assert(errinstanceofredis.ReplyError);//=>true
assert.strictEqual(err.command,'SET');
assert.deepStrictEqual(err.args,['foo',123,'bar']);
redis.debug_mode=true;
client.set('foo','bar');
client.get('foo');
process.nextTick(function(){
//Forceclosingtheconnectionwhilethecommanddidnotyetreturn
client.end(true);
redis.debug_mode=false;
});
});
每個(gè)ReplyError都包含有全大寫(xiě)的command名和參數(shù)(args)。
--------------------------------------------------
譯者注:
varredis=require('redis');
varassert=require('assert');
varclient=redis.createClient();
client.set('foo',123,'bar',function(err,res){//Toomanyarguments
assert(errinstanceofredis.ReplyError);//=>true
console.log(err)
console.log("command:"+err.command);
console.log("args:",err.args);
console.log("code:"+err.code);
});
返回
{ReplyError:ERRsyntaxerror
atparseError(F: estedis-test
ode_modulesedis-parserlibparser.js:193:12)
atparseType(F: estedis-test
ode_modulesedis-parserlibparser.js:303:14)command:'SET',args:['foo',123,'bar'],code:'ERR'}
command:SET
args:['foo',123,'bar']
code:ERR
--------------------------------------------------
如果node_redis由于其他錯(cuò)誤而發(fā)出庫(kù)錯(cuò)誤,則將觸發(fā)錯(cuò)誤添加到返回的錯(cuò)誤中,作為origin屬性。
Error codes
如果客戶端連接失敗,noderedis返回一個(gè)NR_CLOSED的錯(cuò)誤代碼。如果一個(gè)命令未解決的命令被拒絕,則返回一個(gè)UNCERTAIN_STATE的代碼。如果節(jié)點(diǎn)redis放棄了重新連接,則使用一個(gè)CONNECTION_BROKEN的錯(cuò)誤代碼。
--------------------------------------------------
譯者注:
varredis=require('redis');
varassert=require('assert');
varclient=redis.createClient();
client.on('error',function(err){
console.log(err.code)
});
redis.debug_mode=true;
client.set('foo','bar');
process.nextTick(function(){
//Forceclosingtheconnectionwhilethecommanddidnotyetreturn
client.end(true);
redis.debug_mode=false;
});
返回
NR_CLOSED
--------------------------------------------------
client.unref()
在與Redis服務(wù)器的底層套接字連接上調(diào)用unref(),允許程序在不需要更多命令的情況下退出。
這是一個(gè)實(shí)驗(yàn)性的特性,并且只支持Redis協(xié)議的一個(gè)子集。任何在Redis服務(wù)器上保存客戶端狀態(tài)的命令,例如訂閱或阻塞的BL命令都不能與.unref()一起工作。
varredis=require("redis")
varclient=redis.createClient()
/*
Callingunref()willallowthisprogramtoexitimmediatelyaftertheget
commandfinishes.Otherwisetheclientwouldhangaslongasthe
client-serverconnectionisalive.
調(diào)用unref()將允許該程序在get命令完成之后立即退出
。否則客戶端和服務(wù)器連接將一直保持。
*/
client.unref()
client.get("foo",function(err,value){
if(err)throw(err)
console.log(value)
})
Friendlier hash commands
友好的hash命令
大多數(shù)Redis命令都使用單個(gè)字符串或字符串?dāng)?shù)組作為參數(shù),并且返回響應(yīng)單個(gè)字符串或字符串?dāng)?shù)組。在處理hash值時(shí),有幾個(gè)有用的例外。
client.hgetall(hash, callback)
HGETALL命令的響應(yīng)將會(huì)被node_redis轉(zhuǎn)換為JavaScript對(duì)象。這樣,你就能夠用JavaScript語(yǔ)法與響應(yīng)進(jìn)行交互了。
Example:
client.hmset("hosts","mjr","1","another","23","home","1234");
client.hgetall("hosts",function(err,obj){
console.dir(obj);
});
Output:
{mjr:'1',another:'23',home:'1234'}
client.hmset(hash, obj[, callback])
Multiple values in a hash can be set by supplying an object:
client.HMSET(key2,{
"0123456789":"abcdefghij",//NOTE:keyandvaluewillbecoercedtostrings
"somemannerofkey":"atypeofvalue"
});
The properties and values of this Object will be set as keys and values in the Redis hash.
client.hmset(hash, key1, val1, ... keyn, valn, [callback])
Multiple values may also be set by supplying a list:
client.HMSET(key1,"0123456789","abcdefghij","somemannerofkey","atypeofvalue");
Publish / Subscribe
發(fā)布/訂閱API的例子。該程序打開(kāi)兩個(gè)客戶端連接,訂閱其中一個(gè)通道,并在另一個(gè)通道上發(fā)布該通道:
varredis=require("redis");
varsub=redis.createClient(),pub=redis.createClient();
varmsg_count=0;
sub.on("subscribe",function(channel,count){
pub.publish("anicechannel","Iamsendingamessage.");
pub.publish("anicechannel","Iamsendingasecondmessage.");
pub.publish("anicechannel","Iamsendingmylastmessage.");
});
sub.on("message",function(channel,message){
console.log("subchannel"+channel+":"+message);
msg_count+=1;
if(msg_count===3){
sub.unsubscribe();
sub.quit();
pub.quit();
}
});
sub.subscribe("anicechannel");
--------------------------------------------------
譯者注:
varredis=require("redis");
varsub=redis.createClient(),pub=redis.createClient();
varmsg_count=0;
sub.on("subscribe",function(channel,count){
pub.publish("anicechannel","Iamsendingamessage.");
pub.publish("anicechannel","Iamsendingasecondmessage.");
pub.publish("anicechannel","Iamsendingmylastmessage.");
pub.publish("anicechannel","Iamsendingmylastmessage.");//仍會(huì)接收
setTimeout(()=>{
pub.publish("anicechannel","Iamsendingmylastmessage.");//將會(huì)遺棄
},1000)
});
sub.on("message",function(channel,message){
console.log("subchannel"+channel+":"+message);
msg_count+=1;
if(msg_count===3){
sub.unsubscribe();
sub.quit();
}
});
sub.subscribe("anicechannel");
--------------------------------------------------
當(dāng)客戶機(jī)發(fā)出訂閱或訂閱時(shí),該連接將被放入“訂閱者”模式。在這一點(diǎn)上,只有修改訂閱集的命令是有效的(TODO)。當(dāng)訂閱集為空時(shí),連接將被放回常規(guī)模式。
如果您需要在訂閱模式下向Redis發(fā)送常規(guī)命令,只需打開(kāi)另一個(gè)與新客戶機(jī)的連接(提示:使用client.duplicate())。
Subscriber Events
如果客戶端有訂閱活動(dòng),它可能會(huì)發(fā)出這些事件:
"message" (channel, message)
客戶端將為收到的每一條與活動(dòng)的訂閱相匹配的消息發(fā)出message 事件。偵聽(tīng)器參數(shù)以channel作為頻道名稱,并以message作為消息。
"pmessage" (pattern, channel, message)
客戶端將為收到的每一條與活動(dòng)訂閱模式相匹配的消息發(fā)出pmessage事件。偵聽(tīng)器參數(shù)以PSUBSCRIBE作為原始的正則匹配模式 、以channel作為頻道名稱,并以message作為消息。
--------------------------------------------------
譯者注:
源碼中查找demo如下
'usestrict';
varredis=require('redis');
varclient1=redis.createClient();
varclient2=redis.createClient();
varclient3=redis.createClient();
varclient4=redis.createClient();
varmsg_count=0;
client1.on('psubscribe',function(pattern,count){
console.log('client1psubscribedto'+pattern+','+count+'totalsubscriptions');
client2.publish('channeltwo','Me!');
client3.publish('channelthree','Metoo!');
client4.publish('channelfour','Andmetoo!');
});
client1.on('punsubscribe',function(pattern,count){
console.log('client1punsubscribedfrom'+pattern+','+count+'totalsubscriptions');
client4.end();
client3.end();
client2.end();
client1.end();
});
client1.on('pmessage',function(pattern,channel,message){
console.log('('+pattern+')client1receivedmessageon'+channel+':'+message);
msg_count+=1;
if(msg_count===3){
client1.punsubscribe();
}
});
client1.psubscribe('channel*');
由于channel*正則匹配了channeltwo、channelthree、channelfour。client1就能接收到這三個(gè)頻道的消息。
--------------------------------------------------
"message_buffer" (channel, message)
This is the same as themessageevent with the exception, that it is always going to emit a buffer. If you listen to themessageevent at the same time as themessage_buffer, it is always going to emit a string.
(TODO)
"pmessage_buffer" (pattern, channel, message)
This is the same as thepmessageevent with the exception, that it is always going to emit a buffer. If you listen to thepmessageevent at the same time as thepmessage_buffer, it is always going to emit a string.
(TODO)
"subscribe" (channel, count)
客戶端根據(jù)SUBSCRIBE命令觸發(fā)subscribe事件。偵聽(tīng)器參數(shù)以channel作為頻道名稱,并以count作為新訂閱者數(shù)量。
"psubscribe" (pattern, count)
客戶端根據(jù)PSUBSCRIBE命令觸發(fā)psubscribe事件。偵聽(tīng)器參數(shù)以pattern作為原始的正則匹配模式,并以count作為新訂閱者數(shù)量。
"unsubscribe" (channel, count)
客戶端根據(jù)UNSUBSCRIBE命令觸發(fā)unsubscribe事件。偵聽(tīng)器參數(shù)以channel作為頻道名稱,并以count作為新訂閱者數(shù)量。當(dāng)count為0時(shí),客戶端將退出訂閱者模式,并且不再有訂閱者事件觸發(fā)。
"punsubscribe" (pattern, count)
客戶端根據(jù)PUNSUBSCRIBE命令觸發(fā)punsubscribe事件。偵聽(tīng)器參數(shù)以pattern作為原始的正則匹配模式,并以count作為新訂閱者數(shù)量。當(dāng)count為0時(shí),客戶端將退出訂閱者模式,并且不再有訂閱者事件觸發(fā)。
client.multi([commands])
MULTI命令排隊(duì)直到一個(gè)EXEC 命令被執(zhí)行,然后所有的命令都由Redis原子運(yùn)行。node_redis的接口是通過(guò)調(diào)用client.multi()返回一個(gè)單獨(dú)的Multi對(duì)象。如果隊(duì)列中任何命令執(zhí)行失敗,那么所有命令都會(huì)被回滾,并且不會(huì)執(zhí)行任何操作(更多信息查看transactions)。
varredis=require("./index"),
client=redis.createClient(),set_size=20;
client.sadd("bigset","amember");
client.sadd("bigset","anothermember");
while(set_size>0){
client.sadd("bigset","member"+set_size);
set_size-=1;
}
//multichainwithanindividualcallback
client.multi()
.scard("bigset")
.smembers("bigset")
.keys("*",function(err,replies){
//NOTE:codeinthiscallbackisNOTatomic
//thisonlyhappensafterthethe.execcallfinishes.
client.mget(replies,redis.print);
})
.dbsize()
.exec(function(err,replies){
console.log("MULTIgot"+replies.length+"replies");
replies.forEach(function(reply,index){
console.log("Reply"+index+":"+reply.toString());
});
});
Multi.exec([callback])
client.multi()是一個(gè)返回Multi對(duì)象的構(gòu)造函數(shù)。Multi對(duì)象與client對(duì)象共享所有相同的命令方法。在multi對(duì)象中,直到multi.exec()被調(diào)用,命令才被調(diào)用。
如果您的代碼包含一個(gè)語(yǔ)法錯(cuò)誤,那么將會(huì)拋出一個(gè)EXECABORT 錯(cuò)誤,所有的命令都將被中止。那個(gè)錯(cuò)誤包含一個(gè).errors屬性以描述具體的錯(cuò)誤。如果所有命令都成功地排隊(duì),并且并且一個(gè)錯(cuò)誤在redis執(zhí)行命令的過(guò)程中被拋出,那么錯(cuò)誤將在結(jié)果數(shù)組中被返回!除了onces失敗之外,其他命令不會(huì)被中止。
您可以像上面的示例中將多個(gè)命令鏈接,或者您仍然可以如以下例子那樣,排列并發(fā)送單條普通命令,
varredis=require("redis"),
client=redis.createClient(),multi;
//startaseparatemulticommandqueue
multi=client.multi();
multi.incr("incrthing",redis.print);
multi.incr("incrotherthing",redis.print);
//runsimmediately
client.mset("incrthing",100,"incrotherthing",1,redis.print);
//drainsmultiqueueandrunsatomically
multi.exec(function(err,replies){
console.log(replies);//101,2
});
除了向多隊(duì)列添加命令之外,還可以向構(gòu)造函數(shù)傳遞一個(gè)命令和參數(shù)數(shù)組:
varredis=require("redis"),
client=redis.createClient();
client.multi([
["mget","multifoo","multibar",redis.print],
["incr","multifoo"],
["incr","multibar"]
]).exec(function(err,replies){
console.log(replies);
});
Multi.exec_atomic([callback])
與Multi.exec類似,但是區(qū)別是執(zhí)行單個(gè)命令時(shí)不會(huì)使用事務(wù)。
client.batch([commands])
與 .multi 相同但沒(méi)有事務(wù)。如果您希望同時(shí)執(zhí)行多個(gè)命令,但不需要依賴事務(wù),那么建議您這樣做。
BATCH批處理命令在隊(duì)列中排隊(duì)等待執(zhí)行,然后所有的命令都由Redis原子運(yùn)行。node_redis中的接口是通過(guò)調(diào)用client.Batch()來(lái)返回一個(gè)單獨(dú)的Batch處理對(duì)象。.batch和.multi的唯一區(qū)別是.batch沒(méi)有事務(wù)。注意,錯(cuò)誤-就像在multi語(yǔ)句中一樣-返回在結(jié)果中。否則,錯(cuò)誤和結(jié)果都可以同時(shí)返回。
如果您同時(shí)觸發(fā)多個(gè)命令,那么這與一個(gè)循環(huán)中執(zhí)行相同的命令相比將大大提高執(zhí)行速度,而不需要等待結(jié)果!查看benchmarks 獲取更多比較信息。請(qǐng)記住,所有的命令都保存在內(nèi)存中,直到它們被觸發(fā)。
Monitor mode
Redis支持MONITOR命令,它讓您可以看到所有客戶端連接的所有命令,包括來(lái)自其他客戶端庫(kù)和其他計(jì)算機(jī)。
對(duì)于連接到服務(wù)器的任何客戶端發(fā)出的每個(gè)命令,都會(huì)發(fā)出一個(gè)monitor事件,包括monitoring客戶端本身。monitor事件的回調(diào)從Redis服務(wù)器獲取時(shí)間戳,一個(gè)命令參數(shù)數(shù)組和原始監(jiān)控字符串。
Example:
varclient=require("redis").createClient();
client.monitor(function(err,res){
console.log("Enteringmonitoringmode.");
});
client.set('foo','bar');
client.on("monitor",function(time,args,raw_reply){
console.log(time+":"+args);//1458910076.446514:['set','foo','bar']
});
Extras
還有一些你可能想知道的事情。
client.server_info
在就緒的探測(cè)完成之后,INFO命令的結(jié)果將保存在client.server_info對(duì)象中。
versions鍵包含以版本字符串的字符組成的數(shù)組中,以便進(jìn)行比較。
>client.server_info.redis_version '2.3.0' >client.server_info.versions [2,3,0]
redis.print()
一個(gè)方便的回調(diào)函數(shù),用于在測(cè)試時(shí)顯示返回值。例子:
varredis=require("redis"),
client=redis.createClient();
client.on("connect",function(){
client.set("foo_rand000000000000","somefantasticvalue",redis.print);
client.get("foo_rand000000000000",redis.print);
});
This will print:
Reply:OK Reply:somefantasticvalue
注意,這個(gè)程序不會(huì)干凈地退出,因?yàn)榭蛻舳巳匀皇沁B接的。
Multi-word commands
執(zhí)行redis的multi-word命令,如SCRIPT LOAD或CLIENT LIST,將第二個(gè)單詞作為第一個(gè)參數(shù)傳遞:
client.script('load','return1');
client.multi().script('load','return1').exec(...);
client.multi([['script','load','return1']]).exec(...);
client.duplicate([options][, callback])
復(fù)制所有當(dāng)前選項(xiàng)并返回一個(gè)新的redisClient實(shí)例。傳遞給duplicate函數(shù)的所有選項(xiàng)都將替換原來(lái)的選項(xiàng)。如果您傳遞一個(gè)回調(diào),duplicate將等待客戶端準(zhǔn)備好并在回調(diào)中返回它。如果與此同時(shí)發(fā)生錯(cuò)誤,則會(huì)返回一個(gè)錯(cuò)誤,而不是在回調(diào)中。
使用duplicate()的一個(gè)例子包含如下連接——阻塞的redis命令BRPOP、BLPOP和BRPOPLPUSH。如果這些命令在與非阻塞命令相同的redisClient實(shí)例上使用,則非阻塞的命令可能會(huì)排隊(duì)直到阻塞的命令結(jié)束。
varRedis=require('redis');
varclient=Redis.createClient();
varclientBlocking=client.duplicate();
varget=function(){
console.log("getcalled");
client.get("any_key",function(){console.log("getreturned");});
setTimeout(get,1000);
};
varbrpop=function(){
console.log("brpopcalled");
clientBlocking.brpop("nonexistent",5,function(){
console.log("brpopreturn");
setTimeout(brpop,1000);
});
};
get();
brpop();
使用duplicate()的另一個(gè)原因是,通過(guò)redis SELECT命令訪問(wèn)同一個(gè)服務(wù)器上的多個(gè)DBs。每個(gè)DB都可以使用它自己的連接。
client.send_command(command_name[, [args][, callback]])
所有的Redis命令都被添加到客戶端對(duì)象中。但是,如果在這個(gè)庫(kù)更新之前引入了新的命令,或者如果您想要添加單獨(dú)的命令,那么可以使用sendcommand()向Redis發(fā)送任意命令。
所有命令都是作為多批量命令發(fā)送的。args可以是一組參數(shù),也可以是未定義的參數(shù)。
client.add_command(command_name)
調(diào)用add_command將向原型添加一個(gè)新的命令。在使用這個(gè)新命令調(diào)用時(shí),將使用精確的命令名。使用任意參數(shù)與任何其他命令一樣是可能的。
client.connected
跟蹤連接到Redis服務(wù)器的連接狀態(tài)的布爾值。
client.command_queue_length
The number of commands that have been sent to the Redis server but not yet replied to. You can use this to enforce some kind of maximum queue depth for commands while connected.
client.offline_queue_length
已經(jīng)發(fā)送到Redis服務(wù)器但還沒(méi)有回復(fù)的命令數(shù)量。你可以使用這條命令為pre-connection命令去執(zhí)行一些類別的最大隊(duì)列深度。
Commands with Optional and Keyword arguments
這適用于任何使用一個(gè)可選的在redis.io/commands文檔中的[WITHSCORES]或[LIMIT offset count]。
Example:
varargs=['myzset',1,'one',2,'two',3,'three',99,'ninety-nine'];
client.zadd(args,function(err,response){
if(err)throwerr;
console.log('added'+response+'items.');
//-Infinityand+Infinityalsowork
varargs1=['myzset','+inf','-inf'];
client.zrevrangebyscore(args1,function(err,response){
if(err)throwerr;
console.log('example1',response);
//writeyourcodehere
});
varmax=3,min=1,offset=1,count=2;
varargs2=['myzset',max,min,'WITHSCORES','LIMIT',offset,count];
client.zrevrangebyscore(args2,function(err,response){
if(err)throwerr;
console.log('example2',response);
//writeyourcodehere
});
});
Performance
為了使node_redis盡可能快地進(jìn)行普通操作,花費(fèi)了大量的精力。
LenovoT450s,i7-5600Uand12gbmemory clients:1,NodeJS:6.2.0,Redis:3.2.0,parser:javascript,connectedby:tcp PING,1/1avg/max:0.02/5.262501mstotal,46916ops/sec PING,batch50/1avg/max:0.06/4.352501mstotal,755178ops/sec SET4Bstr,1/1avg/max:0.02/4.752501mstotal,40856ops/sec SET4Bstr,batch50/1avg/max:0.11/1.512501mstotal,432727ops/sec SET4Bbuf,1/1avg/max:0.05/2.762501mstotal,20659ops/sec SET4Bbuf,batch50/1avg/max:0.25/1.762501mstotal,194962ops/sec GET4Bstr,1/1avg/max:0.02/1.552501mstotal,45156ops/sec GET4Bstr,batch50/1avg/max:0.09/3.152501mstotal,524110ops/sec GET4Bbuf,1/1avg/max:0.02/3.072501mstotal,44563ops/sec GET4Bbuf,batch50/1avg/max:0.10/3.182501mstotal,473171ops/sec SET4KiBstr,1/1avg/max:0.03/1.542501mstotal,32627ops/sec SET4KiBstr,batch50/1avg/max:0.34/1.892501mstotal,146861ops/sec SET4KiBbuf,1/1avg/max:0.05/2.852501mstotal,20688ops/sec SET4KiBbuf,batch50/1avg/max:0.36/1.832501mstotal,138165ops/sec GET4KiBstr,1/1avg/max:0.02/1.372501mstotal,39389ops/sec GET4KiBstr,batch50/1avg/max:0.24/1.812501mstotal,208157ops/sec GET4KiBbuf,1/1avg/max:0.02/2.632501mstotal,39918ops/sec GET4KiBbuf,batch50/1avg/max:0.31/8.562501mstotal,161575ops/sec INCR,1/1avg/max:0.02/4.692501mstotal,45685ops/sec INCR,batch50/1avg/max:0.09/3.062501mstotal,539964ops/sec LPUSH,1/1avg/max:0.02/3.042501mstotal,41253ops/sec LPUSH,batch50/1avg/max:0.12/1.942501mstotal,425090ops/sec LRANGE10,1/1avg/max:0.02/2.282501mstotal,39850ops/sec LRANGE10,batch50/1avg/max:0.25/1.852501mstotal,194302ops/sec LRANGE100,1/1avg/max:0.05/2.932501mstotal,21026ops/sec LRANGE100,batch50/1avg/max:1.52/2.892501mstotal,32767ops/sec SET4MiBstr,1/1avg/max:5.16/15.552502mstotal,193ops/sec SET4MiBstr,batch20/1avg/max:89.73/99.962513mstotal,223ops/sec SET4MiBbuf,1/1avg/max:2.23/8.352501mstotal,446ops/sec SET4MiBbuf,batch20/1avg/max:41.47/50.912530mstotal,482ops/sec GET4MiBstr,1/1avg/max:2.79/10.912502mstotal,358ops/sec GET4MiBstr,batch20/1avg/max:101.61/118.112541mstotal,197ops/sec GET4MiBbuf,1/1avg/max:2.32/14.932502mstotal,430ops/sec GET4MiBbuf,batch20/1avg/max:65.01/84.722536mstotal,308ops/sec
Debugging
為了獲得調(diào)試輸出,您可以在node_redis應(yīng)用程序中使用NODE_DEBUG=redis。
這也會(huì)導(dǎo)致好的堆棧跟蹤,而不是無(wú)用的堆棧跟蹤,否則對(duì)于任何異步操作都是如此。如果您只想擁有好的堆棧跟蹤,而不是調(diào)試輸出,請(qǐng)?jiān)陂_(kāi)發(fā)模式中運(yùn)行您的應(yīng)用程序(NODE_ENV=development)。
好的堆棧跟蹤只在開(kāi)發(fā)和調(diào)試模式中被激活,因?yàn)檫@將導(dǎo)致嚴(yán)重的性能損失。
Comparison: Useless stack trace:
ReplyError:ERRwrongnumberofargumentsfor'set'command atparseError(/home/ruben/repos/redis/node_modules/redis-parser/lib/parser.js:158:12) atparseType(/home/ruben/repos/redis/node_modules/redis-parser/lib/parser.js:219:14)
Good stack trace:
ReplyError:ERRwrongnumberofargumentsfor'set'command atnewCommand(/home/ruben/repos/redis/lib/command.js:9:902) atRedisClient.set(/home/ruben/repos/redis/lib/commands.js:9:3238) atContext.<anonymous>(/home/ruben/repos/redis/test/good_stacks.spec.js:20:20) atcallFnAsync(/home/ruben/repos/redis/node_modules/mocha/lib/runnable.js:349:8) atTest.Runnable.run(/home/ruben/repos/redis/node_modules/mocha/lib/runnable.js:301:7) atRunner.runTest(/home/ruben/repos/redis/node_modules/mocha/lib/runner.js:422:10) at/home/ruben/repos/redis/node_modules/mocha/lib/runner.js:528:12 atnext(/home/ruben/repos/redis/node_modules/mocha/lib/runner.js:342:14) at/home/ruben/repos/redis/node_modules/mocha/lib/runner.js:352:7 atnext(/home/ruben/repos/redis/node_modules/mocha/lib/runner.js:284:14) atImmediate._onImmediate(/home/ruben/repos/redis/node_modules/mocha/lib/runner.js:320:5) atprocessImmediate[as_immediateCallback](timers.js:383:17)
How to Contribute
Open a pull request or an issue about what you want to implement / change. We're glad for any help!
Please be aware that we'll only accept fully tested code.
Contributors
The original author of node_redis isMatthew Ranney
The current lead maintainer isRuben Bridgewater
Manyotherscontributed tonode_redistoo. Thanks to all of them!
License
MIT
Consolidation: It's time for celebration
Right now there are two great redis clients around and both have some advantages above each other. We speak about ioredis and node_redis. So after talking to each other about how we could improve in working together we (that is @luin and @BridgeAR) decided to work towards a single library on the long run. But step by step.
First of all, we want to split small parts of our libraries into others so that we're both able to use the same code. Those libraries are going to be maintained under the NodeRedis organization. This is going to reduce the maintenance overhead, allows others to use the very same code, if they need it and it's way easyer for others to contribute to both libraries.
We're very happy about this step towards working together as we both want to give you the best redis experience possible.
If you want to join our cause by help maintaining something, please don't hesitate to contact either one of us.
總結(jié)
以上是生活随笔為你收集整理的node_redis 中文文档及node_redis 注释笔记(中文版)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Chrome开发者工具Element s
- 下一篇: rxjs of操作符传入数组的单步执行