1. 程式人生 > >redis 4.0 擴充套件模組介紹(官網文章翻譯)

redis 4.0 擴充套件模組介紹(官網文章翻譯)

Redis 模組介紹

  Redis外部模組是用來擴充套件 Redis功能的,我們可以實現一個與Redis原生命令速度和特性類似的命令在Redis核心中。Redis modules 實際是一個動態庫,它能夠讓Redis-cli 使用 MODULE LOAD 命令去把它載入到Redis核心。Redis module 使用 C 的API實現的,相應的函式在redismodule.h標頭檔案中,這也叫意味著RedisModule 必須使用 c或者c++這種去實現它。
  Redis Module 不依賴於Redis的版本,只要相應Redis 版本支援模組載入即可。所以如果有個新Redis版本出現了,之前的模組不用重新編譯和設計就可以被載入到新的版本中。這都歸功於Redis Module 使用了一個特殊的API version來在Redis內部進行註冊,當前API version 是 1.

載入模組

載入模組方法
  • 方法1 在redis.conf 中增加 loadmodule /path/to/mymodule.so
  • 方法2 使用MODULE LOAD /path/mymodule.so 命令去載入
讀取模組列表命令
  • MODULE LIST
解除安裝模組命令
  • MODULE UNLOAD mymodule
    模組的動態庫的命名應該和模組一個名字這是一個很好的習慣

module demo

為了能更好的展示一個模組的不同組成部分,在本文將實現一個返回一個隨機數的命令的模組

#include "redismodule.h"
#include <stdlib.h>
int HelloworldRand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { RedisModule_ReplyWithLongLong(ctx,rand()); return REDISMODULE_OK; } MODULE LOAD ./xxx.so 1 2 3 4 5 .so 後面的才是引數 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; }

上面的倆個函式,第一個函式是我們自定義命令(模組)的一個函式,第二個函式是RedisModule_Onload是一個必須出現在每個Redis module中的一個函式,這個函式裡面要對一個指向redis module 的一個指標進行初始化,並且對其註冊命令、可能被使用到的私有結構體。
注意我們給一個命令的命名風格是 module.command 。這樣就不太可能出現命令重名的情況,如果不同的模組有相同命名的命令Redis會報錯,因為一個 RedisModule_CreateCommand 函式將會在其中一個模組的建立命令的過程中失敗,所以模組載入將會返回 REDISMODULE_ERR 的錯誤碼

Module 初始化

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

上面這個函式應該是在OnLoad函式中第一個被呼叫的,它用來告訴Redis核心我們要註冊的module的module_name 、module_version、api_version,如果這個api不是第一個呼叫的直接呼叫其他api會出現core dump
如果有API版本錯誤、模組name已存在、其他類似的錯誤話,這個函式將返回REDISMODULE_ERR,並且在OnLoad函式中必須把這個錯誤返回出去。
注意如果這個函式不是第一個呼叫的那麼會發生段錯誤導致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 ,以至於對建立命令、執行命令的時候都需要一個這樣的引數。
第二個引數指的是命令名字
第三個引數是一個具體命令實現的函式指標
第四個引數是flag 標誌,它由以下多個或者一個構成,當多個構成的時候中間以空格為分隔符。

  • “write” 它表示這個命令也行會修改資料
  • “readonly” 它表示這個函式只會從讀取資料,但是不會修改資料
  • “admin” 表示管理命令,使用這個引數表示在實現的命令函式中也許會做replication(同步)之類的操作
  • “deny-oom” 表示也許會使用一些額外的記憶體但在記憶體不足的時候不會再強行阻塞式申請
  • “deny-script” 不允許這個 module 命令使用到Lua指令碼中
  • “allow-loading” 允許這個命令當redis-server在載入資料的時候允許,只有該命令不會與Redis內部的資料互動的時候才可以使用這個module 命令,如果不瞭解這個選項千萬別用
  • “pubsub” 這個選項表這個命令會發出一些東西在 Pub/Sub 管道
  • “random” 這個選項表該命令有不同的輸出即使有相同的引數
  • “allow-stable” The command is allowed to run on slaves that don’t serve stale data. Don’t use if you don’t know what this means.
  • “no-monitor” Don’t propoagate the command on monitor. Use this if the command has sensible data among the arguments.
  • “fast” 這個命令的時間複雜度不會高於 logN ,N是遍歷元素的個數

為了在上面講的 ctx中建立一個命令,我們需要一個ctx、命令名、函式指標。這個函式指標的型別是RedisModuleCmdFunc

RedisModuleCmdFunc

該函式的第一個引數也是ctx,它將被相應的其他API呼叫它時傳遞,其他的倆個命令列引數由使用者傳遞。就像下面的原型所展示的出的,第二個引數型別是一個特殊的string的陣列。對於這個RedisModuleString來講,它是一個特殊的資料型別,我們只能通過API去訪問、使用它,絕對不能直接去訪問或者修改它的內容。

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

回顧到上面初始化命令時的Load函式中,還有一個RedisModule_ReplyWithLongLong函式,該函式只是返回一個整形給呼叫這個命令的client,事實上其他Redis命令也是這個原理,比如 INCR or SCARD

傳遞配置引數給Redis module

當一個Redis module被MODULE LOAD 命令載入時或使用 loadmodule 直接在redis.conf檔案中載入時,使用者是能夠傳遞配置引數給相應的模組通過在載入一個模組時在後面追加引數,列如

loadmodule mymodule.so  foo bar 1234

在上面的列子中,string 型別的 boo、bar 與 123將被傳遞給 OnLoad函式在 argv陣列中,它們的引數個數就是argc。這些命令列引數都會被儲存在一個static 的全域性變數中,它可以在該模組中被廣泛的訪問,以至於可以起到相同的引數能夠在不同的命令中有不同的表現

如何使用RedisModuleString

這個特殊的string型別,通常被傳遞給自定義的commands函式中,並且也有可能其他module的返回值也是這個型別
通常上我們都是直接傳遞這個string給其他module API,然後有時你也需要去直接訪問這個string物件,所以下面介紹一些操作該string物件的函式

讀取RedisModuleString

可以看到下面的函式原型,它返回的是一個const 型別的指標,所以我們只能去讀這個 RedisModuleString中的內容但是絕對不能修改它

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

如果你想去建立一個新的string 物件你可以用下面的API,它返回一個RedisModuleString物件,我們必須記得析構它因為它是在堆上申請的記憶體,否則會造成記憶體洩漏。如果我們要釋放它,得使用相應的RedisModule_FreeString函式

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

如果你想去使用RAII的方式去管理這個string,可以使用Redis提供的相應函式,下面會介紹到。
注意命令列引數的string是絕對不能釋放的,我們只能free我們建立的string 或者 一些API標註了它返回的string必須要釋放的那些string。

void RedisModule_FreeString(RedisModuleString *str);
RedisModuleString版本的 atoi 或 itoa

我們大多數時候都有需要把 整形轉化為一個string的需求,那麼我們可以通過下面這個函式來實現

RedisModuleString *mystr = RedisModule_CreateStringFromLongLong(ctx,10);

相似地也可以把string 轉換為一個整形

RedisModule_StringToLongLong(RedisModuleCtx * ctx,RedisModuleString ** argv ,int argc)
long long myval;
if (RedisModule_StringToLongLong(ctx,argv[1],&myval) == REDISMODULE_OK) {
    /* Do something with 'myval' */
}
論使用 modules 訪問 Redis key

大多數自定義模組為了便用性,都不得不直接訪問Redis 的資料。Redis 模組 有倆套 API 可以訪問Redis資料,一套是低階API,一套是高階API。低階API提供更快的訪問和一系列操作Redis資料結構的函式介面。高階API則通過使用Redis命令去得到相應結果,它與Lua 訪問 Redis很型別,但是高階API也是很有用的,它可以訪問低階API不可訪問的Redis功能。
通常上模組開發者更喜歡低階API,因為命令實現時使用低階API可以達到原生命令的執行速度。但是也有明確的高階API使用場景比如有時一個需求的瓶頸不在訪問速度而在於處理速度。但是也要記住,有時使用低階API不比高階API難。

高階API

高階API實現Redis commands

高階API 是由一系列的RedisModule_Call 和 訪問reply物件函式構成的一個集合。
RedisModule_Call 有一個特殊的引數格式,這個引數格式通過可變引數來表示出傳遞給RedisModule_Call函式的都是什麼引數型別,RedieModule可接受的引數型別有 C風格字串、RedisModuleString、 帶有len的二進位制的Cbuffer、longlong。
當我們想呼叫 INCRBY 指令時它接受的第一個引數就是先前已經收到的一個命令列引數(這個指已經設定過的key),第二個引數是一個C風格的number字串,如下

