1. 程式人生 > >redis module 學習—官網文檔整理

redis module 學習—官網文檔整理

score dna slot truct 可能 add 結果 多次 tell

前言

redis在4.0版本中,推出了一個非常吸引的特性,可以通過編寫插件的模式,來動態擴展redis的能力。在4.0之前,如果用戶想擁有一個帶TTL的INCRBY 命令,那麽用戶只能自己去改代碼,重新編譯了。在4.0版本推出之後,想實現一個自定義的命令就簡單的多了。
在這個功能發布之後,已經有許多的第三方擴展插件被開發出來。具體可以參見官方模塊倉庫 :
技術分享圖片
接下來,本文將基於redis官方文檔 進行翻譯,介紹下相關的功能(翻譯的和原有有出入,更多是基於自己的理解。如果大家有問題,歡迎討論),也算對這個功能的學習筆記。

Loading modules (如何加載模塊)

加載模塊一共有2中方式:

  1. 在redis.conf 配置文件中指定:
loadmodule /path/to/mymodule.so
  1. 運行時使用命令加載模塊:
MODULE LOAD /path/to/mymodule.so

也可以通過命令查看當前有多少模塊被加載了:

MODULE LIST

如果加載了模塊之後想移除,也可以通過如下的命令移除模塊:

MODULE UNLOAD mymodule

The simplest module you can write (寫一個簡單的模塊)

redis官網上提供了一個非常簡單的示例,演示了通過模塊添加一個返回隨機數的命令:

#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) == REDISMODULE_ERR)
        return REDISMODULE_ERR;

    return REDISMODULE_OK;
}

在示例中有2個函數,HelloworldRand_RedisCommand 這個函數用於實現新添加的命令。而RedisModule_OnLoad函數是每個模塊中都必須存在的。這個函數是一個redis在加載模塊的時候的入口(初始化模塊,註冊命令,初始化數據結構等工作),原文文檔是這麽描述這個入口函數的:

It is the entry point for the module to be initialized, register its commands, and potentially other private data structures it uses.

那麽要如何調用這個新的命令呢?原文文檔中,給出的提示是,最好通過{模塊名}.{命令名稱}這樣子的格式來調用。同時也有一個要註意的地方:

兩個不同的模塊下,如果有相同的名稱的命令,則 RedisModule_CreateCommand 這個創建命令的函數會失敗。
Note that if different modules have colliding commands, they‘ll not be able to work in Redis at the same time, since the function RedisModule_CreateCommand will fail in one of the modules, so the module loading will abort returning an error condition.

Module initialization (模塊初始化)

前文介紹如何寫一個簡單的模塊中,RedisModule_OnLoad函數裏面調用如下的2個函數 :

  1. RedisModule_Init
  2. RedisModule_CreateCommand

接下來依據redis文檔分別介紹下兩個函數。

RedisModule_Init

官方文檔給出的函數定義的形式如下:

int RedisModule_Init(RedisModuleCtx *ctx, const char *modulename,
                     int module_version, int api_version);

這個函數必須在redis模塊初始化的時候被第一個調用,調用的時候會註冊如下的信息:

  1. 模塊名稱:即參數const char *modulename
  2. 模塊的版本:即參數int module_version
  3. API的版本:即參數 int api_version

如果有以下的錯誤發生,則這個函數返回錯誤REDISMODULE_ERR :

  1. API的版本錯誤
  2. 模塊名稱已經被其他模塊註冊過
  3. 其他相似的錯誤

RedisModule_CreateCommand

官方文檔給出的函數定義的形式如下:

int RedisModule_CreateCommand(RedisModuleCtx *ctx, const char *cmdname,
                              RedisModuleCmdFunc cmdfunc);

RedisModule_CreateCommand函數用於註冊一個新的命令。 可以看出,RedisModule_CreateCommand需要傳入如下的三個參數:

參數 說明
RedisModuleCtx *ctx 第一個參數是 redis module 的上下文指針(姑且這麽稱呼吧,原文中是這麽描述這個參數的:As you can see, most Redis modules API calls all take as first argument the context of the module, so that they have a reference to the module calling it, to the command and client executing a given command, and so forth.)
const char *cmdname 命令的名稱,例如上文就是 "helloworld.rand"
RedisModuleCmdFunc cmdfunc 命令實現的函數指針,例如上文的HelloworldRand_RedisCommand

