27

Redis 模块机制 - 模块编写篇

 3 years ago
source link: https://mp.weixin.qq.com/s/YFZoErNF5Y9u1blLB-uOiw
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

在 Redis 应用中,模块机制是提及得比较少的一个功能,主要是 Redis 的功能基本上能应付各种需求,很少需要自己编写模块来扩展功能的。但有时候我们希望为 Redis 提供一些较为高级的功能,比如提供支持回滚的事务功能,这时就需要编写模块来进行扩展。

Redis 为模块编写者提供了丰富的 API 来操纵 Redis,下面我们编写一个简单的 Redis 模块来阐明模块编写的过程。

编写一个简单的 Redis 模块

我们要编写的模块只提供一个简单的功能,就是计算一个数的平方数,命令如下

$ 127.0.0.1:6379> math.double 10
(integer) 100

我们先把模块的代码贴出来,然后再解释代码的意义:

#include "redismodule.h"
#include <stdlib.h>

int MathDouble_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
long long num;

if (argc == 2) {
RedisModule_StringToLongLong(argv[1], &num);
RedisModule_ReplyWithLongLong(ctx, num * num);
return REDISMODULE_OK;
}

return RedisModule_ReplyWithError(ctx, "ERR invalid num parameters");
}

int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
if (RedisModule_Init(ctx, "math", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR)
return REDISMODULE_ERR;

if (RedisModule_CreateCommand(ctx, "math.double",
MathDouble_RedisCommand, "readonly", 1, 1, 1) == REDISMODULE_ERR)
return REDISMODULE_ERR;

return REDISMODULE_OK;
}

上面引入的 redismodule.h 文件是从 Redis 源码包中复制过来的,编写 Redis 模块时必须引入这个文件。

然后我们可以通过以下命令来编译这个模块:

$ gcc math.c -fPIC -shared -o math.so

上面的命令是把模块编译成 .so 文件,也就是共享库文件。然后可以通过 Redis 的 module load 命令来把模块加载到 Redis 中,如下:

$ module load ./math.so
OK

然后可以通过 module list 命令查看 Redis 已经加载的模块列表:

$ module list
1) 1) "name"
2) "math"
3) "ver"
4) (integer) 1

可以看到,我们的 math 模块已经被加载到 Redis 中。

接下来,我们就可以通过 math 模块提供的 math.double 命令来计算一个数的平方数,如下:

$ 127.0.0.1:6379> math.double 10
(integer) 100

如果看到上面的结果,那么恭喜你,因为你编写的模块已经被加入到 Redis 中。

模块代码解释

每个 Redis 模块都需要提供一个 RedisModule_OnLoad() 函数,这个函数是 Redis 加载模块时会调用的函数,也就是说,Redis 加载一个模块时,会调用这个模块的 RedisModule_OnLoad() 函数。 RedisModule_OnLoad() 函数的定义如下:

int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);

函数的第一个参数类型必须是 RedisModuleCtx,表示模块的上下文,主要用来存放模块的一些基本信息。第二个参数类型为 RedisModuleString,表示载入模块时传入的参数列表。第三个参数类型为 int,表示参数列表的个数。

在 RedisModule_OnLoad() 函数中一般需要调用 RedisModule_Init() 函数来向 Redis 注册模块,RedisModule_Init() 函数原型如下:

int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver);

第一个参数的类型前面已经介绍过,第二个参数用于指定模块的名称,第三个参数用于指定模块的版本,而第四个参数用于指定API的版本。

注册完模块后,就可以通过 RedisModule_CreateCommand() 函数来为模块创建命令。RedisModule_CreateCommand() 函数原型如下:

int RedisModule_CreateCommand(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc,
const char *strflags, int firstkey, int lastkey, int keystep);

RedisModule_CreateCommand() 函数稍微有点复杂,下面介绍各个参数的作用:

  • ctx:就是模块的上下文。

  • name:用于指定命令的名称,比如上面我们定义的 math.double

  • cmdfunc:用于指定命令的功能函数,也就是当我们调用一个命令时,就会通过调用这个函数来实现具体的功能。

  • strflags:用于指定命令的功能,比如 write 表示命令对 Redis 进行写操作,而 readonly 表示命令只会读取 Redis 的数据等,为什么需要指定命令的功能呢?因为如果命令会对 Redis 进行写操作,那么 Redis 就需要把命令持久化。

  • firstkeylastkeykeystep:用于指导 command getkeys 命令(command getkeys 命令的使用可以参考 Redis 使用手册)获取 keys 列表时使用的,如果不提供这个功能,可以设置为0。

一个模块最重要的是命令的功能函数,如上面模块中的 MathDouble_RedisCommand() 函数,我们把 MathDouble_RedisCommand() 再展示一遍:

int MathDouble_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
long long num;

if (argc == 2) {
RedisModule_StringToLongLong(argv[1], &num);
RedisModule_ReplyWithLongLong(ctx, num * num);
return REDISMODULE_OK;
}

return RedisModule_ReplyWithError(ctx, "ERR invalid num parameters");
}

一个命令的功能函数的原型如下:

int xxx_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);

函数的名称可以随便定义,不过按照规范,一般以 _RedisCommand 结尾。第一个参数就是模块上下文,而第二个参数是命令的参数列表,第三个参数是参数列表的个数。

在上面的例子中,MathDouble_RedisCommand() 函数首先判断参数个数是否正确,如果不正确就返回 invalid num parameters 的错误信息。然而,Redis 提供了很多 API 让我们使用,比如 RedisModule_StringToLongLong() 函数就是把参数转换成 long long 类型的数值。而 RedisModule_ReplyWithLongLong() 函数可以让 Redis 回复一个整型数值给客户端。

Redis 提供的 API 非常多,具体可以参考 Redis 官网的模块开发介绍或者查看 redismodule.h 文件。Redis 模块编写的介绍就到这里了,下篇会介绍 Redis 模块内部的实现原理。

参考文献:

  1. Redis模块开发手册:Redis Modules: an introduction to the API

  2. Redis模块API手册 Modules API reference


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK