Redis异步处理
在使用hiredis进行编程时,比较常用的就是使用同步的方式,就是发送一条指令,然后等待结果,这样的操作如果很频繁的话就会造成程序执行比较缓慢,走走停停的感觉。所以这里我们想想能不能使用异步执行命令的方式,hiredis为我们提供了一套异步的接口。
异步的思想:
所谓异步就是把一些耗时的操作单独开一个线程或者进程去执行,通过这样的方式,主程序就不用去等待执行,所以就会提升速度,但其实就是把这样的操作交给另外的worker做。这就是异步的思想。
那么对于执行命令来说:
- 第一我们需要把这个命令发送给redis,所以需要一个发送的动作。
- 第二,redis执行完了以后需要通知我们执行的结果,我们需要对这个结果进行处理,由于是异步的,所以我们可以想到Reactor模式,即回调。
那么我们现在只要这么做:
- 主程序把需要执行的命令发出去,发给谁,发给专门负则执行命令的异步框架,然后主程序就去继续自己的事情,这就好比,我有一堆活要做,我分一些给别人来做。
- 然后worker现在要做的就是把命令发布给redis指定结果处理函数,然后等到redis执行完成后,会回调处理结果的函数。
这样就实现了异步执行命令的操作,其实这种思想没有同步简单粗暴,同步就是一个正常思维,我先干啥在干啥,比如先执行命令再处理结果,而异步就是,我把命令发出去执行,至于啥时候结束,你通知我,也就是回调函数,所以结果都放在回调函数里面处理。
这段代码:
- 第一部分:模拟异步执行set-key-value,统计执行test——cmd——count次的时间。
- 第二部分:一个生产者消费者模型,先执行生产者,然后通知消费者消费,redis异步消息队列就是这样的一个操作。
这里只是模拟使用hiredis异步处理,测试性能。
真正的异步应该是把这个做成一个异步处理框架,也就是一个worker,然后收到来自别人执行的命令,去执行,然后,回调执行结果的处理函数。
#include <iostream>
#ifdef __cplusplus
extern "C" {
#endif
#include <hiredis/async.h>
#include <hiredis/hiredis.h>
#include <hiredis/adapters/libev.h>
#ifdef __cplusplus
}
#endif
#define PORT 6379
#define IP "127.0.0.1"
#define TEST_CMD_COUNT 1000000
int g_iCmdCallback = 0;
unsigned long long g_ullBeginTime = 0;
std::string GetCurrentTime();
unsigned long long GetMicrosecond();
void RdsCbConnect(const redisAsyncContext* pRdsContext, int iStatus);
void RdsCbDisConnect(const redisAsyncContext* pRdsContext, int iStatus);
void RdsCbCmd(redisAsyncContext* pRdsContext, void* pReply, void* pData);
int main() {
redisAsyncContext* pRdsContext = redisAsyncConnect(IP, PORT);//异步连接的上下文对象
if (pRdsContext->err != REDIS_OK) {
printf("async redis connect failed! err code = %d, err msg = %s\n", pRdsContext->err, pRdsContext->errstr);
return 1;
}
struct ev_loop* pLoop = EV_DEFAULT;
redisLibevAttach(pLoop, pRdsContext);
redisAsyncSetConnectCallback(pRdsContext, RdsCbConnect);//设置连接的回调处理函数
redisAsyncSetDisconnectCallback(pRdsContext, RdsCbDisConnect);
ev_run(pLoop, 0);//如果不在这里阻塞,就把这个开启一个线程,这就是worker负责处理命令
return 0;
}
unsigned long long GetMicrosecond() {
timeval stTime;
gettimeofday(&stTime, NULL);
return (stTime.tv_sec * 1000000 + stTime.tv_usec);
}
std::string GetCurrentTime() {
time_t lTime = time(NULL);
struct tm stTime;
localtime_r(&lTime, &stTime);
char szCurrentTime[20] = {0};
strftime(szCurrentTime, 20, "%Y-%m-%d %H:%M:%S", &stTime);
return std::string(szCurrentTime);
}
//一个消息队列,生产请求
void SendProduceRequest(const redisAsyncContext* pRdsContext) {
for (int i = 0; i < TEST_CMD_COUNT; i++) {
size_t iArgSize = 3;
size_t szArglen[iArgSize];
const char* ppArg[iArgSize];
ppArg[0] = "lpush";
szArglen[0] = strlen("lpush");
char szKey[64] = {0};
snprintf(szKey, sizeof(szKey), "redislist_h");
ppArg[1] = szKey;
szArglen[1] = strlen(szKey);
char szValue[1024] = {0};
snprintf(szValue, sizeof(szValue), "{test 1 1 2 hello world %d}", i);
ppArg[2] = szValue;
szArglen[2] = strlen(szValue);
redisAsyncCommandArgv((redisAsyncContext*)pRdsContext, RdsCbCmd, NULL, iArgSize, ppArg, szArglen);
}
}
//消费请求
void SendCustomRequest(const redisAsyncContext* pRdsContext) {
size_t iArgSize = 3;
size_t szArglen[iArgSize];
const char* ppArg[iArgSize];
//brpop redislist_h 10
ppArg[0] = "brpop";
szArglen[0] = strlen("brpop");
ppArg[1] = "redislist_h";
szArglen[1] = strlen("redislist_h");
ppArg[2] = "10";
szArglen[2] = strlen("10");
redisAsyncCommandArgv((redisAsyncContext*)pRdsContext, RdsCbCmd, NULL, iArgSize, ppArg, szArglen);
}
void SendSetRequest(const redisAsyncContext* pRdsContext) {
for (int i = 0; i < TEST_CMD_COUNT; i++) {//发送100000个set命令
size_t iArgSize = 3;
size_t szArglen[iArgSize];
const char* ppArg[iArgSize];
char szTestInfo[64] = {0};
memset(szTestInfo, 'a', sizeof(szTestInfo));
ppArg[0] = "SET";
szArglen[0] = strlen("SET");
char szKey[64] = {0};
snprintf(szKey, sizeof(szKey), "key-%d", i);
ppArg[1] = szKey;
szArglen[1] = strlen(szKey);
char szValue[1024] = {0};
snprintf(szValue, sizeof(szValue), "value-%d-%s", i, szTestInfo);
ppArg[2] = szValue;
szArglen[2] = strlen(szValue);
//执行命令并指定回调函数
redisAsyncCommandArgv((redisAsyncContext*)pRdsContext, RdsCbCmd, NULL, iArgSize, ppArg, szArglen);
}
}
void RdsCbConnect(const redisAsyncContext* pRdsContext, int iStatus) {
printf("connect call back, status = %d \n", iStatus);
printf("test write cmd count = %d\n", TEST_CMD_COUNT);
g_ullBeginTime = GetMicrosecond();
printf("test begin time: %s, %llu\n", GetCurrentTime().c_str(), g_ullBeginTime);
//生产者和消费者模型
//SendProduceRequest(pRdsContext);
SendSetRequest(pRdsContext);//这个函数就是用来执行命令的
//SendCustomRequest(pRdsContext);
}
void RdsCbDisConnect(const redisAsyncContext* pRdsContext, int iStatus) {
printf("disconnect call back, status = %d \n", iStatus);
}
void RdsCbCmd(redisAsyncContext* pRdsContext, void* pReply, void* pData) {
//printf("redis cmd call back, err code = %d, err msg = %s!\n", pRdsContext->err, pRdsContext->errstr);
if (NULL == pReply) {
printf("call back repplay null!\n");
//SendCustomRequest(pRdsContext);
return;
}
redisReply* pRdsReply = (redisReply*)pReply;
//printf("%d, redis reply info: type = %d, string = %s\n", ++g_iCmdCallback, pRdsReply->type, pRdsReply->str);
if (REDIS_REPLY_NIL == pRdsReply->type) {
//SendCustomRequest(pRdsContext);
printf("reply nil\n");
return;
}
//通知消费者消费
//SendCustomRequest(pRdsContext);
//end.
if (++g_iCmdCallback >= TEST_CMD_COUNT) {//执行完成统计时间
unsigned long long ullEndTime = GetMicrosecond();
printf("test end time: %s, %llu, interval: %llu\n",
GetCurrentTime().c_str(), ullEndTime, ullEndTime - g_ullBeginTime);
redisAsyncDisconnect((redisAsyncContext*)pRdsContext);
}
}
参考:https://www.jianshu.com/p/1acaf10ec753