RedisModuleCmdFunc

上文中可以看到,RedisModule_CreateCommand創建命令的函數最後一個參數是RedisModuleCmdFunc。這個參數指向實現當前註冊的命令的函數。 例如在前文的例子就是:

int HelloworldRand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
    RedisModule_ReplyWithLongLong(ctx,rand());
    return REDISMODULE_OK;
}

一個命令的實現函數必須有如下的形式:

int mycommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
參數 說明
RedisModuleCtx *ctx module的指針,前文介紹過了。
RedisModuleString **argv 新命令需要的參數
int argc 參數的個數

Setup and dependencies of a Redis module (這節具體說了怎麽編譯和生成redis 模塊)

文檔中這節說的很簡略:

Redis modules don‘t depend on Redis or some other library, nor they need to be compiled with a specific redismodule.h file. In order to create a new module, just copy a recent version of redismodule.h in your source tree, link all the libraries you want, and create a dynamic library having the RedisModule_OnLoad() function symbol exported.
The module will be able to load into different versions of Redis.

簡單的來說就是:

  1. Redis模塊不依賴於Redis或其他庫,也不需要使用特定的redismodule.h文件進行編譯。
  2. 只需在源代碼樹中復制最新版本的redismodule.h,鏈接所需的所有庫,並創建一個導出RedisModule_OnLoad()函數符號的動態庫。

Passing configuration parameters to Redis modules (傳遞參數給redis 模塊)

加載模塊的時候,也是可以傳遞參數給需要加載的模塊的。例如下面的例子:

loadmodule mymodule.so foo bar 1234

傳遞給module參數 foo bar 1234 這幾個參數。這些參數會在RedisModule_OnLoad 函數裏面的RedisModuleString **argv這個參數裏獲取到。

Working with RedisModuleString objects(RedisModuleString的使用)

在module中,經常使用到RedisModuleString這種類型的變量。比如前文中提到的RedisModule_OnLoad函數的入參RedisModuleString **argv,以及redis module API的返回值等等都用到了這個類型。
在官方文檔中提供了多個函數,方便對於RedisModuleString的操作:

  1. const char RedisModule_StringPtrLen(RedisModuleString string, size_t *len);
  2. RedisModuleString RedisModule_CreateString(RedisModuleCtx ctx, const char *ptr, size_t len);
  3. void RedisModule_FreeString(RedisModuleString *str);
  4. RedisModule_CreateStringFromLongLong(ctx,10);

RedisModule_StringPtrLen

有些時候需要從RedisModuleString 裏面獲取原始的C字符串,以便傳遞給其他的類型的接口。 Redis提供了 RedisModule_StringPtrLen函數,用於實現這個功能。

const char *RedisModule_StringPtrLen(RedisModuleString *string, size_t *len);

從函數定義可以看出,RedisModule_StringPtrLen返回一個char 指針,並把這個字符串的長度設置到len當中。

RedisModule_CreateString 和 RedisModule_FreeString

前文提到了怎麽從 RedisModuleString裏面獲取C原始的字符串。 那麽如何從一個C字符串創建一個RedisModuleString 呢? 答案是可以通過 RedisModule_CreateString函數創建一個。函數的定義如下:

RedisModuleString *RedisModule_CreateString(RedisModuleCtx *ctx, const char *ptr, size_t len);

使用 RedisModule_CreateString 創建的字符串,必須使用 RedisModule_FreeString 去釋放。

void RedisModule_FreeString(RedisModuleString *str);

當然如果想偷懶,不每次都去釋放創建的對象的話,也可以使用redis的在 automatic memory management,這個特性在後面的文檔會介紹到。

RedisModule_CreateStringFromLongLong

有些時候,需要從整數中創建一個字符串,可以使用函數 RedisModule_CreateStringFromLongLong 。

RedisModuleString *mystr = RedisModule_CreateStringFromLongLong(ctx,10);

官方文檔給出的例子如下:

long long myval;
if (RedisModule_StringToLongLong(ctx,argv[1],&myval) == REDISMODULE_OK) {
    /* Do something with ‘myval‘ */
}

Accessing Redis keys from modules (訪問redis的數據)

Redis module 提供了2套方法來訪問redis的數據空間。 按文檔中的說法,一套是 high level API ,一套是 low level API 。文檔中對於兩套API是這麽解釋的:

  1. low level API :

    one is a low level API that provides very fast access and a set of functions to manipulate Redis data structures

    In general modules developers should prefer the low level API, because commands implemented using the low level API run at a speed comparable to the speed of native Redis commands.

  2. high level API:

    The other API is more high level, and allows to call Redis commands and fetch the result, similarly to how Lua scripts access Redis.

從文檔的解釋可以看出Redis module 提供了2個層次的API,我們姑且稱之為高層API(high level API)和底層API(low level API)。對於高層API,調用起來類似於lua 腳本,而且也較為的方便。但是不如底層API來得快。 同時文檔建議開發者在開發模塊的時候,盡量調用底層API,因為底層API 的執行速度幾乎可以和redis原生命令一樣快。

Calling Redis commands (通過high level API 調用 redis命令)

使用high level API 調用 redis命令,都是通過RedisModule_Call這個函數來實現的。 原文文檔中給出了一個示例調用incr 命令:

RedisModuleCallReply *reply;
reply = RedisModule_Call(ctx,"INCR","sc",argv[1],"10");

參數解釋如下:

  1. ctx :在之前的部分介紹過,因此不再介紹。
  2. "INCR":第二個參數是一個C字符串,傳遞的是要被調用的命令。本例中是incr
  3. "sc": 第三個參數,指明了接下去傳給incr的幾個參數,都是些什麽類型的。
參數 原文說明
c Null terminated C string pointer
b C buffer, two arguments needed: C string pointer and size_t length.
s RedisModuleString as received in argv or by other Redis module APIs returning a RedisModuleString object.
I Long long integer.
v Array of RedisModuleString objects.
! This modifier just tells the function to replicate the command to slaves and AOF. It is ignored from the point of view of arguments parsing.
  1. argv[1]和"10":後面的2個參數,就是分別傳遞給incr的值了。根據上面的"sc"參數可以看出,一個是RedisModuleString 另外一個是C string 。

RedisModule_Call的返回是一個RedisModuleCallReply對象。在兩種情況下,返回的是空值(Null):

  1. 當調用RedisModuleCallReply 錯誤的時候
  2. 當redis cluster enabled ,當前訪問的key不在這個節點的時候

NULL is returned when the command name is invalid, the format specifier uses characters that are not recognized, or when the command is called with the wrong number of arguments. In the above cases the errno var is set to EINVAL. NULL is also returned when, in an instance with Cluster enabled, the target keys are about non local hash slots. In this case errno is set to EPERM.

Working with RedisModuleCallReply objects. (RedisModuleCallReply的使用)

如前面的部分所述,RedisModuleCallReply 在RedisModule_Call 函數使用的時候被返回給用戶。本節介紹下 RedisModuleCallReply的使用。
RedisModuleCallReply會有如下的幾個類型:

  1. REDISMODULE_REPLY_STRING Bulk string or status replies
  2. REDISMODULE_REPLY_ERROR Errors
  3. REDISMODULE_REPLY_INTEGER Signed 64 bit integers
  4. REDISMODULE_REPLY_ARRAY Array of replies
  5. REDISMODULE_REPLY_NULL NULL reply

對於Strings(REDISMODULE_REPLY_STRING Bulk string or status replies ),error (REDISMODULE_REPLY_ERROR Errors )和Array (REDISMODULE_REPLY_ARRAY Array of replies) 類型,可以通過如下的函數獲取返回的長度 :

size_t reply_len = RedisModule_CallReplyLength(reply); 

int類型(REDISMODULE_REPLY_INTEGER Signed 64 bit integers ) 獲取int的值,可以通過如下的函數:

long long reply_integer_val = RedisModule_CallReplyInteger(reply);

上文的函數,如果傳入的參數類型錯誤,則返回的是 LLONG_MIN。

對於Array類型的返回,獲取返回裏的某個元素,可以使用如下的函數:

RedisModuleCallReply *subreply;
subreply = RedisModule_CallReplyArrayElement(reply,idx);

如果數組越界了,則返回的是Null。
同時如果需要Strings和error 的原始的C字符串返回,可以像如下的示例一樣:

size_t len;
char *ptr = RedisModule_CallReplyStringPtr(reply,&len);

如果入參的reply的類型不是 string或者error ,則返回值是Null。
RedisCallReply也可以轉換為 RedisModuleString :

RedisModuleString *mystr = RedisModule_CreateStringFromCallReply(myreply);

如果入參reply不是正確的type,則返回是Null。
對於Reply objects 可以通過RedisModule_FreeCallReply函數進行釋放。對於數組類型的返回,不需要挨個去釋放元素。此外,如果使用了自動內存管理特性的情況下,開發者不必去釋放Reply objects。

Returning values from Redis commands (這節主要介紹使用什麽方法將返回值返回給客戶端)

大部分情況下,module中新設計的命令都需要返回一定的值給客戶端,就像正常的Redis命令一樣。 Redis module中提供這個能力的函數簇是 RedisModule_ReplyWith.

返回error

返回一個error 可以使用如下的命令:

RedisModule_ReplyWithError(RedisModuleCtx *ctx, const char *err); 

比如開發者可以返回一個"ERR invalid arguments" 的提示:

RedisModule_ReplyWithError(ctx,"ERR invalid arguments");

返回一個整形

返回一個整形可以通過如下的函數 :

RedisModule_ReplyWithLongLong(ctx,12345); 

返回一個簡單的字符串

返回一個簡單的 不是二進制安全或者不帶換行的字符串,例如"OK"這種,可以用如下的命令:

RedisModule_ReplyWithSimpleString(ctx,"OK");

如果需要返回一些復雜的字符串,例如需要二進制安全的特性的,可以使用如下的2個函數 :

int RedisModule_ReplyWithStringBuffer(RedisModuleCtx *ctx, const char *buf, size_t len);

int RedisModule_ReplyWithString(RedisModuleCtx *ctx, RedisModuleString *str);

返回一個數組類型

返回值也可以是一個數組。文檔中給出了示例如下,首先給出將要返回的數組長度,然後調用相應數量的上面那些回應的API:

RedisModule_ReplyWithArray(ctx,2);
RedisModule_ReplyWithStringBuffer(ctx,"age",3);
RedisModule_ReplyWithLongLong(ctx,22);

同樣,回應的數組中的某個元素也可以是一個數組如下:

RedisModule_ReplyWithArray(ctx,2);
RedisModule_ReplyWithStringBuffer(ctx,"age",3);
RedisModule_ReplyWithArray(ctx,3);
RedisModule_ReplyWithLongLong(ctx,22);
RedisModule_ReplyWithLongLong(ctx,22);
RedisModule_ReplyWithLongLong(ctx,22);

返回動態長度的數組

有時想預先知道一個要操作的數組長度是不可能的,列如當我們用Redis module 實現一個FACTOR的命令,它的參數是一個數組結果輸出是它的素數因子。為了實現上面的功能,可以先通過一個特殊的參數創建數組(RedisModule_ReplyWithArray(ctx, long )),然後在執行完相應的操作後,最後再設置這個數組的長度。

RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN);  
RedisModule_ReplySetArrayLength(ctx, number_of_items); 

使用的例子如下:

RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN);
number_of_factors = 0;
while(still_factors) {
    RedisModule_ReplyWithLongLong(ctx, some_factor);
    number_of_factors++;
}
RedisModule_ReplySetArrayLength(ctx, number_of_factors);

遍歷元素然後按條件過濾一部分再返回結果也是這個特性經常使用到的列子。向前文講的返回一個內嵌數組也是可以的,對於內嵌數組來講,SetArrayLength函數只會設置上一個最近調用 ReplywithArray的數組的大小。

RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN);
... generate 100 elements ...
RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN);
... generate 10 elements ...
RedisModule_ReplySetArrayLength(ctx, 10);
RedisModule_ReplySetArrayLength(ctx, 100);

Arity and type checks (參數個數和類型的檢查)

如果參數的個數不符合檢查的規則,可以使用RedisModule_WrongArity()作為返回:

if (argc != 2) return RedisModule_WrongArity(ctx);

對於打開的key 類型的檢查,則可以類似於如下的代碼段:

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

Low level access to keys(通過low level api 訪問key)

通過low level api 去訪問Redis的key ,可以擁有和redis原生命令一樣的速度和性能。 有幾個地方在原文文檔中提示需要嚴格註意的:

  1. 一個key被打開多次,並且其中至少有一個實例是寫入狀態,這種情況下可能會導致崩潰。

Opening the same key multiple times where at least one instance is opened for writing, is undefined and may lead to crashes.

  1. 如果要打開一個key ,最好的途徑還是通過low level key API 的方式打開。例如當打開了一個key,之後使用RedisModule_Call的方式去Del這個key,將會導致crash。因此,最安全的方式還是通過low level API 去打開,關閉,操作和管理一個key 。

    While a key is open, it should only be accessed via the low level key API. For example opening a key, then calling DEL on the same key using the RedisModule_Call() API will result into a crash. However it is safe to open a key, perform some operation with the low level API, closing it, then using other APIs to manage the same key, and later opening it again to do some more work.

打開和關閉一個key

打開一個Redis的key可以通過如下的函數實現 :

RedisModuleKey *key;
key = RedisModule_OpenKey(ctx,argv[1],REDISMODULE_READ);

參數 :

  1. ctx : 之前介紹過了,這裏不復述。
  2. argv[1]: 這裏傳入的是key的名稱。 這個參數必須是 RedisModuleString類型。
  3. REDISMODULE_READ : 第三個參數很容易理解,讓我偷懶下,直接貼原文的解釋了:

    The third argument is the mode: REDISMODULE_READ or REDISMODULE_WRITE. It is possible to use | to bitwise OR the two modes to open the key in both modes.Currently a key opened for writing can also be accessed for reading but this is to be considered an implementation detail. The right mode should be used in sane modules.

這裏還有3個地方註意下:

  1. 如果打開一個不存在的key為了去寫入它,則函數會創建一個新的key。之後可以執行寫入的工作 。
  2. 如果打開一個不存在的key只是為了讀取,RedisModule_OpenKey會返回Null 。
  3. 一個key如果被打開了,可以通過RedisModule_CloseKey函數關閉這個key。當前如果使用了automatic memory management這個功能,也不是需要都主動關閉key 。

獲取一個key的類型

獲取一個key的類型,可以使用如下的函數 :

int keytype = RedisModule_KeyType(key);

返回值有如下的類型:

  1. REDISMODULE_KEYTYPE_EMPTY
  2. REDISMODULE_KEYTYPE_STRING
  3. REDISMODULE_KEYTYPE_LIST
  4. REDISMODULE_KEYTYPE_HASH
  5. REDISMODULE_KEYTYPE_SET
  6. REDISMODULE_KEYTYPE_ZSET

上述的類型是Redis中常見的類型,還有一個類型是空類型。如果函數返回的是空類型,則代表這個key不存在。

創建一個新的key

創建一個新的key,可以通過如下的示例:

RedisModuleKey *key;
key = RedisModule_OpenKey(ctx,argv[1],REDISMODULE_WRITE);
if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_EMPTY) {
    RedisModule_StringSet(key,argv[2]);
}

刪除一個key

刪除一個key可以用如下的函數:

RedisModule_DeleteKey(key);

如果一個key沒有被打開並寫入,則函數返回REDISMODULE_ERR 。

過期時間的設定(TTLS)

Redis提供了如下2個函數來操作過期時間:

mstime_t RedisModule_GetExpire(RedisModuleKey *key);
int RedisModule_SetExpire(RedisModuleKey *key, mstime_t expire);

RedisModule_GetExpire 會返回一個key的過期時間(單位是毫秒)。如果一個key沒有過期時間,則會返回REDISMODULE_NO_EXPIRE 。
如果要改變一個key的過期時間,則使用RedisModule_SetExpire函數。如果傳入函數的key不存在,則返回REDISMODULE_ERR。
當RedisModule_SetExpire函數被使用,如果一個key當前沒有過期時間,則會賦予它一個新的過期時間。如果當前key已經有過期時間,則replaced with the new value 。 如果一個key有過期時間,REDISMODULE_NO_EXPIRE被使用,則過期時間被移除。

獲取values 的長度

Redis提供了一個單獨的函數獲取一個key的value的長度。如果是value的類型是string 則返回字符串長度。如果是list,set,sorted set,hash則返回元素的個數。

size_t len = RedisModule_ValueLength(key);

如果key不存在,則返回0 。

字符串相關的API

對一個key設定string的value ,可以用如下的函數:

int RedisModule_StringSet(RedisModuleKey *key, RedisModuleString *str);

這個函數類似於原生的set命令,如果之前有值存在,則老的值會被覆蓋。
獲取一個key的value也可以通過DMA(direct memory access)的方式,例如如下的示例:

size_t len, j;
char *myptr = RedisModule_StringDMA(key,&len,REDISMODULE_WRITE);
for (j = 0; j < len; j++) myptr[j] = ‘A‘;

這個示例通過RedisModule_StringDMA直接獲取了這個key的values的C原始字符串,然後直接修改了它。在這個示例裏面,如果要寫入字符串,必須確保當前是WRITE mode 。
有時候,需要更改一個字符串的長度,可以使用如下的函數:

RedisModule_StringTruncate(mykey,1024);

這個函數根據需要截斷或放大字符串,如果前一個長度小於我們請求的新長度,則用零字節填充字符串。如果該字符串不存在,則會創建一個字符串值並與該鍵相關聯。註意,每次調用StringTruncate時,都需要重新獲取DMA指針,因為舊的指針可能無效。

list 相關API

對於list的操作,可以使用如下的兩個函數:

int RedisModule_ListPush(RedisModuleKey *key, int where, RedisModuleString *ele);
RedisModuleString *RedisModule_ListPop(RedisModuleKey *key, int where);

在這兩個API中,where參數使用以下宏指定是從尾部還是頭部推還是彈出:

  1. REDISMODULE_LIST_HEAD
  2. REDISMODULE_LIST_TAIL

sort set 相關API

那啥,原文文檔說Documentation missing, please refer to the top comments inside module.c for the following functions。 在此我列下 API吧。

  1. RedisModule_ZsetAdd
  2. RedisModule_ZsetIncrby
  3. RedisModule_ZsetScore
  4. RedisModule_ZsetRem
  5. RedisModule_ZsetRangeStop
  6. RedisModule_ZsetFirstInScoreRange
  7. RedisModule_ZsetLastInScoreRange
  8. RedisModule_ZsetFirstInLexRange
  9. RedisModule_ZsetLastInLexRange
  10. RedisModule_ZsetRangeCurrentElement
  11. RedisModule_ZsetRangeNext
  12. RedisModule_ZsetRangePrev
  13. RedisModule_ZsetRangeEndReached

hash 相關API

同樣的hash相關的API文檔也是missing。

  1. RedisModule_HashSet
  2. RedisModule_HashGet

Replicating commands

Redis module 也提供了對於AOF和salve同步的支持功能。 在文檔中,如果想要命令被replication 有以下的幾種方式:

  1. 在RedisModule_Call的時候在format specifier中加一個! 號。例如:
reply = RedisModule_Call(ctx,"INCR","!sc",argv[1],"10");
  1. 如果使用的是low level api ,則需要調用RedisModule_ReplicateVerbatim :
RedisModule_ReplicateVerbatim(ctx);
  1. 還有一種方法是調用RedisModule_Replicate
RedisModule_Replicate(ctx,"INCRBY","cl","foo",my_increment);

Automatic memory management

在前面的內容中,一直提到一個內容就是自動內存管理。在此做下介紹。通常,當用C語言編寫程序時,需要手動管理內存。這就是為什麽Redis模塊API具有釋放字符串、關閉打開的密鑰、釋放響應等功能。但是,由於命令是在一個包含的環境中執行的,並且有一組嚴格的API,因此Redis能夠以一定的性能(大多數情況下,成本非常低)為模塊提供自動內存管理:

RedisModule_AutoMemory(ctx);

Allocating memory into modules

普通的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相同的分配器,在調用這些函數的時候,相關的內存分配信息會被記錄到Redis的info信息中。如果是使用libc malloc 這些信息都不會被記錄。使用模塊函數來分配內存的另一個原因是,在模塊內創建本機數據類型時,RDB加載函數可以(從RDB文件)直接返回反序列化字符串作為redis module_alloc()分配,這樣它們可以在加載後直接用於填充數據結構,而不必復制它們的數據結構。

結尾

看完和翻譯完redis的文檔,還是有點小期待的。畢竟可以有這麽一個棒棒的功能,可以隨時擴展Redis的功能。但是也有一些不完美的地方,例如對於Redis cluster 的支持,文檔中沒有任何的說明,要自己去探索。下一篇文章將會實操一次,編寫一個mini的插件,同時探索下module插件如何獲得cluster 集群能力的支持。


作者:bush2582
mail:[email protected]

redis module 學習—官網文檔整理