【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 ;//定時器開關
void  getPersistenceStatus(RedisModuleCtx *ctx,RedisModuleString **infoStr);
void  publishPersistenceStatus(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"
}

void  getPersistenceStatus(RedisModuleCtx *ctx,RedisModuleString **infoStr){
    RedisModuleCallReply *reply;
//調用info Persistence獲取redis持久化相關的信息
    reply = RedisModule_Call(ctx,"info","c","Persistence");

    *infoStr = RedisModule_CreateStringFromCallReply(reply);
}

void  publishPersistenceStatus(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-cli  subscribe 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...

參考文檔

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章