https://github.com/NodeRedis/node_redis
这是node.js的一个完整且功能丰富的Redis客户端。它支持所有的Redis命令,并专注于高性能。
Install with:
npm install redis
var redis = require("redis"), client = redis.createClient(); // if you'd like to select database 3, instead of 0 (default), call // client.select(3, function() { /* ... */ }); client.on("error", function (err) { console.log("Error " + err); }); client.set("string key", "string val", redis.print); client.hset("hash key", "hashtest 1", "some value", redis.print); client.hset(["hash key", "hashtest 2", "some other value"], redis.print); client.hkeys("hash key", 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)$ node example.js Reply: OK Reply: 0 Reply: 0 2 replies: 0: hashtest 1 1: hashtest 2 mjr:~/work/node_redis (master)$
请注意,该API完全是异步的。您需要使用一个回调,从服务器获取数据。自从2.6版本开始,API支持在所有地方的选项、变量、事件等等使用驼峰和下划线命名规范。不过,建议使用Node.js默认的风格——驼峰式。
--------------------------------------------------
译者注:
关于异步我做了一个如下demo
var redis = require("redis"), client = redis.createClient(); client.set("string key",true,()=>{ console.log(1) }); console.log(2) client.get("string key" ,(err,key)=>{ console.log(3) }); console.log(4)
返回了
2 4 1 3
--------------------------------------------------
你也可以通过如下方式使用bluebird模块,将node_redis promises化:
var redis = require('redis'); bluebird.promisifyAll(redis.RedisClient.prototype); bluebird.promisifyAll(redis.Multi.prototype);
它将在所有node_redis函数后添加一个Async (例如 return client.getAsync().then())
// We expect a value 'foo': 'bar' to be present // So instead of writing client.get('foo', cb); you have to write: return client.getAsync('foo').then(function(res) { console.log(res); // => 'bar' }); // Using multi with promises looks like: return client.multi().get('foo').execAsync().then(function(res) { console.log(res); // => 'bar' });
--------------------------------------------------
译者注:
bluebird装饰后可以结合ES6的await/async使用
var redis = require("redis"), bluebird = require("bluebird"); bluebird.promisifyAll(redis.RedisClient.prototype); bluebird.promisifyAll(redis.Multi.prototype); let client = redis.createClient(); (async function () { await client.setAsync("ip",1,"ex",10); let ip = await client.getAsync("ip"); await client.setAsync("ip",++ip,"ex",10); ip = await client.getAsync("ip"); console.log(ip) })();
--------------------------------------------------
每个Redis命令都作为client
对象上的一个函数暴露出来。所有函数都采用一个args
数组加上可选的callback
函数,或者一个可变数量的的单独参数跟随一个可选的回调。例如:
client.hmset(["key", "test keys 1", "test val 1", "test keys 2", "test val 2"], function (err, res) {}); // Works the same as client.hmset("key", ["test keys 1", "test val 1", "test keys 2", "test val 2"], function (err, res) {}); // Or client.hmset("key", "test keys 1", "test val 1", "test keys 2", "test val 2", 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)
注意,无论哪种形式,回调都是可选的:
client.set("some key", "some val");client.set(["some other key", "some val"]);
如果key不存在,将返回null。只有当Redis Command Reference特别声明,它才不会为空。
client.get("missingkey", function(err, reply) { // reply is null when the key is missing console.log(reply); });
Redis命令列表,请参见 Redis Command Reference
在返回结果中进行了最低限度的解析。命令中,integer 返回Javascript的Numbers, arrays 返回JavaScript Array. HGETALL
返回hash keys作为key的 Object . 所有strings 将返回 string ,亦或者你特别设置返回类型为buffer类型 。请注意:null, undefined 和Boolean 在返回中将强制转换为字符串。
--------------------------------------------------
译者注:
关于Boolean 在返回中将强制转换为字符串。我做了如下demo
var redis = require("redis"), client = redis.createClient(); client.set("string key",true); client.get("string key" ,(err,key)=>{ console.log(typeof key); });
返回
string
--------------------------------------------------
这个库跟 Redis 命令一一映射。请参照Redis命令页获取完整的使用细节。
例如使用SET command设置key值得自动失效时间
// this key will expire after 10 seconds client.set('key', 'value!', 'EX', 10);
客户端将会发送一些关于连接到Redis服务器的状态的事件。
client
当连接建立后,将触发一次 ready
事件. 在ready
事件之前发出的命令将被排队,ready事件触发后,队列中的命令依次执行。
一旦stream连接到服务器,client立即触发connect事件。
--------------------------------------------------
译者注:
关于“ready“,”connect”执行先后问题,设计了如下demo
var redis = require("redis"), client = redis.createClient(); client.on("ready",()=>{ console.log("ready"); }); client.on("connect",()=>{ console.log("connect"); });
结果
connect ready
--------------------------------------------------
在失去连接后,当尝试重新连接到Redis服务器,client将触发reconnecting
事件。监听器将被传递一个包含delay
(in ms) 和attempt
(the attempt #) 属性的对象。
当遇到连接到Redis服务器错误,或在node_redis中出现的任何其他错误时,client将触发error事件。如果你使用一个没有回调的命令,或者遇到返回异常时,错误监听器将被触发。
因此,请将错误侦听器附加到node_redis上。
当已和Redis服务器建立的连接被关闭时,client将触发end事件。
当TCP连接到Redis server曾经被缓存,但现在是可写的(译者注:缓存池排干时)。这个事件将被用于流命令进入Redis,并适应背压。
当流被缓存 client.should_buffer
被设置为 true. 否则变量始终被设置为false。这样你能够决定何时降低发送速率,当触发drain事件时再恢复传输。
您也可以检查每个命令的返回值,因为它也将返回背压指示器(不建议使用)。如果返回false,则流必须缓冲。
--------------------------------------------------
译者注:
关于drain原理,可以参阅http://taobaofed.org/blog/2015/12/31/nodejs-drain/
我写了一个drain测试demo
'use strict'; var redis = require('redis'); var client = redis.createClient({ return_buffers: true }); client.auth("g7845120"); var fs = require('fs'); var filename = 'Funny-Cat-GIFs.jpg'; fs.readFile(filename, function (err, data) { if (err) throw err; console.log('Read ' + data.length + ' bytes from filesystem.'); client.set(filename, data, function () { console.log("set end") }); // set entire file if(client.should_buffer){ client.stream.pause(); } client.stream.on("drain",function () { console.log("drain"); client.stream.resume(); }); });
--------------------------------------------------
当设置密码时,但不需要,并且使用了一个不赞成的选项/功能/类似。client
将触发warning事件。
在没有正在等待响应的未完成命令时发出时,client将触发idle事件
如果你的node服务器和redis服务器运行在同一台机器上,那么默认设置的port和host或许是可用的,并且你不需要提供其他参数。createClient()
返回一个 RedisClient
对象。否则,createClient()
需要接受这些参数:
redis.createClient([options])
redis.createClient(unix_socket[, options])
redis.createClient(redis_url[, options])
redis.createClient(port[, host][, options])
提示:如果Redis服务器与客户端在同一台计算机上运行,请尽可能使用unix套接字来增加吞吐量。
options
object 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 at IANA). |
parser | javascript | Deprecated Use either the built-in JS parser javascript or the native hiredis parser. Note node_redis < 2.6 uses hiredis as default if installed. This changed in v.2.6.0. |
string_numbers | null | 设置为true,node_redis将返回String类型Redis的number值,以替代javascript的Nembers类型。当你需要处理一个大数字时有用(超过Number.MAX_SAFE_INTEGER === 2^53)。Hiredis无能为力,因此设置此选项 |
return_buffers | false | 如果设置为true,那么所有的返回值将以Buffers的形式替代Strings发送到回调函数. |
detect_buffers | false | 如果设置为 |
socket_keepalive | true | 如果设置为 |
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_redis has a "ready check" which sends the INFO command to the server. The response from the INFO command indicates whether the server is ready for more commands. When ready, node_redis emits a ready event. Setting no_ready_check to true will 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. Setting enable_offline_queue to false will 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 | Deprecated Please use retry_strategy instead. By default, every time the client tries to connect and fails, the reconnection delay almost doubles. This delay normally grows infinitely, but setting retry_max_delay limits it to the maximum value provided in milliseconds. |
connect_timeout | 3600000 | Deprecated Please use retry_strategy instead. Setting connect_timeout limits 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 | Deprecated Please use retry_strategy instead. By default, a client will try reconnecting until connected. Setting max_attempts limits total amount of connection attempts. Setting this to 1 will prevent any reconnect attempt. |
retry_unfulfilled_commands | false | If set to true , 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. Alias auth_pass Notenode_redis < 2.5 must use auth_pass |
db | null | If set, client will run Redis select command on connect. |
family | IPv4 | You can force using IPv6 if you set the family to 'IPv6'. See Node.js net or dnsmodules on how to use the family type. |
disable_resubscribing | false | If set to true , 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 the Redis security topics for more info. |
tls | null | An object containing options to pass to tls.connect to 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 the keys command will not be prefixed. The keys command 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 | 这是一个函数,接受一个option对象作为参数,其参数包含重试attempt,指示距离最后一次连接的时间total_retry_time,为什么连接失败的error 和总共重连次数的times_connected 。如果在这个函数中你返回一个数字,那么将在这个数字的毫秒数的时间后尝试重连。如果你返回一个非数字,则不会再发生重连,并且所有脱机命令将会刷新并显示错误。返回一个错误,将特定错误返回给所有脱机命令。下面的例子。 |
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); // Will print `<Buffer 4f 4b>` // 译者注:官网的代码是console.log(reply.toString()); 这会输出ok 而不是`<Buffer 4f 4b>` }); client.quit();
retry_strategy example
var client = redis.createClient({ retry_strategy: function (options) { if (options.error && options.error.code === 'ECONNREFUSED') { // End reconnecting on a specific error and flush all commands with // a individual error return new Error('The server refused the connection'); } if (options.total_retry_time > 1000 * 60 * 60) { // End reconnecting after a specific timeout and flush all commands // with a individual error return new Error('Retry time exhausted'); } if (options.attempt > 10) { // End reconnecting with built in error return undefined; } // reconnect after return Math.min(options.attempt * 100, 3000); } });
当连接的Redis服务器需要安全认证,AUTH
命令必须在连接后作为第一条命令被发送。这可能需要与重新连接、就绪的检查等进行协调。为了更方便,client.auth()储存password ,并在以后的每个连接,包括重连时发送它。回调只在对第一个AUTH命令发出的响应之后才调用一次。注意:你调用client.auth() 不应该在ready事件的处理函数中。如果你执行了这个错误,client将触发类似这样的错误:Ready check failed: ERR operation not permitted.
--------------------------------------------------
译者注:
var redis = require("redis") let client = redis.createClient(); client.on("ready",()=>{ console.log("ready") client.auth("g7845120"); });
返回
ReplyError: Ready check failed: NOAUTH Authentication required.
--------------------------------------------------
client 在client.stream中暴露使用的stream 并且在client.should_buffer中暴露流或者客户端是否不得不被缓存。结合这些,你可以通过在发送一个命令前检查buffer状态并且监听stream的drain事件实现背压。
这会将退出命令发送到redis服务器,并在正确处理所有运行的命令之后以干净的方式结束。如果这是在重新连接时调用的(因此不存在与redis服务器的连接),它将立即终止连接,而不是产生进一步的重新连接!在这种情况下,所有的离线命令都将被一个错误所刷新。
强行关闭与Redis服务器的连接。注意,这不会等到所有的回复都被解析后才会出现。如果您想要干净地退出,请调用上述client.quit ()。
如果您不完全确定您不关心其他命令,那么您应该将flush 设置为true。如果您将flush 设置为false,所有仍然运行的命令将会无声地失败。
这个例子在读取回复之前关闭了与Redis服务器的连接。你可能不想这样做:
var redis = require("redis"), client = redis.createClient(); client.set("foo_rand000000000000", "some fantastic value", function (err, reply) { // This will either result in an error (flush parameter is set to true) // or will silently fail and this callback will not be called at all (flush set to false) console.log(err); }); client.end(true); // No further commands will be processed client.get("foo_rand000000000000", function (err, reply) { console.log(err); // => 'The connection has already been closed.' });
client.end()如果不将flush 参数设置为true,就不应该在生产中使用!
当前存在以下错误子类:
RedisError
: 客户端返回的所有错误
ReplyError
RedisError的子类
: All errors returned by Redis itself
AbortError
RedisError的子类: 由于某种原因所有的命令无法完成而中断
ParserError
RedisError的子类:返回解析错误时返回(这不应该发生)
AggregateError
RedisError的子类:如果没有回调的多个未解决的命令被释放,在调试模式中会被rejected,而不是大量的AbortErrors。
所有的错误类都是由模块导出的。
Example:
var redis = require('./'); var assert = require('assert'); var client = redis.createClient(); client.on('error', function (err) { assert(err instanceof Error); assert(err instanceof redis.AbortError); assert(err instanceof redis.AggregateError); // The set and get get aggregated in here assert.strictEqual(err.errors.length, 2); assert.strictEqual(err.code, 'NR_CLOSED'); }); client.set('foo', 123, 'bar', function (err, res) { // Too many arguments assert(err instanceof redis.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 () { // Force closing the connection while the command did not yet return client.end(true); redis.debug_mode = false; }); });
每个ReplyError都包含有全大写的command名和参数(args)。
--------------------------------------------------
译者注:
var redis = require('redis'); var assert = require('assert'); var client = redis.createClient(); client.set('foo', 123, 'bar', function (err, res) { // Too many arguments assert(err instanceof redis.ReplyError); // => true console.log(err) console.log("command:" + err.command); console.log("args:",err.args); console.log("code:" + err.code); });
返回
{ ReplyError: ERR syntax error at parseError (F:\test\redis-test\node_modules\redis-parser\lib\parser.js:193:12) at parseType (F:\test\redis-test\node_modules\redis-parser\lib\parser.js:303:14) command: 'SET', args: [ 'foo', 123, 'bar' ], code: 'ERR' } command:SET args: [ 'foo', 123, 'bar' ] code:ERR
--------------------------------------------------
如果node_redis 由于其他错误而发出库错误,则将触发错误添加到返回的错误中,作为origin属性。
Error codes
如果客户端连接失败,noderedis返回一个NR_CLOSED 的错误代码。如果一个命令未解决的命令被拒绝,则返回一个UNCERTAIN_STATE 的代码。如果节点redis放弃了重新连接,则使用一个CONNECTION_BROKEN 的错误代码。
--------------------------------------------------
译者注:
var redis = require('redis'); var assert = require('assert'); var client = redis.createClient(); client.on('error', function (err) { console.log(err.code) }); redis.debug_mode = true; client.set('foo', 'bar'); process.nextTick(function () { // Force closing the connection while the command did not yet return client.end(true); redis.debug_mode = false; });
返回
NR_CLOSED
--------------------------------------------------
在与Redis服务器的底层套接字连接上调用unref(),允许程序在不需要更多命令的情况下退出。
这是一个实验性的特性,并且只支持Redis协议的一个子集。任何在Redis服务器上保存客户端状态的命令,例如订阅或阻塞的BL命令都不能与.unref()一起工作。
var redis = require("redis") var client = redis.createClient() /* Calling unref() will allow this program to exit immediately after the get command finishes. Otherwise the client would hang as long as the client-server connection is alive. 调用unref()将允许该程序在get命令完成之后立即退出 。否则客户端和服务器连接将一直保持。 */ client.unref() client.get("foo", function (err, value){ if (err) throw(err) console.log(value) })
大多数Redis命令都使用单个字符串或字符串数组作为参数,并且返回响应单个字符串或字符串数组。在处理hash 值时,有几个有用的例外。
HGETALL 命令的响应将会被node_redis转换为JavaScript对象。这样,你就能够用JavaScript语法与响应进行交互了。
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' }
Multiple values in a hash can be set by supplying an object:
client.HMSET(key2, { "0123456789": "abcdefghij", // NOTE: key and value will be coerced to strings "some manner of key": "a type of value" });
The properties and values of this Object will be set as keys and values in the Redis hash.
Multiple values may also be set by supplying a list:
client.HMSET(key1, "0123456789", "abcdefghij", "some manner of key", "a type of value");
发布/订阅API的例子。该程序打开两个客户端连接,订阅其中一个通道,并在另一个通道上发布该通道:
var redis = require("redis"); var sub = redis.createClient(), pub = redis.createClient(); var msg_count = 0; sub.on("subscribe", function (channel, count) { pub.publish("a nice channel", "I am sending a message."); pub.publish("a nice channel", "I am sending a second message."); pub.publish("a nice channel", "I am sending my last message."); }); sub.on("message", function (channel, message) { console.log("sub channel " + channel + ": " + message); msg_count += 1; if (msg_count === 3) { sub.unsubscribe(); sub.quit(); pub.quit(); } }); sub.subscribe("a nice channel");
--------------------------------------------------
译者注:
var redis = require("redis"); var sub = redis.createClient(), pub = redis.createClient(); var msg_count = 0; sub.on("subscribe", function (channel, count) { pub.publish("a nice channel", "I am sending a message."); pub.publish("a nice channel", "I am sending a second message."); pub.publish("a nice channel", "I am sending my last message."); pub.publish("a nice channel", "I am sending my last message.");//仍会接收 setTimeout(()=>{ pub.publish("a nice channel", "I am sending my last message.");//将会遗弃 },1000) }); sub.on("message", function (channel, message) { console.log("sub channel " + channel + ": " + message); msg_count += 1; if (msg_count === 3) { sub.unsubscribe(); sub.quit(); } }); sub.subscribe("a nice channel");
--------------------------------------------------
当客户机发出订阅或订阅时,该连接将被放入“订阅者”模式。在这一点上,只有修改订阅集的命令是有效的(TODO)。当订阅集为空时,连接将被放回常规模式。
如果您需要在订阅模式下向Redis发送常规命令,只需打开另一个与新客户机的连接(提示:使用client.duplicate())。
如果客户端有订阅活动,它可能会发出这些事件:
客户端将为收到的每一条与活动的订阅相匹配的消息发出message 事件。侦听器参数以channel作为频道名称,并以message作为消息。
客户端将为收到的每一条与活动订阅模式相匹配的消息发出pmessage事件。侦听器参数以PSUBSCRIBE作为原始的正则匹配模式 、以channel作为频道名称,并以message作为消息。
--------------------------------------------------
译者注:
源码中查找demo如下
'use strict'; var redis = require('redis'); var client1 = redis.createClient(); var client2 = redis.createClient(); var client3 = redis.createClient(); var client4 = redis.createClient(); var msg_count = 0; client1.on('psubscribe', function (pattern, count) { console.log('client1 psubscribed to ' + pattern + ', ' + count + ' total subscriptions'); client2.publish('channeltwo', 'Me!'); client3.publish('channelthree', 'Me too!'); client4.publish('channelfour', 'And me too!'); }); client1.on('punsubscribe', function (pattern, count) { console.log('client1 punsubscribed from ' + pattern + ', ' + count + ' total subscriptions'); client4.end(); client3.end(); client2.end(); client1.end(); }); client1.on('pmessage', function (pattern, channel, message) { console.log('(' + pattern + ') client1 received message on ' + channel + ': ' + message); msg_count += 1; if (msg_count === 3) { client1.punsubscribe(); } }); client1.psubscribe('channel*');
由于channel*正则匹配了channeltwo、channelthree、channelfour。client1就能接收到这三个频道的消息。
--------------------------------------------------
This is the same as the message
event with the exception, that it is always going to emit a buffer. If you listen to the message
event at the same time as the message_buffer
, it is always going to emit a string.
(TODO)
This is the same as the pmessage
event with the exception, that it is always going to emit a buffer. If you listen to the pmessage
event at the same time as the pmessage_buffer
, it is always going to emit a string.
(TODO)
客户端根据SUBSCRIBE
命令触发subscribe
事件。侦听器参数以channel作为频道名称,并以count作为新订阅者数量。
客户端根据PSUBSCRIBE
命令触发psubscribe
事件。侦听器参数以pattern作为原始的正则匹配模式,并以count作为新订阅者数量。
客户端根据UNSUBSCRIBE
命令触发unsubscribe
事件。侦听器参数以channel作为频道名称,并以count作为新订阅者数量。当count为0时,客户端将退出订阅者模式,并且不再有订阅者事件触发。
客户端根据PUNSUBSCRIBE
命令触发punsubscribe
事件。侦听器参数以pattern作为原始的正则匹配模式,并以count作为新订阅者数量。当count为0时,客户端将退出订阅者模式,并且不再有订阅者事件触发。
MULTI命令排队直到一个EXEC 命令被执行,然后所有的命令都由Redis原子运行。node_redis 的接口是通过调用client.multi()返回一个单独的Multi 对象。如果队列中任何命令执行失败,那么所有命令都会被回滚,并且不会执行任何操作(更多信息查看 transactions)。
var redis = require("./index"), client = redis.createClient(), set_size = 20; client.sadd("bigset", "a member"); client.sadd("bigset", "another member"); while (set_size > 0) { client.sadd("bigset", "member " + set_size); set_size -= 1; } // multi chain with an individual callback client.multi() .scard("bigset") .smembers("bigset") .keys("*", function (err, replies) { // NOTE: code in this callback is NOT atomic // this only happens after the the .exec call finishes. client.mget(replies, redis.print); }) .dbsize() .exec(function (err, replies) { console.log("MULTI got " + replies.length + " replies"); replies.forEach(function (reply, index) { console.log("Reply " + index + ": " + reply.toString()); }); });
client.multi()是一个返回Multi 对象的构造函数。Multi 对象与client 对象共享所有相同的命令方法。在multi对象中,直到multi.exec()被调用,命令才被调用。
如果您的代码包含一个语法错误,那么将会抛出一个EXECABORT 错误,所有的命令都将被中止。那个错误包含一个.errors属性以描述具体的错误。如果所有命令都成功地排队,并且并且一个错误在redis执行命令的过程中被抛出,那么错误将在结果数组中被返回!除了onces失败之外,其他命令不会被中止。
您可以像上面的示例中将多个命令链接,或者您仍然可以如以下例子那样,排列并发送单条普通命令,
var redis = require("redis"), client = redis.createClient(), multi; // start a separate multi command queue multi = client.multi(); multi.incr("incr thing", redis.print); multi.incr("incr other thing", redis.print); // runs immediately client.mset("incr thing", 100, "incr other thing", 1, redis.print); // drains multi queue and runs atomically multi.exec(function (err, replies) { console.log(replies); // 101, 2 });
除了向多队列添加命令之外,还可以向构造函数传递一个命令和参数数组:
var redis = 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类似,但是区别是执行单个命令时不会使用事务。
与 .multi 相同但没有事务。如果您希望同时执行多个命令,但不需要依赖事务,那么建议您这样做。
BATCH
批处理命令在队列中排队等待执行,然后所有的命令都由Redis原子运行。node_redis
中的接口是通过调用client.Batch()来返回一个单独的Batch
处理对象。 .batch 和 .multi的唯一区别是.batch没有事务。注意,错误-就像在multi 语句中一样-返回在结果中。否则,错误和结果都可以同时返回。
如果您同时触发多个命令,那么这与一个循环中执行相同的命令相比将大大提高执行速度,而不需要等待结果!查看benchmarks 获取更多比较信息。请记住,所有的命令都保存在内存中,直到它们被触发。
Redis支持MONITOR
命令,它让您可以看到所有客户端连接的所有命令,包括来自其他客户端库和其他计算机。
对于连接到服务器的任何客户端发出的每个命令,都会发出一个monitor
事件,包括monitoring 客户端本身。monitor事件的回调从Redis服务器获取时间戳,一个命令参数数组和原始监控字符串。
Example:
var client = require("redis").createClient(); client.monitor(function (err, res) { console.log("Entering monitoring mode."); }); client.set('foo', 'bar'); client.on("monitor", function (time, args, raw_reply) { console.log(time + ": " + args); // 1458910076.446514:['set', 'foo', 'bar'] });
还有一些你可能想知道的事情。
在就绪的探测完成之后,INFO命令的结果将保存在client.server_info对象中。
versions 键包含以版本字符串的字符组成的数组中,以便进行比较。
> client.server_info.redis_version '2.3.0' > client.server_info.versions [ 2, 3, 0 ]
一个方便的回调函数,用于在测试时显示返回值。例子:
var redis = require("redis"), client = redis.createClient(); client.on("connect", function () { client.set("foo_rand000000000000", "some fantastic value", redis.print); client.get("foo_rand000000000000", redis.print); });
This will print:
Reply: OK Reply: some fantastic value
注意,这个程序不会干净地退出,因为客户端仍然是连接的。
执行redis的multi-word命令,如SCRIPT LOAD或CLIENT LIST,将第二个单词作为第一个参数传递:
client.script('load', 'return 1'); client.multi().script('load', 'return 1').exec(...); client.multi([['script', 'load', 'return 1']]).exec(...);
复制所有当前选项并返回一个新的redisClient实例。传递给duplicate 函数的所有选项都将替换原来的选项。如果您传递一个回调,duplicate 将等待客户端准备好并在回调中返回它。如果与此同时发生错误,则会返回一个错误,而不是在回调中。
使用duplicate()的一个例子包含如下连接——阻塞的redis命令BRPOP、BLPOP和BRPOPLPUSH。如果这些命令在与非阻塞命令相同的redisClient实例上使用,则非阻塞的命令可能会排队直到阻塞的命令结束。
var Redis=require('redis'); var client = Redis.createClient(); var clientBlocking = client.duplicate(); var get = function() { console.log("get called"); client.get("any_key",function() { console.log("get returned"); }); setTimeout( get, 1000 ); }; var brpop = function() { console.log("brpop called"); clientBlocking.brpop("nonexistent", 5, function() { console.log("brpop return"); setTimeout( brpop, 1000 ); }); }; get(); brpop();
使用duplicate()的另一个原因是,通过redis SELECT命令访问同一个服务器上的多个DBs。每个DB都可以使用它自己的连接。
所有的Redis命令都被添加到客户端对象中。但是,如果在这个库更新之前引入了新的命令,或者如果您想要添加单独的命令,那么可以使用sendcommand()向Redis发送任意命令。
所有命令都是作为多批量命令发送的。args可以是一组参数,也可以是未定义的参数。
调用add_command将向原型添加一个新的命令。在使用这个新命令调用时,将使用精确的命令名。使用任意参数与任何其他命令一样是可能的。
跟踪连接到Redis服务器的连接状态的布尔值。
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.
已经发送到Redis服务器但还没有回复的命令数量。你可以使用这条命令为 pre-connection命令去执行一些类别的最大队列深度。
这适用于任何使用一个可选的在redis.io/commands文档中的[WITHSCORES]
或[LIMIT offset count] 。
Example:
var args = [ 'myzset', 1, 'one', 2, 'two', 3, 'three', 99, 'ninety-nine' ]; client.zadd(args, function (err, response) { if (err) throw err; console.log('added '+response+' items.'); // -Infinity and +Infinity also work var args1 = [ 'myzset', '+inf', '-inf' ]; client.zrevrangebyscore(args1, function (err, response) { if (err) throw err; console.log('example1', response); // write your code here }); var max = 3, min = 1, offset = 1, count = 2; var args2 = [ 'myzset', max, min, 'WITHSCORES', 'LIMIT', offset, count ]; client.zrevrangebyscore(args2, function (err, response) { if (err) throw err; console.log('example2', response); // write your code here }); });
为了使node_redis尽可能快地进行普通操作,花费了大量的精力。
Lenovo T450s, i7-5600U and 12gb memory clients: 1, NodeJS: 6.2.0, Redis: 3.2.0, parser: javascript, connected by: tcp PING, 1/1 avg/max: 0.02/ 5.26 2501ms total, 46916 ops/sec PING, batch 50/1 avg/max: 0.06/ 4.35 2501ms total, 755178 ops/sec SET 4B str, 1/1 avg/max: 0.02/ 4.75 2501ms total, 40856 ops/sec SET 4B str, batch 50/1 avg/max: 0.11/ 1.51 2501ms total, 432727 ops/sec SET 4B buf, 1/1 avg/max: 0.05/ 2.76 2501ms total, 20659 ops/sec SET 4B buf, batch 50/1 avg/max: 0.25/ 1.76 2501ms total, 194962 ops/sec GET 4B str, 1/1 avg/max: 0.02/ 1.55 2501ms total, 45156 ops/sec GET 4B str, batch 50/1 avg/max: 0.09/ 3.15 2501ms total, 524110 ops/sec GET 4B buf, 1/1 avg/max: 0.02/ 3.07 2501ms total, 44563 ops/sec GET 4B buf, batch 50/1 avg/max: 0.10/ 3.18 2501ms total, 473171 ops/sec SET 4KiB str, 1/1 avg/max: 0.03/ 1.54 2501ms total, 32627 ops/sec SET 4KiB str, batch 50/1 avg/max: 0.34/ 1.89 2501ms total, 146861 ops/sec SET 4KiB buf, 1/1 avg/max: 0.05/ 2.85 2501ms total, 20688 ops/sec SET 4KiB buf, batch 50/1 avg/max: 0.36/ 1.83 2501ms total, 138165 ops/sec GET 4KiB str, 1/1 avg/max: 0.02/ 1.37 2501ms total, 39389 ops/sec GET 4KiB str, batch 50/1 avg/max: 0.24/ 1.81 2501ms total, 208157 ops/sec GET 4KiB buf, 1/1 avg/max: 0.02/ 2.63 2501ms total, 39918 ops/sec GET 4KiB buf, batch 50/1 avg/max: 0.31/ 8.56 2501ms total, 161575 ops/sec INCR, 1/1 avg/max: 0.02/ 4.69 2501ms total, 45685 ops/sec INCR, batch 50/1 avg/max: 0.09/ 3.06 2501ms total, 539964 ops/sec LPUSH, 1/1 avg/max: 0.02/ 3.04 2501ms total, 41253 ops/sec LPUSH, batch 50/1 avg/max: 0.12/ 1.94 2501ms total, 425090 ops/sec LRANGE 10, 1/1 avg/max: 0.02/ 2.28 2501ms total, 39850 ops/sec LRANGE 10, batch 50/1 avg/max: 0.25/ 1.85 2501ms total, 194302 ops/sec LRANGE 100, 1/1 avg/max: 0.05/ 2.93 2501ms total, 21026 ops/sec LRANGE 100, batch 50/1 avg/max: 1.52/ 2.89 2501ms total, 32767 ops/sec SET 4MiB str, 1/1 avg/max: 5.16/ 15.55 2502ms total, 193 ops/sec SET 4MiB str, batch 20/1 avg/max: 89.73/ 99.96 2513ms total, 223 ops/sec SET 4MiB buf, 1/1 avg/max: 2.23/ 8.35 2501ms total, 446 ops/sec SET 4MiB buf, batch 20/1 avg/max: 41.47/ 50.91 2530ms total, 482 ops/sec GET 4MiB str, 1/1 avg/max: 2.79/ 10.91 2502ms total, 358 ops/sec GET 4MiB str, batch 20/1 avg/max: 101.61/118.11 2541ms total, 197 ops/sec GET 4MiB buf, 1/1 avg/max: 2.32/ 14.93 2502ms total, 430 ops/sec GET 4MiB buf, batch 20/1 avg/max: 65.01/ 84.72 2536ms total, 308 ops/sec
为了获得调试输出,您可以在node_redis应用程序中使用NODE_DEBUG=redis。
这也会导致好的堆栈跟踪,而不是无用的堆栈跟踪,否则对于任何异步操作都是如此。如果您只想拥有好的堆栈跟踪,而不是调试输出,请在开发模式中运行您的应用程序(NODE_ENV=development)。
好的堆栈跟踪只在开发和调试模式中被激活,因为这将导致严重的性能损失。
Comparison: Useless stack trace:
ReplyError: ERR wrong number of arguments for 'set' command at parseError (/home/ruben/repos/redis/node_modules/redis-parser/lib/parser.js:158:12) at parseType (/home/ruben/repos/redis/node_modules/redis-parser/lib/parser.js:219:14)
Good stack trace:
ReplyError: ERR wrong number of arguments for 'set' command at new Command (/home/ruben/repos/redis/lib/command.js:9:902) at RedisClient.set (/home/ruben/repos/redis/lib/commands.js:9:3238) at Context.<anonymous> (/home/ruben/repos/redis/test/good_stacks.spec.js:20:20) at callFnAsync (/home/ruben/repos/redis/node_modules/mocha/lib/runnable.js:349:8) at Test.Runnable.run (/home/ruben/repos/redis/node_modules/mocha/lib/runnable.js:301:7) at Runner.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 at next (/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 at next (/home/ruben/repos/redis/node_modules/mocha/lib/runner.js:284:14) at Immediate._onImmediate (/home/ruben/repos/redis/node_modules/mocha/lib/runner.js:320:5) at processImmediate [as _immediateCallback] (timers.js:383:17)
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.
The original author of node_redis is Matthew Ranney
The current lead maintainer is Ruben Bridgewater
Many others contributed to node_redis
too. Thanks to all of them!
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.