【Redis原始碼研究】Redis module開發
作者:張仕華
module的作用
Redis通過對外提供一套API和一些資料型別, 可以供開發者開發自己的模組並且載入到redis中.通過API可以直接操作redis中的資料,也可以通過呼叫redis命令來操作資料(類似lua script).
通過編寫模組可以註冊自己的命令到redis中.
編寫一個module
我們通過編寫一個簡單的module來體驗一下該功能.該module對外提供兩個命令,一個是啟動一個定時任務,每隔5s將redis持久化相關的資訊傳送到pinfo這個channel中,另一個是關閉該定時任務.
註冊該模組後,我們可以通過"subscribe pinfo"來訂閱該渠道,然後就可以定時收到redis持久化相關的資訊,以便做一些監控或相應的應對措施
註冊命令到redis中
//每個模組都必須有該函式.該函式是redis載入模組的入口,我們通過該函式可以註冊相關的命令進去 int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { REDISMODULE_NOT_USED(argv); REDISMODULE_NOT_USED(argc); //註冊模組,模組名稱為'pushpersistenceinfo' if (RedisModule_Init(ctx,"pushpersistenceinfo",1,REDISMODULE_APIVER_1) == REDISMODULE_ERR) return REDISMODULE_ERR; //在redis中建立命令.第二部分為命令名稱,第三部分為執行該命令時的回撥函式 if (RedisModule_CreateCommand(ctx,"pushpersistenceinfo.timer", TimerCommand_RedisCommand,"readonly",0,0,0) == REDISMODULE_ERR) return REDISMODULE_ERR; if (RedisModule_CreateCommand(ctx,"pushpersistenceinfo.stop", TimerStopCommand_RedisCommand,"readonly",0,0,0) == REDISMODULE_ERR) return REDISMODULE_ERR; return REDISMODULE_OK; }
如上函式類似一個模板,只需要填充自己的模組名稱和相應的命令即可.重點是呼叫RedisModule_CreateCommand時的第三個引數-即回撥函式.
定義回撥函式
#define REDISMODULE_EXPERIMENTAL_API #include "../redismodule.h" #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <string.h> #include <errno.h> RedisModuleString *infoStr; int off=0 ;//定時器開關 voidgetPersistenceStatus(RedisModuleCtx *ctx,RedisModuleString **infoStr); voidpublishPersistenceStatus(RedisModuleCtx *ctx,RedisModuleString **infoStr); /* Timer callback. */ //時間任務的回撥函式 void timerHandler(RedisModuleCtx *ctx,void *data) { if(off == 1) return;//如果關閉了定時器,則返回退出 REDISMODULE_NOT_USED(ctx); getPersistenceStatus(ctx,&infoStr);//獲取redis持久化相關的資訊並放入infoStr中 publishPersistenceStatus(ctx,&infoStr);//publish redis持久化相關的資訊到pinfo渠道 RedisModule_CreateTimer(ctx,5000,timerHandler,NULL);//建立定時任務,5s後執行 } int TimerCommand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { off = 0; REDISMODULE_NOT_USED(argv); REDISMODULE_NOT_USED(argc); RedisModule_AutoMemory(ctx);//開啟記憶體的自動管理 getPersistenceStatus(ctx,&infoStr);//獲取redis持久化相關的資訊並放入infoStr中 publishPersistenceStatus(ctx,&infoStr);//publish redis持久化相關的資訊到pinfo渠道 RedisModule_CreateTimer(ctx,5000,timerHandler,NULL);//建立定時任務,5s後執行.回撥函式為timerHandler return RedisModule_ReplyWithSimpleString(ctx, "OK");//給客戶端返回字串"OK" } int TimerStopCommand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { off = 1;//關閉定時任務 return RedisModule_ReplyWithSimpleString(ctx, "OK");//給客戶端返回字串"OK" } voidgetPersistenceStatus(RedisModuleCtx *ctx,RedisModuleString **infoStr){ RedisModuleCallReply *reply; //呼叫info Persistence獲取redis持久化相關的資訊 reply = RedisModule_Call(ctx,"info","c","Persistence"); *infoStr = RedisModule_CreateStringFromCallReply(reply); } voidpublishPersistenceStatus(RedisModuleCtx *ctx,RedisModuleString **infoStr){ //呼叫publish pinfo xxxxx將持久化資訊推送到pinfo渠道 RedisModule_Call(ctx,"publish","cs","pinfo",*infoStr); }
執行pushpersistenceinfo.timer和pushpersistenceinfo.stop命令後會分別回撥TimerCommand_RedisCommand和TimerStopCommand_RedisCommand這兩個回撥函式.前者會建立一個定時任務,定時任務回撥函式為timerHandler,如果off不為1,則回撥函式中會再次建立定時任務;後者會將off置為1,不再執行定時任務.
演示
將模組置於redis原始碼目錄的src/modules/目錄中,然後執行如下命令編譯模組
gcc -fPIC -std=gnu99 -c -o pushpersistenceinfo.o pushpersistenceinfo.c ld -o pushpersistenceinfo.so pushpersistenceinfo.o -shared -Bsymbolic -lc
載入模組
127.0.0.1> module load $RedisSourcePath/src/modules/pushpersistenceinfo.so OK 127.0.0.1> module list 1) 1) "name" 2) "pushpersistenceinfo" 3) "ver" 4) (integer)
執行命令(首先訂閱pinfo渠道)
redis-clisubscribe pinfo Reading messages... (press Ctrl-C to quit) 1) "subscribe" 2) "pinfo" 3) (integer) 1
執行模組中的命令
127.0.0.1> PUSHPERSISTENCEINFO.timer OK
檢視輸出(可以看到,每隔5s會輸出一次)
1) "message" 2) "pinfo" 3) "# Persistence\r\nloading:0\r\nrdb_changes_since_last_save:15\r\nrdb_bgsave_in_progress:0\r\nrdb_last_save_time:1555494610\r\nrdb_last_bgsave_status:ok\r\nrdb_last_bgsave_time_sec:-1\r\nrdb_current_bgsave_time_sec:-1\r\nrdb_last_cow_size:0\r\naof_enabled:1\r\naof_rewrite_in_progress:0\r\naof_rewrite_scheduled:0\r\naof_last_rewrite_time_sec:-1\r\naof_current_rewrite_time_sec:-1\r\naof_last_bgrewrite_status:ok\r\naof_last_write_status:ok\r\naof_last_cow_size:0\r\naof_current_size:631\r\naof_base_size:631\r\naof_pending_rewrite:0\r\naof_buffer_length:0\r\naof_rewrite_buffer_length:0\r\naof_pending_bio_fsync:0\r\naof_delayed_fsync:0\r\n" 1) "message" 2) "pinfo" 3) "# Persistence\r\nloading:0\r\nrdb_changes_since_last_save:15\r\nrdb_bgsave_in_progress:0\r\nrdb_last_save_time:1555494610\r\nrdb_last_bgsave_status:ok\r\nrdb_last_bgsave_time_sec:-1\r\nrdb_current_bgsave_time_sec:-1\r\nrdb_last_cow_size:0\r\naof_enabled:1\r\naof_rewrite_in_progress:0\r\naof_rewrite_scheduled:0\r\naof_last_rewrite_time_sec:-1\r\naof_current_rewrite_time_sec:-1\r\naof_last_bgrewrite_status:ok\r\naof_last_write_status:ok\r\naof_last_cow_size:0\r\naof_current_size:631\r\naof_base_size:631\r\naof_pending_rewrite:0\r\naof_buffer_length:0\r\naof_rewrite_buffer_length:0\r\naof_pending_bio_fsync:0\r\naof_delayed_fsync:0\r\n"
停止定時器
127.0.0.1> PUSHPERSISTENCEINFO.stop OK
解除安裝模組
127.0.0.1> module unload pushpersistenceinfo OK
該模組程式碼地址:https://github.com/erpeng/red...