nodejs redis 客户端 ioredis 源码分析
周末有空,阅读了 nodejs的redis客户端 ioredis 源码, 分享一下。
仓库地址 https:// github.com/luin/ioredis
依赖库
ioredis 依赖了node-redis-parser 用来解析 redis REPS 协议,依赖 redis-commands 获取 redis 支持的所有命令
redis 协议解析库 https:// github.com/NodeRedis/no de-redis-parser
redis 命令库 https:// github.com/NodeRedis/re dis-commands
源码
redis 命令定义
- lib/redis/index.ts 文件定义了客户端初始化逻辑
function Redis() {
this.parseOptions(arguments[0], arguments[1], arguments[2]);
// 如果 options 中提供了 Connector,则通过 Connector 中的 连接器创建
if (this.options.Connector) {
this.connector = new this.options.Connector(this.options);
} else if (this.options.sentinels) {
// 如果开启了哨兵模式,则使用哨兵连接器
const sentinelConnector = new SentinelConnector(this.options);
sentinelConnector.emitter = this;
this.connector = sentinelConnector;
} else {
// 使用标准连接器
this.connector = new StandaloneConnector(this.options);
this.connect().catch(noop);
- lib/commander.ts 文件定义了 redis 客户端支持的命令
第三方库 redis-commands 中定义了redis支持的所有命令
const commands = require("redis-commands").list
获取redis支持的所有命令并定义执行函数
commands.forEach(function (commandName) {
Commander.prototype[commandName] = generateFunction(
commandName,
commandName,
"utf8"
Commander.prototype[commandName + "Buffer"] = generateFunction(
commandName + "Buffer",
commandName,
这里可以看到每个 redis 命令执行的时候调用了 redis.sendCommand() 方法
// 每个 redis 支持的方法都是通过 generateFunction 函数动态生成的
function generateFunction( functionName: string,) {
return function (...args) {
return this.sendCommand(
new Command(commandName, args, options, callback)
redis 客户端启动流程
所在文件 lib/redis/index.ts
- 创建 redis 客户端对象
new Redis({ host, port })
-
根据 Options 选择连接器
-
SentinelConnector 如果开启了哨兵模式
-
StandaloneConnector 标准连接模式(默认)
- 调用 connect() 函数,连接到 redis 服务,将 连接成功的 socket 对象保存到 Redis 的 stream 属性。
- connect 函数中会创建 connectHandler 连接管理器来管理当前连接的状态变化 connect ->connecting->reply
- connectHandler 方法中会创建 DataHandler 来接口 redis 服务器发送回来的结果
- 后续的读写操作都通过 stream。
单个命令执行过程分析
调用单个 redis 命令时,最终会调用 command.ts文件中生成的闭包函数
const result = await redis.set('name', 'xixi')
闭包函数中调用都会转化成调用 redis 的 sendCommand 方法
// 每个 redis 支持的方法都是通过 generateFunction 函数动态生成的
function generateFunction( functionName: string,) {
return function (...args) {
return this.sendCommand(
new Command(commandName, args, options, callback)
sendCommand 内部将命令转成字符串或者 buffer,然后通过 connect() 时创建的 Socket 流,将命令发送到 redis服务器
并将当前发送的命令push 到命令执行队列,返回当前执行命令的 promise 对象
(stream || this.stream).write(command.toWritable());
// commandQueue 队列中 Push 进去
this.commandQueue.push({
command: command,
stream: stream,
select: this.condition.select,
return command.promise
DataHandler.ts 订阅 stream (就是 connect() 时创建的 Socket 流),接收到数据调用 RedisParser 解析数据
所在文件 lib/DataHandler.ts
redis.stream.on("data", (data) => {
parser.execute(data);
// redisParser成功解析数据后,会将数据发送到 returnReply 方法
returnReply(reply: ReplyData) {