Redis 模块 API
编写 Redis 模块简介
模块文档由以下页面组成:
- Redis 模块简介(此文件)。有关 Redis Modules 系统和 API 的概述。从这里开始阅读是个好主意。
- 实现本机数据类型包括将本机数据类型实现到模块中。
- 阻塞作 展示了如何编写不会立即回复,但会阻塞客户端,而不会阻塞 Redis 服务器,并在可能的情况下提供回复。
- Redis 模块 API 参考是从 RedisModule 函数的 module.c top comments 生成的。为了了解每个函数的工作原理,这是一个很好的参考。
Redis 模块可以使用外部 模块,快速实现具有 类似于 Core 本身内部可以做的事情。
Redis 模块是动态库,可以在
startup 或使用MODULE LOAD
命令。Redis 导出一个 C API,在
形式,称为redismodule.h
.模块是指
用 C 编写,但是可以使用 C++ 或其他语言
具有 C 绑定功能。
模块的设计是为了加载到不同版本的 Redis 中, 因此,不需要设计或重新编译给定的模块,以便 使用特定版本的 Redis 运行。因此,该模块将 使用特定的 API 版本注册到 Redis 核心。当前 API version 为 “1”。
本文档介绍了 Redis 模块的 Alpha 版本。API, 功能 其他详细信息将来可能会更改。
加载模块
为了测试您正在开发的模块,您可以加载模块
使用以下redis.conf
配置指令:
loadmodule /path/to/mymodule.so
也可以使用以下命令在运行时加载模块:
MODULE LOAD /path/to/mymodule.so
为了列出所有加载的模块,请使用:
MODULE LIST
最后,您可以使用 以下命令:
MODULE UNLOAD mymodule
请注意,mymodule
above 不是没有.so
suffix 的 URL 中,但
相反,模块用于将自身注册到 Redis 核心的名称。
可以使用MODULE LIST
.但是,这是很好的做法
动态库的文件名与模块的名称相同
用于将自身注册到 Redis 核心中。
您可以编写的最简单的模块
为了展示模块的不同部分,这里我们将展示一个非常 simple 模块,该模块实现输出随机数的命令。
#include "redismodule.h"
#include <stdlib.h>
int HelloworldRand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
RedisModule_ReplyWithLongLong(ctx,rand());
return REDISMODULE_OK;
}
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
if (RedisModule_Init(ctx,"helloworld",1,REDISMODULE_APIVER_1)
== REDISMODULE_ERR) return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"helloworld.rand",
HelloworldRand_RedisCommand, "fast random",
0, 0, 0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
return REDISMODULE_OK;
}
示例模块有两个功能。一个实现一个名为
HELLOWORLD 的 THE Package。兰特。此功能特定于该模块。但是,
其他名为RedisModule_OnLoad()
必须存在于每个
Redis 模块。它是要初始化的模块的入口点,
注册其命令,并可能注册其他私有数据结构
它使用。
请注意,模块最好使用
模块的名称,后跟一个点,最后是命令名称
就像在HELLOWORLD.RAND
.这样就不太可能
有冲突。
请注意,如果不同的模块有冲突的命令,它们将不会
能够同时在 Redis 中工作,因为函数RedisModule_CreateCommand
将在其中一个模块中失败,因此该模块
loading 将中止返回 Error 条件。
模块初始化
上面的示例显示了函数的用法RedisModule_Init()
.
它应该是模块调用的第一个函数OnLoad
功能。
以下是函数原型:
int RedisModule_Init(RedisModuleCtx *ctx, const char *modulename,
int module_version, int api_version);
这Init
函数宣布模块具有给定的 Redis 核心
name,其版本(由MODULE LIST
),并且愿意
以使用特定版本的 API。
如果 API 版本错误,则名称已被占用,或者存在其他
类似的错误,该函数将返回REDISMODULE_ERR
和OnLoad
函数应返回 ASAP 但出现错误。
在Init
function 时,不能调用其他 API 函数,
否则模块将出现段错误,并且 Redis 实例将崩溃。
第二个函数调用RedisModule_CreateCommand
,按顺序使用
将命令注册到 Redis 核心中。以下是原型:
int RedisModule_CreateCommand(RedisModuleCtx *ctx, const char *name,
RedisModuleCmdFunc cmdfunc, const char *strflags,
int firstkey, int lastkey, int keystep);
如您所见,大多数 Redis 模块 API 调用都作为第一个参数
这context
的 Module,以便它们具有对 module 的引用
调用它,命令和执行给定命令的客户端,依此类推。
要创建新命令,上述函数需要上下文,命令的 name、指向实现命令的函数的指针、命令的标志 以及键名称在命令参数中的位置。
实现该命令的函数必须具有以下原型:
int mycommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
命令函数参数只是将要传递的上下文 对于所有其他 API 调用,命令参数 vector 和 total number of 参数。
如您所见,参数作为指向特定数据的指针提供
type 中,使用RedisModuleString
.这是您具有的不透明数据类型 API
函数来访问和使用,则永远不需要直接访问其字段。
放大示例命令实现,我们可以找到另一个调用:
int RedisModule_ReplyWithLongLong(RedisModuleCtx *ctx, long long integer);
此函数将一个整数返回给调用命令
与其他 Redis 命令完全相同,例如INCR
或SCARD
.
模块清理
在大多数情况下,不需要进行特殊清理。
当一个模块被卸载时,Redis 会自动取消注册命令,并且
取消订阅通知。
但是,如果模块包含一些持久内存或
configuration 中,模块可以包含一个可选的RedisModule_OnUnload
功能。
如果模块提供了此函数,则会在模块卸载时调用
过程。
以下是函数原型:
int RedisModule_OnUnload(RedisModuleCtx *ctx);
这OnUnload
函数可以通过返回REDISMODULE_ERR
.
否则REDISMODULE_OK
应返回。
Redis 模块的设置和依赖项
Redis 模块不依赖于 Redis 或其他库,也不依赖于 Redis
需要用特定的redismodule.h
文件。挨次
要创建新模块,只需复制最新版本的redismodule.h
在你的源代码树中,链接你想要的所有库,然后创建
一个动态库,具有RedisModule_OnLoad()
功能符号
出口。
该模块将能够加载到不同版本的 Redis 中。
模块可以设计为支持较新和较旧的 Redis 版本,其中某些 API 函数并非在所有版本中都可用。 如果 API 函数未在当前运行的 Redis 版本中实现,则函数指针设置为 NULL。 这允许模块在使用函数之前检查函数是否存在:
if (RedisModule_SetCommandInfo != NULL) {
RedisModule_SetCommandInfo(cmd, &info);
}
在最新版本的redismodule.h
、便利宏RMAPI_FUNC_SUPPORTED(funcname)
已定义。
使用宏或仅与 NULL 进行比较是个人喜好的问题。
将配置参数传递给 Redis 模块
当模块加载了MODULE LOAD
命令,或使用loadmodule
指令中的redis.conf
文件,用户能够将
通过在模块后添加参数来配置参数
文件名:
loadmodule mymodule.so foo bar 1234
在上面的示例中,字符串foo
,bar
和1234
将传递
前往模块OnLoad()
函数中的argv
参数作为数组
的 RedisModuleString 指针。传递的参数数为argc
.
您访问这些字符串的方式将在本文的其余部分中解释
公文。通常,模块会存储模块配置参数
在某些static
全局变量,该变量可以在模块范围内访问,因此
该配置可以更改不同命令的行为。
使用 RedisModuleString 对象
命令参数 vectorargv
传递给 module 命令,并且
其他模块 API 函数的返回值为RedisModuleString
.
通常,您可以直接将模块字符串传递给其他 API 调用,但有时 您可能需要直接访问 String 对象。
为了处理字符串对象,有一些函数:
const char *RedisModule_StringPtrLen(RedisModuleString *string, size_t *len);
上面的函数通过返回字符串的指针并将其
长度输入len
.
您永远不应该写入字符串对象指针,您可以从const
pointer 限定符。
但是,如果需要,您可以使用以下命令创建新的字符串对象 应用程序接口:
RedisModuleString *RedisModule_CreateString(RedisModuleCtx *ctx, const char *ptr, size_t len);
上述命令返回的字符串必须使用相应的
调用RedisModule_FreeString()
:
void RedisModule_FreeString(RedisModuleString *str);
但是,如果您想避免释放字符串,则自动内存 管理(本文档稍后将介绍)可能是一个不错的选择,例如 为您做这件事。
请注意,通过参数 vector 提供的字符串argv
永远不需要
被释放。您只需要释放您创建的新字符串或新字符串
由其他 API 返回,其中指定返回的字符串必须
被释放。
从数字创建字符串或将字符串解析为数字
从整数创建新字符串是一个非常常见的作,因此有 是一个函数来执行此作:
RedisModuleString *mystr = RedisModule_CreateStringFromLongLong(ctx,10);
同样,为了将字符串解析为数字:
long long myval;
if (RedisModule_StringToLongLong(ctx,argv[1],&myval) == REDISMODULE_OK) {
/* Do something with 'myval' */
}
从模块访问 Redis 密钥
大多数 Redis 模块为了有用,必须与 Redis 交互 数据空间(这并不总是正确的,例如 ID 生成器可能会 永远不要碰 Redis 键)。Redis 模块有两个不同的 API,以便 访问 Redis 数据空间,一个是底层 API,它提供非常 快速访问和一组函数来作 Redis 数据结构。 另一个 API 更高级,允许调用 Redis 命令和 获取结果,类似于 Lua 脚本访问 Redis 的方式。
高级 API 对于访问 Redis 功能也很有用 不作为 API 提供。
一般来说,模块开发人员应该更喜欢低级 API,因为命令 使用低级 API 实现,运行速度与 本机 Redis 命令。但是,肯定有 更高级别的 API。例如,瓶颈通常是处理 数据,而不是访问它。
另请注意,有时使用低级 API 并不比 更高级别的。
调用 Redis 命令
访问 Redis 的高级 API 是RedisModule_Call()
函数,以及访问
reply 对象返回Call()
.
RedisModule_Call
使用带有格式说明符的特殊调用约定
用于指定要作为参数传递的对象类型
添加到函数中。
Redis 命令仅使用命令名称和参数列表来调用。
但是,在调用命令时,参数可能来自不同的
字符串类型:以 null 结尾的 C 字符串,RedisModuleString 对象为
接收自argv
parameter 在命令实现中,binary
safe C 缓冲区,其中包含一个指针和一个长度,依此类推。
例如,如果我想调用INCRBY
使用第一个参数 (键)
在 argument vector 中接收的字符串argv
,它是一个数组
的 RedisModuleString 对象指针,以及一个 C 字符串,表示
数字 “10” 作为第二个参数(增量),我将使用以下
函数调用:
RedisModuleCallReply *reply;
reply = RedisModule_Call(ctx,"INCRBY","sc",argv[1],"10");
第一个参数是上下文,第二个参数始终是以 null 结尾的
C 字符串替换为命令名称。第三个参数是格式说明符
其中每个字符对应于后面的参数的类型。
在上述情况下"sc"
表示 RedisModuleString 对象,表示 null
终止的 C 字符串。其他参数只是两个参数,如
指定。事实上argv[1]
是 RedisModuleString 且"10"
为 null
终止的 C 字符串。
以下是格式说明符的完整列表:
- c -- 以 Null 结尾的 C 字符串指针。
- b -- C 缓冲区,需要两个参数:C 字符串指针和
size_t
长度。 - s -- 接收于
argv
或通过其他 Redis 模块 API 返回 RedisModuleString 对象。 - l -- long long 整数。
- v -- RedisModuleString 对象的数组。
- !-- 此修饰符只是告诉函数将命令复制到副本和 AOF。从参数解析的角度来看,它被忽略了。
- A -- 此修饰符,当
!
,告诉 to suppress AOF propagation:该命令将仅传播到副本。 - R -- 此修饰符,当
!
,告诉 to suppress replicas propagation:如果启用,该命令将仅传播到 AOF。
该函数返回一个RedisModuleCallReply
对象 on success (对象) on success (对象)
返回错误 NULL。
当命令名称无效时返回 NULL,格式说明符使用
字符,或者使用
参数数量错误。在上述情况下,errno
var 设置为EINVAL
.在启用了 Cluster 的实例中,目标
键是关于非本地哈希槽的。在这种情况下errno
设置为EPERM
.
使用 RedisModuleCallReply 对象。
RedisModuleCall
返回可以使用RedisModule_CallReply*
函数系列。
为了获得类型或回复(对应于数据类型之一
Redis 协议支持),函数RedisModule_CallReplyType()
用于:
reply = RedisModule_Call(ctx,"INCRBY","sc",argv[1],"10");
if (RedisModule_CallReplyType(reply) == REDISMODULE_REPLY_INTEGER) {
long long myval = RedisModule_CallReplyInteger(reply);
/* Do something with myval. */
}
有效的回复类型包括:
REDISMODULE_REPLY_STRING
批量字符串或状态回复。REDISMODULE_REPLY_ERROR
错误。REDISMODULE_REPLY_INTEGER
有符号 64 位整数。REDISMODULE_REPLY_ARRAY
回复数组。REDISMODULE_REPLY_NULL
NULL 回复。
字符串、错误和数组具有关联的长度。对于字符串和错误 length 对应于 String 的长度。对于数组,长度 是元素的数量。要获取回复长度,请使用以下函数 用于:
size_t reply_len = RedisModule_CallReplyLength(reply);
为了获得整数回复的值,使用以下函数,如上例所示:
long long reply_integer_val = RedisModule_CallReplyInteger(reply);
使用错误类型的 reply 对象调用时,上述函数始终
返回LLONG_MIN
.
数组回复的 Sub 元素可通过以下方式访问:
RedisModuleCallReply *subreply;
subreply = RedisModule_CallReplyArrayElement(reply,idx);
如果尝试访问超出范围的元素,则上述函数将返回 NULL。
字符串和错误(类似于字符串,但类型不同)可以
通过以下方式访问,确保永远不要写入
生成的指针(作为const
pointer,以便
误用必须非常明确):
size_t len;
char *ptr = RedisModule_CallReplyStringPtr(reply,&len);
如果回复类型不是字符串或错误,则返回 NULL。
RedisCallReply 对象与模块字符串对象不同 (RedisModuleString 类型)。但是,有时您可能需要传递回复 type string 或 integer 的 API 函数,以调用需要模块 string 的 API 函数。
在这种情况下,您可能需要评估是否使用 low 级别 API 可能是实现命令的更简单方法,或者您可以使用 以下函数为了从 call string、error 或 integer 类型的 reply :
RedisModuleString *mystr = RedisModule_CreateStringFromCallReply(myreply);
如果回复的类型不正确,则返回 NULL。
返回的字符串对象应使用RedisModule_FreeString()
或者通过启用自动内存管理(请参阅相应的
部分)。
释放呼叫回复对象
必须使用 Reply 释放对象RedisModule_FreeCallReply
.对于数组,
您只需释放顶级回复,而不需要释放嵌套回复。
目前 module implementation 提供了一个保护,以避免
如果为 Error 释放嵌套的 Reply 对象,则会崩溃,但是此功能
不能保证永远在这里,因此不应被视为一部分
API 中。
如果您使用自动内存管理(本文档稍后将解释) 您不需要免费回复(但如果您想发布,您仍然可以 尽快内存)。
从 Redis 命令返回值
与普通的 Redis 命令一样,通过模块实现的新命令必须 能够将值返回给调用方。API 导出一组函数 这个目标,为了返回 Redis 协议的通常类型,以及 数组等类型的数组。此外,任何 错误字符串和代码(错误代码是 错误消息,如“BUSY the sever is busy”错误中的 “BUSY” 字符串 消息)。
所有用于向客户端发送回复的函数都被调用RedisModule_ReplyWith<something>
.
要返回错误,请使用:
RedisModule_ReplyWithError(RedisModuleCtx *ctx, const char *err);
对于错误类型的 key 错误,有一个预定义的错误字符串:
REDISMODULE_ERRORMSG_WRONGTYPE
用法示例:
RedisModule_ReplyWithError(ctx,"ERR invalid arguments");
我们已经看到了如何用long long
在上面的示例中:
RedisModule_ReplyWithLongLong(ctx,12345);
要用不能包含二进制值或换行符的简单字符串回复, (所以适合发送小词,比如 “OK”)我们使用:
RedisModule_ReplyWithSimpleString(ctx,"OK");
可以使用 两种不同的功能:
int RedisModule_ReplyWithStringBuffer(RedisModuleCtx *ctx, const char *buf, size_t len);
int RedisModule_ReplyWithString(RedisModuleCtx *ctx, RedisModuleString *str);
第一个函数获取 C 指针和长度。第二个是 RedisModuleString 对象。根据您手头的源类型使用其中一种。
为了回复数组,你只需要使用一个函数来发出 数组长度,后跟对上述函数的调用次数与数字 数组的元素是:
RedisModule_ReplyWithArray(ctx,2);
RedisModule_ReplyWithStringBuffer(ctx,"age",3);
RedisModule_ReplyWithLongLong(ctx,22);
返回嵌套数组很容易,你的嵌套数组元素只使用另一个
调用RedisModule_ReplyWithArray()
后跟发出
sub 数组元素。
返回具有动态长度的数组
有时无法事先知道
一个数组。例如,考虑一个实现 FACTOR 的 Redis 模块
给定一个数字的命令输出质因数。而不是
对数字进行阶乘运算,将质因数存储到数组中,以及
后面产生命令 reply,更好的办法是启动一个数组
reply 在长度未知的地方,稍后设置。这是完成的
替换为RedisModule_ReplyWithArray()
:
RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_LEN);
上面的调用启动了一个数组 reply,因此我们可以使用 otherReplyWith
调用
以生成数组项。最后,为了设置长度,
使用以下调用:
RedisModule_ReplySetArrayLength(ctx, number_of_items);
对于 FACTOR 命令,这将转换为一些类似的代码 到这个:
RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_LEN);
number_of_factors = 0;
while(still_factors) {
RedisModule_ReplyWithLongLong(ctx, some_factor);
number_of_factors++;
}
RedisModule_ReplySetArrayLength(ctx, number_of_factors);
此功能的另一个常见用例是迭代 some 集合,并且只返回通过某种过滤的 URL。
可以有多个嵌套数组的 delayed reply。
每次调用SetArray()
会设置最新对应的长度
调用ReplyWithArray()
:
RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_LEN);
... generate 100 elements ...
RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_LEN);
... generate 10 elements ...
RedisModule_ReplySetArrayLength(ctx, 10);
RedisModule_ReplySetArrayLength(ctx, 100);
这将创建一个 100 items 数组,其最后一个元素是 10 items 数组。
Arity 和类型检查
通常命令需要检查参数的数量和键的类型
是正确的。为了报告错误的 arity,有一个特定的函数
叫RedisModule_WrongArity()
.用法很简单:
if (argc != 2) return RedisModule_WrongArity(ctx);
检查错误的类型包括打开键并检查类型:
RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
REDISMODULE_READ|REDISMODULE_WRITE);
int keytype = RedisModule_KeyType(key);
if (keytype != REDISMODULE_KEYTYPE_STRING &&
keytype != REDISMODULE_KEYTYPE_EMPTY)
{
RedisModule_CloseKey(key);
return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
}
请注意,如果键为 是预期类型,或者为空。
对密钥的低级别访问
对键的低级访问允许对关联的值对象执行作 直接 key 中,其速度类似于 Redis 内部用于 实现内置命令。
打开键后,将返回一个键指针,该指针将与所有 其他低级 API 调用,以便对 key 或其 关联的值。
因为 API 应该非常快,所以它不能执行太多的运行时 检查,因此用户必须了解要遵循的某些规则:
- 在至少打开一个实例进行写入的情况下多次打开同一密钥是未定义的,并且可能导致崩溃。
- 当密钥处于打开状态时,只能通过低级密钥 API 访问它。例如,打开一个键,然后使用
RedisModule_Call()
API 将导致崩溃。但是,可以安全地打开一个密钥,使用低级 API 执行一些作,关闭它,然后使用其他 API 管理同一个密钥,然后再次打开它以执行更多工作。
为了打开一个密钥,RedisModule_OpenKey
函数。它返回
一个 Key 指针,我们将在所有后续调用 access 和 modify 中使用它
值:
RedisModuleKey *key;
key = RedisModule_OpenKey(ctx,argv[1],REDISMODULE_READ);
第二个参数是键名称,必须是RedisModuleString
对象。
第三个参数是 mode:REDISMODULE_READ
或REDISMODULE_WRITE
.
可以使用|
按位 OR 两种模式打开键
两种模式。当前打开用于写入的密钥也可以访问以进行读取
但这应被视为实现细节。正确的模式应该
用于 Sane 模块。
您可以打开不存在的键进行写入,因为将创建键
当尝试写入 key 时。但是,打开密钥时
仅供阅读,RedisModule_OpenKey
如果键没有,将返回 NULL
存在。
使用完密钥后,您可以通过以下方式关闭它:
RedisModule_CloseKey(key);
请注意,如果启用了自动内存管理,则不会强制您 关闭键。当模块函数返回时,Redis 会小心地关闭 所有仍处于打开状态的键。
获取密钥类型
要获取键的值,请使用RedisModule_KeyType()
功能:
int keytype = RedisModule_KeyType(key);
它返回以下值之一:
REDISMODULE_KEYTYPE_EMPTY
REDISMODULE_KEYTYPE_STRING
REDISMODULE_KEYTYPE_LIST
REDISMODULE_KEYTYPE_HASH
REDISMODULE_KEYTYPE_SET
REDISMODULE_KEYTYPE_ZSET
以上只是通常的 Redis 键类型,并添加了一个空的 type 的 intent 语句,该 key 指针与一个空 key 相关联,该 尚不存在。
创建新密钥
要创建新密钥,请打开它进行写入,然后使用一个密钥写入它 的按键写入功能。例:
RedisModuleKey *key;
key = RedisModule_OpenKey(ctx,argv[1],REDISMODULE_WRITE);
if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_EMPTY) {
RedisModule_StringSet(key,argv[2]);
}
删除键
只需使用:
RedisModule_DeleteKey(key);
该函数返回REDISMODULE_ERR
如果密钥未打开写入。
请注意,删除密钥后,会对其进行设置以成为目标
通过新的键盘命令。例如RedisModule_KeyType()
将返回它是
一个空键,写入它将创建一个新键,可能是另一个
类型(取决于使用的 API)。
管理密钥过期 (TTL)
为了控制密钥过期,提供了两个函数,它们可以设置, 修改、获取和取消设置与键关联的生存时间。
一个函数用于查询打开密钥的当前过期时间:
mstime_t RedisModule_GetExpire(RedisModuleKey *key);
该函数返回密钥的生存时间(以毫秒为单位),或者REDISMODULE_NO_EXPIRE
作为特殊值来表示 key 没有关联的
expire 或根本不存在(您可以区分这两种情况检查
如果密钥类型为REDISMODULE_KEYTYPE_EMPTY
).
为了更改密钥的过期时间,请改用以下函数:
int RedisModule_SetExpire(RedisModuleKey *key, mstime_t expire);
在不存在的 key 上调用时,REDISMODULE_ERR
返回,因为
该函数只能将 Expires 关联到现有的开放键(不存在的
open keys 仅用于创建数据类型
特定的写入作)。
同样,expire
时间以毫秒为单位指定。如果密钥当前具有
no expire,则设置新的 expire。如果密钥已过期,则为
替换为新值。
如果 key 具有 expire 和 special 值REDISMODULE_NO_EXPIRE
是
用作新的 expire,则 expire 会被删除,类似于 RedisPERSIST
命令。如果密钥已经是持久的,则不会执行任何作
执行。
获取值的长度
有一个函数可以检索值的长度 关联到打开的密钥。返回的长度是特定于值的,并且是 字符串的字符串长度,以及聚合的元素数 数据类型(列表、集合、排序集、哈希中有多少个元素)。
size_t len = RedisModule_ValueLength(key);
如果 key 不存在,则函数返回 0:
字符串类型 API
设置新的字符串值,如 RedisSET
命令执行
用:
int RedisModule_StringSet(RedisModuleKey *key, RedisModuleString *str);
该函数的工作方式与 Redis 完全相同SET
命令本身,即如果
有一个 prior 值(任何类型的)它将被删除。
使用 DMA(直接内存 access) 以获得速度。API 将返回一个指针和一个长度,所以 可以访问并在需要时直接修改字符串。
size_t len, j;
char *myptr = RedisModule_StringDMA(key,&len,REDISMODULE_WRITE);
for (j = 0; j < len; j++) myptr[j] = 'A';
在上面的例子中,我们直接在字符串上写入。请注意,如果您希望
要写,你必须确保要求WRITE
模式。
DMA 指针仅在没有使用密钥执行其他作时有效 在使用指针之前,在 DMA 调用之后。
有时当我们想直接作字符串时,我们需要更改
他们的规模也是如此。对于此范围,RedisModule_StringTruncate
功能
被使用。例:
RedisModule_StringTruncate(mykey,1024);
该函数根据需要截断或放大字符串,并用
如果之前的长度小于我们请求的新长度,则为零字节。
如果字符串不存在,则key
与打开的空键相关联,
将创建一个 String 值并将其关联到该键。
请注意,每次StringTruncate()
调用时,我们需要重新获取
DMA 指针,因为旧指针可能无效。
列表类型 API
可以从列表值中推送和弹出值:
int RedisModule_ListPush(RedisModuleKey *key, int where, RedisModuleString *ele);
RedisModuleString *RedisModule_ListPop(RedisModuleKey *key, int where);
在这两个 API 中,where
参数指定是否从 tail 推送或 pop
或 head 使用以下宏:
REDISMODULE_LIST_HEAD
REDISMODULE_LIST_TAIL
返回的元素RedisModule_ListPop()
类似于使用RedisModule_CreateString()
,它们必须与RedisModule_FreeString()
或通过启用自动内存管理。
Set 类型 API
正在进行的工作。
有序集类型 API
文档缺失,请参考里面的热门评论module.c
用于以下功能:
RedisModule_ZsetAdd
RedisModule_ZsetIncrby
RedisModule_ZsetScore
RedisModule_ZsetRem
对于排序集迭代器:
RedisModule_ZsetRangeStop
RedisModule_ZsetFirstInScoreRange
RedisModule_ZsetLastInScoreRange
RedisModule_ZsetFirstInLexRange
RedisModule_ZsetLastInLexRange
RedisModule_ZsetRangeCurrentElement
RedisModule_ZsetRangeNext
RedisModule_ZsetRangePrev
RedisModule_ZsetRangeEndReached
哈希类型 API
文档缺失,请参考里面的热门评论module.c
用于以下功能:
RedisModule_HashSet
RedisModule_HashGet
迭代聚合值
正在进行的工作。
复制命令
如果您想像使用普通的 Redis 命令一样使用模块命令,请在 上下文,或使用 AOF 文件进行持久化, 模块命令以一致的 道路。
使用更高级别的 API 调用命令时,会发生复制
如果在RedisModule_Call()
如以下示例所示:
reply = RedisModule_Call(ctx,"INCRBY","!sc",argv[1],"10");
如您所见,格式说明符是"!sc"
.bang 不会解析为
format 说明符,但它在内部将命令标记为 “Must Replicate”。
如果使用上述编程风格,则没有问题。 然而,有时事情比这更复杂,你使用低级 应用程序接口。在这种情况下,如果命令执行中没有副作用,并且 它始终执行相同的工作,可以做的是 在用户执行命令时逐字复制命令。要做到这一点,您只需 需要调用以下函数:
RedisModule_ReplicateVerbatim(ctx);
使用上述 API 时,不应使用任何其他复制功能 因为它们不能保证混合得很好。
但是,这并不是唯一的选择。也可以准确判断
Red是要复制的命令作为命令执行的效果,使用
类似于RedisModule_Call()
但是,而不是调用 command
将其发送到 AOF/副本流。例:
RedisModule_Replicate(ctx,"INCRBY","cl","foo",my_increment);
可以调用RedisModule_Replicate
多次,并且每个
将发出一个命令。发出的所有序列都包裹在MULTI/EXEC
transaction 的 API,使得 AOF 和复制效果都是
与执行单个命令相同。
请注意,Call()
replication 和Replicate()
replication 有一个规则,
如果您想混合两种形式的复制(不一定是好的
如果有更简单的方法)。使用Call()
始终是 final 中第一个发出的MULTI/EXEC
块,而所有
使用Replicate()
将紧随其后。
自动内存管理
通常,当用 C 语言编写程序时,程序员需要管理 memory 的 intent 的 intent 值。这就是为什么 Redis 模块 API 有功能可以发布的原因 strings、close open key、free 回复等。
但是,鉴于命令是在包含的环境中执行的,并且 通过一组严格的 API,Redis 能够提供自动内存管理 到模块中,以牺牲一些性能为代价(大多数情况下,非常低的 成本)。
启用自动内存管理后:
- 您无需关闭打开的键。
- 您无需免费回复。
- 您无需释放 RedisModuleString 对象。
但是,如果您愿意,您仍然可以这样做。例如,自动记忆 management 可能是 active 的,但在分配大量字符串的循环中, 你可能仍然希望释放不再使用的字符串。
要启用自动内存管理,只需调用以下命令 函数:
RedisModule_AutoMemory(ctx);
自动内存管理通常是要走的路,无论多么有经验 C 程序员可能不会使用它来获得一些速度和内存使用 效益。
将内存分配到模块中
普通的 C 程序使用malloc()
和free()
为了分配和
动态释放内存。而在 Redis 模块中,malloc 的使用是
技术上并不被禁止,使用 Redis 模块要好得多
特定函数,这些函数是malloc
,free
,realloc
和strdup
.这些功能是:
void *RedisModule_Alloc(size_t bytes);
void* RedisModule_Realloc(void *ptr, size_t bytes);
void RedisModule_Free(void *ptr);
void RedisModule_Calloc(size_t nmemb, size_t size);
char *RedisModule_Strdup(const char *str);
它们的工作方式与libc
等效调用,但是它们使用
Redis 使用的分配器相同,以及使用这些分配器
functions 由INFO
命令在 memory 部分中为
在执行maxmemory
策略,通常为
Redis 可执行文件的第一个公民。相反,该方法
使用 libc 在 modules 内部分配malloc()
对 Redis 是透明的。
使用 modules 函数来分配内存的另一个原因
是,在模块中创建原生数据类型时,RDB 加载
函数可以直接返回反序列化的字符串(来自 RDB 文件)
如RedisModule_Alloc()
allocations,因此它们可以直接用于
加载后填充数据结构,而不必复制它们
添加到数据结构中。
池分配器
有时在 commands 实现中,需要执行许多 不会在命令末尾保留的小额分配 执行,但仅用于执行命令本身。
使用 Redis 池分配器可以更轻松地完成这项工作:
void *RedisModule_PoolAlloc(RedisModuleCtx *ctx, size_t bytes);
它的工作原理类似于malloc()
,并返回与
大于或等于 的 2 的次幂bytes
(对于最大对齐方式
的 8 字节)。但是,它以块为单位分配内存,因此开销
的分配量很小,更重要的是,分配的内存
在命令返回时自动释放。
因此,一般来说,短期分配是池的良好候选者 分配器。
编写与 Redis Cluster 兼容的命令
文档缺失,请检查里面的以下功能module.c
:
RedisModule_IsKeysPositionRequest(ctx);
RedisModule_KeyAtPos(ctx,pos);