node_redis 中文文档及node_redis 注释笔记(中文版)

node_redis 中文文档及node_redis 注释笔记(中文版)

https://github.com/NodeRedis/node_redis

redis - a node.js redis client

这是node.js的一个完整且功能丰富的Redis客户端。它支持所有的Redis命令,并专注于高性能。

Install with:

npm install redis

Usage Example

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

--------------------------------------------------

Promises

你也可以通过如下方式使用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)
})();

--------------------------------------------------

Sending Commands

发送命令

每个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 Commands

Redis命令

这个库跟 Redis 命令一一映射。请参照Redis命令页获取完整的使用细节。

例如使用SET command设置key值得自动失效时间

// this key will expire after 10 seconds
client.set('key', 'value!', 'EX', 10);

API

Connection and other Events

客户端将会发送一些关于连接到Redis服务器的状态的事件。

"ready"

client 当连接建立后,将触发一次 ready 事件. 在ready 事件之前发出的命令将被排队,ready事件触发后,队列中的命令依次执行。

"connect"

一旦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

--------------------------------------------------

"reconnecting"

在失去连接后,当尝试重新连接到Redis服务器,client将触发reconnecting 事件。监听器将被传递一个包含delay (in ms) 和attempt (the attempt #) 属性的对象。

"error"

当遇到连接到Redis服务器错误,或在node_redis中出现的任何其他错误时,client将触发error事件。如果你使用一个没有回调的命令,或者遇到返回异常时,错误监听器将被触发。

因此,请将错误侦听器附加到node_redis上。

"end"

当已和Redis服务器建立的连接被关闭时,client将触发end事件。

"drain" (deprecated弃用)

当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();
    });
});

--------------------------------------------------

"warning"

当设置密码时,但不需要,并且使用了一个不赞成的选项/功能/类似。client 将触发warning事件。

"idle" (deprecated弃用)

在没有正在等待响应的未完成命令时发出时,client将触发idle事件

redis.createClient()

如果你的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无能为力,因此设置此选项true将导致无论parser选项的值如何,都会使用内置的JavaScript分析器。

return_buffers false

如果设置为true,那么所有的返回值将以Buffers的形式替代Strings发送到回调函数.

detect_buffers false

如果设置为true,则返回值将以Buffers 发送到回调。此选项允许您在每个命令的基础上在Buffers和字符串之间切换,而return_buffers适用于客户端上的每个命令。注意:这与pubsub模式无法正常工作。用户必须总是返回字符串或Buffers。

socket_keepalive true

如果设置为true,则在底层套接字上启用keep-alive功能。

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 INFOcommand indicates whether the server is ready for more commands. When ready, node_redis emits a ready event. Setting no_ready_check to truewill 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_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 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);
    }
});

client.auth(password[, callback])

当连接的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.

--------------------------------------------------

backpressure

stream

client 在client.stream中暴露使用的stream 并且在client.should_buffer中暴露流或者客户端是否不得不被缓存。结合这些,你可以通过在发送一个命令前检查buffer状态并且监听stream的drain事件实现背压。

client.quit()

这会将退出命令发送到redis服务器,并在正确处理所有运行的命令之后以干净的方式结束。如果这是在重新连接时调用的(因此不存在与redis服务器的连接),它将立即终止连接,而不是产生进一步的重新连接!在这种情况下,所有的离线命令都将被一个错误所刷新。

client.end(flush)

强行关闭与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,就不应该在生产中使用!

Error handling (>= v.2.6)

当前存在以下错误子类:

  • 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:	est
edis-test
ode_modules
edis-parserlibparser.js:193:12)
    at parseType (F:	est
edis-test
ode_modules
edis-parserlibparser.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

--------------------------------------------------

client.unref()

在与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)
})

Friendlier hash commands

友好的hash命令

大多数Redis命令都使用单个字符串或字符串数组作为参数,并且返回响应单个字符串或字符串数组。在处理hash 值时,有几个有用的例外。

client.hgetall(hash, callback)

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' }

client.hmset(hash, obj[, callback])

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.

client.hmset(hash, key1, val1, ... keyn, valn, [callback])

Multiple values may also be set by supplying a list:

client.HMSET(key1, "0123456789", "abcdefghij", "some manner of key", "a type of value");

