hiredis的异步处理

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值