[[email protected] src]$ ./redis-cli 
127.0.0.1:6379> set num 10
OK
127.0.0.1:6379> get num
"10"
127.0.0.1:6379> INCRBY num 20
(integer) 30
127.0.0.1:6379> get num
"30"

對於RedisModuleCallReply的INCRBY如下它的第一個引數是 module context ,第二個是個C風格的字串表示命令名,第三個是引數格式,剩下倆個是自定義指定的來個引數,其中一個是RedisModuleString 一個是C風格字串10

RedisModuleCallReply *reply;
reply = RedisModule_Call(ctx,"INCR","sc",argv[1],"10");
RedisModule_Call
RedisModuleCallReply * RedisModule_Call(RedisModuleCtx * ctx , const char * command , 
const char * format,char * arg,...);
RedisModule_Call的引數格式
  • c : c風格字串(以\0結尾的字串)
  • b : c buffer ,它是一個c的string物件和一個size_t 倆部分組成
  • s : RedisModuleString 通常是由其他Redis module API返回的一個物件
  • l : longlong 整形
  • v : 一個RedisModuleString的陣列
  • !: 它不是型別,只是用來表示該命令同步到 slaves 和 AOF的。

如果成功返回 RedisModuleCallReply物件指標,失敗返回NULL。

EINVAL  引數格式字串無法失敗 或 引數個數錯誤 或命令名錯誤
EPERM  在開啟了Cluster模式,當目標key沒有對應的hash clot (hash 槽)的時候返回該錯誤
使用RedisModuleCallReply物件
獲取RedisModuleCallReply物件的型別
RedisModule_CallReplyType(RedisModuleCallReply * reply);

該函式可以獲得Reply物件的型別
合法的 reply type

  • REDISMODULE_REPLY_STRING 大部分string
  • REDISMODULE_REPLY_ERROR error
  • REDISMODULE_REPLY_INTEGER 64bit int
  • REDISMODULE_REPLY_ARRAY 雖然是一個reply 物件,但是內部封裝的是reply 陣列,這個型別就是代表陣列型別
  • REDISMODULE_REPLY_NULL 代表 NULL 的reply
reply = RedisModule_Call(ctx,"INCR","sc",argv[1],"10");
if (RedisModule_CallReplyType(reply) == REDISMODULE_REPLY_INTEGER) {
    long long myval = RedisModule_CallReplyInteger(reply);
    /* Do something with myval. */
}
RedisModule_CallReplyLength

對於string 、error、array它們三個型別都是有相應的長度的如下是獲取長度的函式

size_t reply_len = RedisModule_CallReplyLength(reply);
RedisModule_CallReplyInteger

獲取 int 型別的Reply的值,當傳遞的Reply型別不正確返回 LLONG_MIN 錯誤

long long reply_integer_val = RedisModule_CallReplyInteger(reply);
RedisModule_CallReplyArrayElement

訪問Reply 陣列的元素,如果返回NULL則表示越界

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

獲取string 和 error的型別的結果,注意我們不能修改返回的這個字串,如果傳入型別不對返回NULL

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

用來把error、int、string型別的Reply轉換為一個 Redis Module string , 這個返回的string 必須通過RedisModule_FreeString 函式來釋放

RedisModuleString *mystr = RedisModule_CreateStringFromCallReply(myreply)
論釋放RedisModuleCallReply物件

  所有API返回的Reply 物件必須都得通過 RedisModule_FreeCallReply釋放。對於Reply的陣列型別,我們只能釋放最外層的reply而不是內層的reply。雖然目前module實現了一個保護機制,當我們去釋放內層reply物件的話它會有相應的機制避免程式崩潰,但是我們不能依賴於這個機制所以不能把這個機制當成RedisModule_FreeCallReply函式的一部分。
  如果你使用了自動內層管理,你就不需要每次都去釋放API返回的Reply物件

對於外層reply 和 內層reply物件的解釋
RedisModuleCallReply * nestedreply;
nestedreply =  RedisModule_CallReplyArrayElement(OutReply,idx);
向client 返回結果

  像一個正常的Redis命令一樣,我們通過module實現的 Redis命令也必須給client返回一個返回值。Redis Module實現了一系列函式給我們,讓我們可以用它們去實現向client返回一個屬於Redis 協議的結果(原文 array of Redis protocol types as elemented)。error 也能夠返回一個相應帶有錯誤資訊的string 和 錯誤碼。
  所有向client返回一個迴應的函式都叫做 RedisModule_ReplyWith< something >

