Redis有pubsub功能,我感觉用起来不方便,达不到我的要求,于是打算给Redis开发一个第三方模块,当key超时时自动通知RocketMQ超时的key,RocketMQ的消费者在消费消息,做响应的业务逻辑处理。
首先要做的就是github上下载RocketMQ的C++客户端源码:
https://github.com/apache/rocketmq-client-cpp
然后编译,编译总体来说还是比较顺利的,就碰到一个错误:
libboost_iostreams.a for lack of
只需要安装一下bzip2包即可:yum install bzip2-devel
下来就是开发Redis的模块了。
模块也很简单,我就直接贴代码了 :
#define REDISMODULE_EXPERIMENTAL_API
#include "../../redismodule.h"
#include
#include
#include
#include
#include
#include
#include "CProducer.h"
#include "CCommon.h"
#include "CMessage.h"
#include "CSendResult.h"
static CProducer *producer = NULL;
static char* MQAddr = NULL;
static char* MQTopic = NULL;
static char* MQTag = NULL;
static int SendTOMQ (RedisModuleCtx *ctx, const char* expirekey)
{
CMessage *msg = CreateMessage(MQTopic);
SetMessageTags(msg, MQTag);
SetMessageKeys(msg, expirekey);
CSendResult result;
SetMessageBody(msg, "Redis Key Timeout\n");
SendMessageSync(producer, msg, &result);
if (result.sendStatus == E_SEND_OK) {
printf ("Notify MQ successn: %s\n", expirekey);
return REDISMODULE_OK;
}
else {
printf ("Notify MQ fail: %s\n", expirekey);
return RedisModule_ReplyWithError(ctx,"Send to MQ fail\n");
}
}
//int (*RedisModuleNotificationFunc)(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key);
extern "C" int KeyExpireEventCallback (RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key)
{
REDISMODULE_NOT_USED(type);
REDISMODULE_NOT_USED(event);
size_t keylen = 0;
const char *expireKey = RedisModule_StringPtrLen(key, &keylen);
printf ("RocketmqMoudle callback exe: %s\n", expireKey);
//const char* topic = "FOREGROUND_CONSUMER";//
//const char* tag = "EXPIRE";//
if (MQAddr == NULL || MQTopic == NULL || MQTag == NULL) {
return RedisModule_ReplyWithError(ctx,"You haven't set the parameters of MQ yet.");
}
return SendTOMQ(ctx, expireKey);
}
extern "C" int PublishKey(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
if (argc != 2)
return RedisModule_WrongArity(ctx);
const char *expirekey = RedisModule_StringPtrLen(argv[1], NULL);
return SendTOMQ (ctx, expirekey);
}
/*
write readonly admin deny-oom deny-script allow-loading pubsub random allow-stale no-monitor fast getkeys-api no-cluster
*/
extern "C" int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
{
if (RedisModule_Init(ctx,"rocketmq",1, REDISMODULE_APIVER_1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
size_t len;
/* Log the list of parameters passing loading the module. */
for (int j = 0; j < argc; j++) {
const char *s = RedisModule_StringPtrLen(argv[j], &len);
printf("Module loaded with ARGV[%d] = %s\n", j, s);
}
if (argc != 3) {
return RedisModule_WrongArity(ctx);
}
MQAddr = strdup(RedisModule_StringPtrLen(argv[0], &len));
MQTopic = strdup(RedisModule_StringPtrLen(argv[1], &len));
MQTag = strdup(RedisModule_StringPtrLen(argv[2], &len));
if (RedisModule_CreateCommand(ctx,"rocketmq.pubfun", PublishKey,"readonly",1,1,1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
RedisModule_SubscribeToKeyspaceEvents (ctx, REDISMODULE_NOTIFY_EXPIRED, KeyExpireEventCallback);
producer = CreateProducer("redis_producer");
SetProducerNameServerAddress(producer, MQAddr);
if (StartProducer(producer)) {
printf("Module loaded StartProducer fail\n");
return REDISMODULE_ERR;
}
return REDISMODULE_OK;
}
# Compile flags for linux / osx
SHOBJ_CFLAGS ?= -W -Wall -fno-common -g -ggdb -std=c99 -O2
SHOBJ_LDFLAGS ?= -shared
M_R_FLAGS = -Wall -Wno-deprecated -fPIC -fno-strict-aliasing -O3 -DNDEBUG
M_D_FLAGS = -Wno-deprecated -fPIC -fno-strict-aliasing -O0 -DDEBUG
M_INCLUDES = -I/root/rocketmq-client-cpp-master/bin/include -I/root/rocketmq-client-cpp-master/bin/include/jsoncpp -I/root/rocketmq-client-cpp-master/include
LIBPATH = -L/root/rocketmq-client-cpp-master/bin
LDFLAGS = -lrocketmq
.SUFFIXES: .c .so .xo .o
all: rocketmq.so
.cpp.xo:
$(CC) -I. $(CFLAGS) $(SHOBJ_CFLAGS) $(M_D_FLAGS) $(M_INCLUDES) $(LIBPATH) -fPIC -c $< -o $@
rocketmq.xo: ../../redismodule.h
rocketmq.so: rocketmq.xo
$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LDFLAGS) $(LIBS) -lc
clean:
rm -rf *.xo *.so
模块开发需要注意的小地方:RedisModule_OnLoad 函数中第一个执行的函数必须是 RedisModule_Init,否则就报 segmentfalt
再下来就是配置 redis.conf的配置文件
增加一行:
loadmodule /opt/redis-unstable/src/modules/rocketmq.so 你RocketMQ的IP和端口 你的TOPIC 你的TAG
修改一行:
notify-keyspace-events Ex
也可以开发 kafka的通知消息, MQTT的通知消息, 也可以用一个第三方的模块来做 秒杀 的业务逻辑 或者分布式锁相关的业务逻辑。