Publish / Subscribe

发布/订阅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())。

Subscriber Events

如果客户端有订阅活动,它可能会发出这些事件:

"message" (channel, message)

客户端将为收到的每一条与活动的订阅相匹配的消息发出message 事件。侦听器参数以channel作为频道名称,并以message作为消息。

"pmessage" (pattern, 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就能接收到这三个频道的消息。

--------------------------------------------------

"message_buffer" (channel, message)

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 messageevent at the same time as the message_buffer, it is always going to emit a string.

(TODO)

"pmessage_buffer" (pattern, channel, message)

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" (channel, count)

客户端根据SUBSCRIBE 命令触发subscribe 事件。侦听器参数以channel作为频道名称,并以count作为新订阅者数量。

"psubscribe" (pattern, count)

客户端根据PSUBSCRIBE 命令触发psubscribe 事件。侦听器参数以pattern作为原始的正则匹配模式,并以count作为新订阅者数量。

"unsubscribe" (channel, count)

客户端根据UNSUBSCRIBE 命令触发unsubscribe 事件。侦听器参数以channel作为频道名称,并以count作为新订阅者数量。当count为0时,客户端将退出订阅者模式,并且不再有订阅者事件触发。

"punsubscribe" (pattern, count)

客户端根据PUNSUBSCRIBE 命令触发punsubscribe 事件。侦听器参数以pattern作为原始的正则匹配模式,并以count作为新订阅者数量。当count为0时,客户端将退出订阅者模式,并且不再有订阅者事件触发。

client.multi([commands])

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());
        });
    });

Multi.exec([callback])

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_atomic([callback])

与Multi.exec类似,但是区别是执行单个命令时不会使用事务。

client.batch([commands])

与 .multi 相同但没有事务。如果您希望同时执行多个命令,但不需要依赖事务,那么建议您这样做。

BATCH 批处理命令在队列中排队等待执行,然后所有的命令都由Redis原子运行。node_redis 中的接口是通过调用client.Batch()来返回一个单独的Batch 处理对象。 .batch 和 .multi的唯一区别是.batch没有事务。注意,错误-就像在multi 语句中一样-返回在结果中。否则,错误和结果都可以同时返回。

如果您同时触发多个命令,那么这与一个循环中执行相同的命令相比将大大提高执行速度,而不需要等待结果!查看benchmarks 获取更多比较信息。请记住,所有的命令都保存在内存中,直到它们被触发。

Monitor mode

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']
});

Extras

还有一些你可能想知道的事情。

client.server_info

在就绪的探测完成之后,INFO命令的结果将保存在client.server_info对象中。

versions 键包含以版本字符串的字符组成的数组中,以便进行比较。

> client.server_info.redis_version
'2.3.0'
> client.server_info.versions
[ 2, 3, 0 ]

redis.print()

一个方便的回调函数,用于在测试时显示返回值。例子:

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

注意,这个程序不会干净地退出,因为客户端仍然是连接的。

Multi-word commands

执行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(...);

client.duplicate([options][, callback])

复制所有当前选项并返回一个新的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都可以使用它自己的连接。

client.send_command(command_name[, [args][, callback]])

所有的Redis命令都被添加到客户端对象中。但是,如果在这个库更新之前引入了新的命令,或者如果您想要添加单独的命令,那么可以使用sendcommand()向Redis发送任意命令。

所有命令都是作为多批量命令发送的。args可以是一组参数,也可以是未定义的参数。

client.add_command(command_name)

调用add_command将向原型添加一个新的命令。在使用这个新命令调用时,将使用精确的命令名。使用任意参数与任何其他命令一样是可能的。

client.connected

跟踪连接到Redis服务器的连接状态的布尔值。

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

已经发送到Redis服务器但还没有回复的命令数量。你可以使用这条命令为 pre-connection命令去执行一些类别的最大队列深度。

Commands with Optional and Keyword arguments

这适用于任何使用一个可选的在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
    });
});

Performance

为了使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

Debugging

为了获得调试输出,您可以在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)

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 is Matthew Ranney

The current lead maintainer is Ruben Bridgewater

Many others contributed to node_redis too. 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.

 

原文地址:https://www.cnblogs.com/wjlbk/p/12633278.html