向client 返回一個錯誤
RedisModule_ReplyWithError(RedisModuleCtx * ctx ,const char * err);

  有一個預定義的key的錯誤 REDISMODULE_ERRORMSG_WRONGTYPE

demo :
RedisModule_ReplyWithError(ctx,"ERR invalid arguments");
返回一個LongLong

  向client返回一個longlongint

RedisModule_ReplyWithLongLong(ctx,12345);
返回一個簡單的字串(段串)

  為了返回一個簡單的字串,這個簡單的字串不能有一個二進位制值或者一個長序列(所以這個函式只能返回一個簡單的單詞,比如”OK”)

RedisModule_ReplyWithSimpleString(ctx,"OK");
返回一個長字串

  為了返回一個二進位制安全的字串可以用如下倆個介面(所謂二進位制安全的字串意思是它不通過某個特殊字元 (\0) 來判定結尾,比如c++ 的string型別就是一個二進位制)

int RedisModule_ReplyWithStringBuffer(RedisModuleCtx *ctx, const char *buf, size_t len);
int RedisModule_ReplyWithString(RedisModuleCtx *ctx, RedisModuleString *str);
迴應一個數組資訊

  有的時候為了迴應一堆資訊,我們可以陣列的形式reply , 你僅僅需要先用一個函式先發生出去你的陣列的長度,然後呼叫相應數量的上面那些迴應的API

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

  我們迴應的陣列中的某個元素也可以是一個數組如下

Array 第一個元素是string 第二個元素是一個array
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);
動態設定Reply 陣列

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

RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN);  //設定未知長度的陣列
RedisModule_ReplySetArrayLength(ctx, number_of_items); // 最終再設定陣列長度

  上面是倆個函式的原型,下面是demo

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

低階API

  上面講完高階API了接了下來我們講低階API。從上面的介紹可以看到所謂redis_module的高階API就是通過RedisModule_Call 函式直接去可以去執行相應的命令,高階API 執行命令 步驟是下面這樣的

  1. RedisModule_Call 呼叫
  2. RedisModule_ReplyWith … 迴應結果

  所以看高階API 更像一個bash,它可以直接通過RedisModule_Call 執行Redis的命令去返回結果,而不是直接通過操作底層資料結構返回相應的結果,所以接下來介紹低階API執行/建立命令的過程。

module 命令的引數檢測

  經常有一些命令需要去檢測引數的數量和引數的型別到底對不對。當有一個引數個數出錯的情況下,我們可以呼叫下面的 API去返回一個錯誤資訊

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

  注意RedisModule_WrongArity 這個函式直接會使Redis崩潰
  檢測引數型別包含倆步,第一步開啟key ,第二步檢測它的型別。注意我們只應該去執行引數和型別都正確的命令,如果不正確不應該再進行後面的API操作。

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);
}
低階API訪問一個key

  低階API允許對 key 執行一些相應的操作在與這個key直接關聯的value上,它可以帶有與原生Redis命令一樣的速度去執行module命令。一旦一個key被打開了,那麼就會有一個與之關聯的RedisModuleKey的指標返回回來,這個指標可以傳給其他低階API去操作這個key。因為低階API是相當的快的,所以它不能在執行時有太多的檢測,另外使用者必須意識到以下規則

  1. 多次開啟一個相同的key,對其進行寫操作是未定義行為可能造成崩潰
  2. 當一個key被開啟,它是可以通過其他低階API去訪問的。舉個反面列子,當開啟一個key,然後通過高階API去執行一個del 操作將會造成崩潰,但是如果我們通過執行一些其他的低階API在這個key 上然後關閉它,接著再按照 open - dosth - close 這樣的順序操作它是沒問題的,所以不要通過低階API去開啟一個key 的同時再通過高階API操作它。
開啟key的API
RedisModuleKey * RedisModule_OpenKey(RedisModuleCtx * ctx ,RedisModuleString *str ,int 
mode);

demo
RedisModuleKey *key;
key = RedisModule_OpenKey(ctx,argv[1],REDISMODULE_READ);
mode : REDISMODULE_READ 、REDISMODULE_WRITE 、REDISMODULE_READ | REDISMODULE_WRITE

  函式宣告大概如上,接下來介紹一些注意點

  1. Write 開啟,如果key 不存在就新建立key
  2. Write 開啟,也可以對key進行讀取(但是不應該依賴這個特性它是Redis內部實現的一些原因)
  3. Read 開啟,如果key 不存在返回NULL
關閉key

  我們開啟完key後,執行完操作後,應該及時關閉它。如果開啟了自動記憶體管理機制,那麼我們就可以不去close了,當這個module函式返回後它會自動的關閉還在開啟的key。

RedisModule_CloseKey(key);
檢視一個key型別

  使用 RedisModule_KeyType函式可以獲得一個key的型別

int keytype = RedisModule_KeyType(key);  

返回的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

建立一個key

  為了建立一個新key,可以使用 WRITE 模式去開啟一個key 然後使用相應的 寫API往裡面進行寫即可

RedisModuleKey *key;
key = RedisModule_OpenKey(ctx,argv[1],REDISMODULE_READ); 
// Read 模式其實 寫也打開了可以參考前面的介紹
if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_EMPTY) {
    RedisModule_StringSet(key,argv[2]);
}
刪除key

  如果這個key不是以 WRITE 模式開啟的話,這個函式返回一個REDISMODULE_ERR。注意如果這個key已經被刪除了,它會被設定上相應的標記(empty),當有一個新的WRITE操作發生後這個key就會被創造成一個新key,這個新key 的型別取決於 以具體的寫入API。

RedisModule_DeleteKey(key);
設定key的有效期

  為了設定一個key的有效期,有倆個API提供給我們使用。它們能夠讓我們去 set 、modify、get、unset 一個在相關key 上的超時時間。

mstime_t RedisModule_GetExpire(RedisModuleKey *key);
int RedisModule_SetExpire(RedisModuleKey *key, mstime_t expire);
SetExpire 函式當key不存在的時候會返回REDISMODULE_ERR 
刪除一個key使用REDISMODULE_NO_EXPIRE作為刪除即可

  首先這個expire 時間指的是超時時間,如果超過這個時間,Redis 就會把這個key 刪除,經常用它來作一個快取,當超過這個時間後快取的資料就會被刪除。
  GetExpire 函式可以用來獲得一個已經開啟的key上的期望超時時間。這個函式返回一個毫秒級別的時間,這個時間表示該key最多存活的時間數或者 REDISMODULE_NO_EXPIRE ,REDISMODULE_NO_EXPIRE這個特殊值表示該key沒有設定超時時間 或者 該key不存在(你可以通過 RedisModule_KeyType 函式來區別這倆個錯誤)。
  SetExpire 函式用來改變一個key上的 expire 時間,當呼叫在一個不存在的key時它會返回一個 REDISMODULE_ERR 。當設定這個key的 expire時間的時候,無論這個key有沒有 expire時間,它都會被賦上當前修改的新值。如果想刪除一個key的expire時間,可以使用 REDISMODULE_NO_EXPIRE來作引數使其相應的值被刪除。

獲得一個任何型別key對應的value的長度

  下面這個函式可以獲得任何型別key的長度,如果key是一個string 則返回string的長度,如果是一個聚合結構體(list、set、sorted set 、hash)則返回元素的個數,返回0代表空key

size_t len = RedisModule_ValueLength(key);
string API
設定key為string

  設定一個string型別的key我們可以通過set 命令實現,但是不一樣的是下面這個介面可以設定一個string 型別的key 的時候它可以無視先前的這個型別,無論這個key 是什麼型別(string、set、list…)它都可以強行得把這個key變成string型別然後賦上新值

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

  Redis訪問一個string是以DMA方式訪問的,下面這個API將返回一個指標和一個長度,所以它能夠直接訪問或者直接修改相應的string
  在下面的這個列子我們直接修改了這個string的內容,注意如果你想修改內容的話,你必須以WRITE模式去呼叫StringDMA函式

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

  有的時候我們想直接操作key,我們想去改變這個key的大小,那麼可以使用下面這個介面。這個函式可以擴大或縮小key,如果擴大的話新增字元內容為0。如果一個key為empty,我們擴大就會建立一個相應的string型別的key。
  注意修改或訪問StringDMA的返回值str僅僅在當StringDMA函式被呼叫之後,在操作字串之前沒有任何其他函式被呼叫,這個str字串才是安全的否則可能會崩潰。比如如果在 StringDMA和 訪問str字串之間新增一個擴容函式函式,那麼可能會發生型別迭代器失效造成程式崩潰。所以一定要確保在這些操作String的函式被呼叫後,str的值也需要通過呼叫StringDMA函式來獲得最新的str

RedisModule_StringTruncate(mykey,1024);
List API

  下面的這倆個API可以用來操作string。注意ListPop函式會返回一個相應的RedisModuleString,這個string 如果前面的 RedisModule_CreateString 介面一樣,返回的這個string我們也必須對它負責,可以通過釋放或者開啟了自動記憶體管理來處理這個string

int RedisModule_ListPush(RedisModuleKey *key, int where, RedisModuleString *ele);
RedisModuleString *RedisModule_ListPop(RedisModuleKey *key, int where);
where 的特殊值:
REDISMODULE_LIST_HEAD   表示頭部操作
REDISMODULE_LIST_TAIL   表示尾部操作
set/sortedset/Hash

  這些API官網沒有寫可以直接從module.c中看如何使用

同步命令
高階 API 同步

  只需要在格式化字串最前面加上!即可

低階API同步

  當我們呼叫了下面這個 API ,每次module的執行都會被自動同步。

RedisModule_ReplicateVerbatim(ctx);

總結

  感覺Redis模組實現的真的很巧妙,它可以給使用者一個操作空間,讓使用者去實現動態庫,然後再使用 dlopen函式去執行時載入使用者自定義的動態庫函式,從而實現一些使用者自定義功能。這個Redis module也是教會了我dlopen函式的正常使用姿勢,以後當我們寫程式的話也可以怎麼做,先讀取使用者實現的動態庫名,再使用dlopen函式去動態載入這個動態庫,從而實現一些可擴充套件的功能。
  使用時候需要注意的一些細節點

  1. 用c++ 寫 Redis 模組的時候, RedisModule_OnLoad 一定要用extern C 宣告,要不提示找不到該函式
  2. RedisModule_Init 函式必須在RedisModule_OnLoad 函式內是第一個呼叫的Redis API ,否則會崩潰

相關推薦

redis 4.0 擴充套件模組介紹文章翻譯

Redis 模組介紹   Redis外部模組是用來擴充套件 Redis功能的,我們可以實現一個與Redis原生命令速度和特性類似的命令在Redis核心中。Redis modules 實際是一個動態庫,它能夠讓Redis-cli 使用 MODULE LOAD 命

Nordic官方網路資源介紹/devzone/GitHub

本文將介紹Nordic官方網路資源,包括Nordic官網,開發者論壇(devzone),以及Nordic在GitHub上的共享資源。   1. Nordic官網(產品/SDK/工具/文件庫)   Nordic官網主頁:https://www.nordicsemi.com/,介面如下

Redis 4.0新功能介紹

########################## CLUSTER DOCKER/NAT support ######################## # In certain deployments, Redis Cluster nodes address discovery fails, bec

redis-4.0新功能介紹

阿里雲redis4.0引擎是以社群4.0為基礎,合入大量阿里雲開發的特性以及bugfix後全新推出的售賣版本。除了擁有redis-2.8引擎所具備的所有優勢之外,還帶來了很多新功能。 Lazyfree redis-4.0帶來的Lazyfree機制可以避免del,flu

Apache Hadoop YARN 文章

yarn的根本目標是為了分散資源管理還有任務排程以及監視功能到分離的守護程序。這個目的是擁有一個全域性ResourceManager 和每個應用程式。 應用程式可以是單個作業,也可以是作業的DAG。 resource manager和node manager 構成了資料計算框架。 reso

AMQP協議簡介源自翻譯

對AMQP有了更深刻的認識。 AMQP 0-9-1 簡介 AMQP 0-9-1 和 AMQP 模型高階概述 AMQP是什麼 AMQP(高階訊息佇列協議)是一個網路協議。它支援符合要求的客戶端應用(application)和訊息中介軟體代理(messaging mid

git研究詳解文檔及總結

ems sum lai 倉庫 wing help mod ext maintain 前言:git作為新一代的版本控制軟件,說實話比svn好用多了,個人見解,關於git的詳細介紹及研究,我推薦三個地方 1.git官網上的文檔(推薦UC瀏覽器,比火狐多個英文翻譯的功能) 地

redis cluster 叢集重新分片故障處理基於redis 4.0.6

redis cluster 叢集重新分片故障處理(基於redis 4.0.6)  環境: redis:4.0.6 現象: 開始gem安裝redis預設版本,gem install redis,部署叢集完畢後,測試程式碼寫入叢集資料,然後進行分片,發現一隻報錯,錯誤如下

OpenCV contrib 3.2.0擴充套件模組新增與編譯VS2017+OpenCV3.2.0詳解與排坑

一Cmake配置與生成 安裝cmake下載地址:https://cmake.org/download/ 獲取最新版本:cmake-3.8.1-win64-x64.msi(可執行程式,不是壓縮包) 下載完畢直接執行安裝,只有一個步驟要注意, 選擇‘addCmake t

Linux下的redis單節點安裝和部署redis-4.0.0.tar.gz

最首先你要下載個xshell(360軟體管家就可以),輸入IP,使用者名稱、密碼判斷該IP是否有網:[[email protected]_SOA10 ~]# ping www.baidu.com   若出現圖一所示,則說明該IP有網。( 圖一)1.下載redis-4

redis-4.0.1 源碼一鍵安裝腳本centos 7

inf 啟動 下載 exec var install bin cpu logfile #!/usr/bin/env bash set -e #定義下載路徑: SRC=‘/usr/local/src‘ #定義redis 目錄: REDISDIR=‘/usr/local

RocketMQ最佳實踐4.0版本/概念介紹/安裝除錯/客戶端demo

為什麼選擇RocketMQ 我們來看看官方回答: “我們研究發現,對於ActiveMQ而言,隨著越來越多的使用queues和topics,其IO成為了瓶頸。某些情況下,消費者緩慢(消費能力不足)還會拖慢生產者(造成訊息阻塞)。雖然我們做了最大努力進行優化:節流、斷路器或者回

Python 資料處理擴充套件包: pandas 模組的DataFrame介紹建立和基本操作

DataFrame是Pandas中的一個表結構的資料結構,包括三部分資訊,表頭(列的名稱),表的內容(二維矩陣),索引(每行一個唯一的標記)。 一、DataFrame的建立 有多種方式可以建立DataFrame,下面舉例介紹。 例1: 通過list建立 >

Oracle 10g 10.2.0.1 在Oracle Linux 5.4 32Bit RAC安裝手冊一抹曦陽

oracl track microsoft sun msu ica http net ref Oracle 10g 10.2.0.1 在Oracle Linux 5.4 32Bit RAC安裝手冊(一抹曦陽).pdf下載地址 ,step by step

FineUIMvc v1.4.0 發布了ASP.NET MVC控件庫

ima font .com 基礎 ref baidu 捐贈 三石 全部 FineUIMvc v1.4.0 已經於 2017-06-30 發布,FineUIMvc 是基於 jQuery 的專業 ASP.NET MVC 控件庫,是我們的新產品。由於和 FineUI(專業版)共享

Redis 4.0.1集群搭建

alua 搭建 spa aof sha eval pretty ted 自動生成 Redis 4.0.1集群搭建 一、概述 Redis3.0版本之後支持Cluster. 1.1、redis cluster的現狀   目前redis支持的cluster特性:   1

linux下安裝redis 4.0.2

rediswget http://download.redis.io/releases/redis-4.0.2.tar.gz解壓tar -xvf redis-4.0.2.tar.gz編譯cd redis-4.0.2make mkdir -p /usr/local/redismake PREFIX=/usr/l

redis 4.0.1 | cluster集群

redis cluster安裝:cd /opt wget http://download.redis.io/releases/redis-4.0.1.tar.gz tar zxf redis-4.0.1.tar.gz cd redis-4.0.1 make集群搭建:2臺機器

Redis學習——數據結構介紹

exc 序號 rim smo out tar top 鍵值 就是 一、簡介 作為一款key-value 的NoSQL數據庫,Redis支持的數據結構比較豐富,有:String(字符串) 、List(列表) 、Set(集合) 、Hash(哈希) 、Zset(有序集合),相對

centos6.8 安裝redis 4.0 搭建主從

entos 取數 cas ras isp role while tar.gz con centos6.8 安裝redis 4.0 搭建主從 環境: master:172.17.165.245 slave :172.17.165.230 redis 版本:redis 4