dot 快速的未来正在您的城市举行活动。

加入我们在 Redis 发布会上

模块中包含什么

基本上,模块包含命令处理程序,这些是具有以下签名的 C 函数

int MyCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)

从签名中可以看出,该函数返回一个整数,要么是 OK,要么是 ERR。通常,返回 OK(即使向用户返回错误)是可以的。

命令处理程序接受一个 RedisModuleCtx* 对象。该对象对模块开发者是透明的,但内部它包含调用客户端的状态,甚至内部内存管理,我们将在稍后讨论。接下来,它接收 argvargc 它们基本上是用户传递给被调用的命令的参数。第一个参数是调用本身的名称,其余的只是从 Redis 协议中解析的参数。注意,它们被接收为 RedisModuleString 对象,这些对象同样是透明的。如果需要操作,它们可以转换为没有复制的普通 C 字符串。

要激活模块的命令,模块的标准入口点是一个名为 int RedisModule_OnLoad(RedisModuleCtx *ctx) 的函数。此函数告诉 Redis 模块中包含哪些命令并将它们映射到它们的处理程序。

编写模块

在这个简短的教程中,我们将重点关注一个非常简单的模块示例,它实现了一个新的 Redis 命令: HGETSET <key> <element> <new value>HGETSETHGETHSET 的组合,它允许您原子地检索 HASH 对象中的当前值并设置一个新值来代替它。这非常基本,也可以通过简单的交易或 LUA 脚本完成,但 HGETSET 具有非常简单的优势。

  1. 让我们从一个裸命令处理程序开始
int HGetSetCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
  return REDISMODULE_OK;
}

同样,这目前什么也不做,它只是返回 OK 代码。所以让我们给它一些内容。

  1. 验证参数

请记住,我们的命令是 HGETSET <key> <element> <new value>,这意味着它将始终在 argv 中有四个参数。所以让我们确保这确实发生了

/* HGETSET <key> <element> <new value> */
int HGetSetCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
  if (argc != 4) {
    return RedisModule_WrongArity(ctx);
  }
  Return REDISMODULE_OK;
}

RedisModule_WrongArity 将以以下形式向客户端返回一个标准错误

(error) ERR wrong number of arguments for ‘get’ command.

  1. 激活自动内存

Redis 模块 API 的一项重要功能是自动资源和内存管理。虽然模块作者可以独立地分配和释放内存,但调用 RedisModule_AutoMemory 允许您在处理程序的生命周期内自动创建 Redis 资源并分配 Redis 字符串、键和响应。

RedisModule_AutoMemory(ctx);
  1. 执行 Redis 调用

现在我们将运行两个 Redis 调用中的第一个, HGET。我们将 argv[1] 和 argv[2](键和元素)作为参数传递。我们使用通用的 RedisModule_Call 命令,它允许模块开发者调用任何现有的 Redis 命令,就像 LUA 脚本一样

RedisModuleCallReply *rep = RedisModule_Call(ctx, “HGET”, “ss”, argv[1], argv[2]);
// And let’s make sure it’s not an error
if (RedisModule_CallReplyType(rep) == REDISMODULE_REPLY_ERROR) {
  return RedisModule_ReplyWithCallReply(ctx, srep);
}

请注意, RedisModule_Call 的第三个参数“ss”表示 Redis 应该如何处理传递给该函数的可变参数。 “ss”表示“两个 RedisModuleString 对象”。其他说明符是“c”表示 c 字符串,“d”表示双精度浮点数,“l”表示长整数,“b”表示 c 缓冲区(字符串及其长度)。

现在让我们执行第二个 Redis 调用, HSET

RedisModuleCallReply *srep = RedisModule_Call(ctx, “HSET”, “sss”, argv[1], argv[2], argv[3]);
if (RedisModule_CallReplyType(srep) == REDISMODULE_REPLY_ERROR) {
  return RedisModule_ReplyWithCallReply(ctx, srep);
}

使用 HSETHGET 命令类似,除了我们向它传递三个参数。

  1. 返回结果

在这个简单的情况下,我们只需要返回 HGET 的结果,或者我们在更改它之前的值。这是使用一个简单的函数 RedisModule_ReplyWithCallReply 完成的,该函数将回复对象转发给客户端

RedisModule_ReplyWithCallReply(ctx, rep);
return REDISMODULE_OK;
}

就这样!我们的命令处理程序已准备就绪;我们只需要正确注册我们的模块和命令处理程序。

  1. 初始化模块

所有 Redis 模块的入口点是一个名为 RedisModule_OnLoad 的函数,开发者必须实现它。它注册并初始化模块,并将它的命令注册到 Redis,以便可以调用它们。初始化我们的模块如下所示

int RedisModule_OnLoad(RedisModuleCtx *ctx) {
  // Register the module itself – it’s called ‘example’ and has an API
  version of 1
  if (RedisModule_Init(ctx, “example”, 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR) {
    return REDISMODULE_ERR;
  }

  // register our command – it is a write command, with one key at argv[1]
  if (RedisModule_CreateCommand(ctx, “HGETSET”, HGetSetCommand, “write”, 1, 1, 1) == REDISMODULE_ERR) {
    return REDISMODULE_ERR;
  }
  return REDISMODULE_OK;
}

就这样!我们的模块完成了。

  1. 关于模块构建的一句话

剩下要做的就是编译我们的模块。我不会深入介绍为它创建 Makefile 的细节,但您需要知道的是,Redis 模块不需要特殊的链接。一旦您在模块文件中包含了 redismodule.h 文件并实现了入口点函数,Redis 就需要加载您的模块。任何其他链接都由您决定。使用 gcc 编译基本模块所需的命令是

On Linux:
$ gcc -fPIC -std=gnu99 -c -o module.o module.c
$ ld -o module.so module.o -shared -Bsymbolic -lc

On OSX:
$ gcc -dynamic -fno-common -std=gnu99 -c -o module.o module.c
$ ld -o module.so module.o -bundle -undefined dynamic_lookup -lc
  1. 加载我们的模块

构建模块后,您需要加载它。假设您从其最新的稳定版本(支持模块)下载了 Redis,您只需使用 loadmodule 命令行参数运行它

redis-server –loadmodule /path/to/module.so

Redis 现在正在运行并已加载我们的模块。我们可以简单地使用 redis-cli 连接并运行我们的命令!

获取源代码

此处详细介绍的完整源代码可以在 RedisModuleSDK 中找到,其中包含一个模块项目模板、Makefile 和一个实用程序库(包含自动执行编写模块周围一些更枯燥内容的函数,这些内容不包含在原始 API 中)。您不必使用它,但可以随意使用。我们的模块完成了。