QT hiredis 消息订阅模式

c++,redis相关学习资料:

 https://edu.51cto.com/video/115.html

 https://edu.51cto.com/video/4196.html

redis真的是对c++太不友好了,官方指定文件只能使用同步模式,对于异步模式的编译设置一点都不带提的。

hiredis的异步实现是通过事件来分发redis发送过来的消息的,hiredis可以使用libae、libev、libuv和libevent中的任何一个实现事件的分发,网上大部分案例都是选用libevent。而libevent我下载编译完成后,加入到工程中,hiredis还是各种报错找不到文件,怀疑可能是我的hiredis编译的时候没有将libevent对应的依赖文件一块进行编译,所以会出现很多问题,而且libevent应该是对linux比较友好,window还是差劲!

尝试后发现可以使用 adapters/ae.h,下面介绍如何将所依赖文件一块编译到hiredis静态库中。

redis官网对windows的最新支持为 Redis3.2.100,下载完成后解压,然后将 redis\src 文件夹下的 Win32_Interop文件夹、adlist.c  ae.h  ae.c  ae_wsiocp.c  zmalloc.c  config.h 直接拷贝到deps\hiredis下,然后打开vs2013,将这几个文件添加到hiredis解决方案下,此时进行编译会提示一堆找不到文件,挨个打开文件将所需文件路径改为当前文件路径。然后重新进行编译,此时生成的hiredis.lib将会包含ae事件驱动所需函数文件,加入工程即可正常使用redis异步模式。下面附发布订阅模式源码:
头文件:

#ifndef REDIS_SUB_CLIENT_H  
#define REDIS_SUB_CLIENT_H  

extern "C"
{
#include <stdlib.h>
#include "hiredis\hiredis.h"
#include "hiredis\async.h"
#include "hiredis\adapters\ae.h"
}
#include <string>  
#include <vector> 
#include <boost\thread.hpp>
#include <boost\thread\condition.hpp>
#include <boost\thread\mutex.hpp>
#include <boost\thread\condition.hpp>
#include <boost\signals2\signal.hpp>
#include <boost\signals2.hpp>
#include <iostream>
using namespace std;

class CRedisSubClient
{
public:

    CRedisSubClient();
    ~CRedisSubClient();

    bool init();   //初始化,事件对象,信号量  
    bool uninit();  //释放对象
    bool connect();  //连接服务器
    bool disconnect(); //断开服务器

    bool ConnectBack();//连接状态返回

    bool subscribe(const string &channel_name);    //订阅频道 

    bool RedisAsyncCommand(redisCallbackFn *Callback, void *privdata, const char *format, ...);

private:
    // 下面三个回调函数供redis服务调用  
    // 连接回调  
    static void connect_callback(const redisAsyncContext *redis_context,
        int status);

    // 断开连接的回调  
    static void disconnect_callback(const redisAsyncContext *redis_context,
        int status);

    // 执行命令回调  
    static void command_callback(redisAsyncContext *redis_context,void *reply, void *privdata);

    // 事件分发线程函数  
    static void event_thread(void *data);
    void event_proc();


private:
    // ae事件对象  
    aeEventLoop *loop;
    
    // 事件线程ID  
    boost::thread _event_handle;

    // hiredis异步对象  
    redisAsyncContext *_redis_context;

    //信号量控制
    int _status;    
};

#endif

源:

#include <stddef.h>  
#include <assert.h>  
#include <string.h>  
#include "RedisSubClient.h"

using namespace std;

CRedisSubClient::CRedisSubClient() :loop(0), _redis_context(0), _status(-1)
{
}

CRedisSubClient::~CRedisSubClient()
{
}

bool CRedisSubClient::init()
{
    loop = aeCreateEventLoop(64*1024);    // 创建ae对象
    if (NULL == loop)
    {
        printf("Create redis event failed.\n");
        return false;
    }
    return true;
}

bool CRedisSubClient::uninit()
{
    aeStop(loop);
    return true;
}

bool CRedisSubClient::connect()
{
    _redis_context = redisAsyncConnect("127.0.0.1", 6379);    // 异步连接到redis服务器上,使用6380端口

    if (NULL == _redis_context)
    {
        printf(": Connect redis failed.\n");
        return false;
    }

    if (_redis_context->err)
    {
        printf("Connect redis error: %d, %s\n",_redis_context->err, _redis_context->errstr);    // 输出错误信息  
        return false;
    }

    //此变量hiredis 未使用 可以用作连接回调参数
    _redis_context->data = (void*)this;

    // 将事件绑定到redis context上,使redis的回调跟事件关联 
    redisAeAttach(loop, _redis_context);    

    // 设置连接回调,当异步调用连接后,服务器处理连接请求结束后调用,通知调用者连接的状态  
    redisAsyncSetConnectCallback(_redis_context,&CRedisSubClient::connect_callback);

    // 设置断开连接回调,当服务器断开连接后,通知调用者连接断开,调用者可以利用这个函数实现重连  
    redisAsyncSetDisconnectCallback(_redis_context,&CRedisSubClient::disconnect_callback);

    redisAsyncCommand(_redis_context, NULL, NULL, "AUTH %s", "test123", 7);

    // 启动事件线程  
    _event_handle = boost::thread(CRedisSubClient::event_thread, this);
    if (!_event_handle.joinable())
    {
        printf("Create event thread failed.\n");
        disconnect();
        return false;
    }
    return true;
}

bool CRedisSubClient::disconnect()
{
    if (_redis_context)
    {
        redisAsyncDisconnect(_redis_context);
        //redisAsyncFree(_redis_context);
        _redis_context = NULL;
    }
    return true;
}

bool CRedisSubClient::subscribe(const string &channel_name)
{
    int ret = redisAsyncCommand(_redis_context, &CRedisSubClient::command_callback, this, "SUBSCRIBE %s",
        channel_name.c_str()); //订阅一个频道

    if (REDIS_ERR == ret)
    {
        printf("Subscribe command failed: %d\n", ret);
        return false;
    }

    printf("Subscribe success: %s\n", channel_name.c_str());
    return true;
}


//异步命令调用
bool CRedisSubClient::RedisAsyncCommand(redisCallbackFn *Callback, void *privdata, const char *format, ...)
{
    va_list ap;
    va_start(ap, format);
    int ret = redisvAsyncCommand(_redis_context, Callback, privdata, format, ap); //订阅一个频道
    va_end(ap);
    
    if (REDIS_ERR == ret)
    {
        printf("Command failed: %d\n", ret);
        return false;
    }

    printf("Command success!\n");
    return true;
}

//连接状态返回
bool CRedisSubClient::ConnectBack()
{
    if (_status != REDIS_OK)
        return false;
    else
        return true;
}


//连接
void CRedisSubClient::connect_callback(const redisAsyncContext *redis_context,int status)
{
    CRedisSubClient* slef_this = reinterpret_cast<CRedisSubClient*>(redis_context->data);
    slef_this->_status = status;
    return;
}

//断开
void CRedisSubClient::disconnect_callback(const redisAsyncContext *redis_context, int status)
{
    if (status != REDIS_OK)
    {
        printf(": Error: %s\n", redis_context->errstr);
    }
}

// 消息接收回调函数  
void CRedisSubClient::command_callback(redisAsyncContext *redis_context,void *reply, void *privdata)
{
    if (NULL == reply || NULL == privdata) {
        return;
    }

    redisReply *redis_reply = reinterpret_cast<redisReply *>(reply);

    // 订阅接收到的消息是一个带三元素的数组  
    if (redis_reply->type == REDIS_REPLY_ARRAY &&
        redis_reply->elements == 3)
    {
        printf("Recieve message:%s %s %s\n",
            redis_reply->element[0]->str,
            redis_reply->element[1]->str,
            redis_reply->element[2]->str);
    }
}

void CRedisSubClient::event_thread(void *data)
{
    if (NULL == data)
    {
        printf(": Error!\n");
        assert(false);
        return;
    }

    CRedisSubClient *self_this = reinterpret_cast<CRedisSubClient *>(data);
    
    //进行事件处理循环  
    return self_this->event_proc();
}

void CRedisSubClient::event_proc()
{
    //进行事件处理循环  
    aeMain(loop);
    return;
}

测试:

#include <iostream>
#include "RedisSubClient.h"
using namespace std;


void getCallback(redisAsyncContext *c, void *r, void *privdata)
{
    redisReply *reply = (redisReply *)r;
    if (reply == NULL) return;
    printf("Get key: %s\n", reply->str);

    /* Disconnect after receiving the reply to GET */
    //redisAsyncDisconnect(c);
}


void getsubCallback(redisAsyncContext *redis_context, void *reply, void *privdata)
{
    if (NULL == reply) 
        return;

    redisReply *redis_reply = reinterpret_cast<redisReply*>(reply);

    // 订阅接收到的消息是一个带三元素的数组  
    if (redis_reply->type == REDIS_REPLY_ARRAY &&
        redis_reply->elements == 3)
    {
        printf("Recieve message:%s %s %s\n",
            redis_reply->element[0]->str,
            redis_reply->element[1]->str,
            redis_reply->element[2]->str);
    }
}

CRedisSubClient subcriber;

void Unsubscribe()
{
    boost::this_thread::sleep(boost::posix_time::seconds(30));
    bool ret_sub = subcriber.RedisAsyncCommand(getsubCallback, NULL, "UNSUBSCRIBE %s", "Chat");
    if (ret_sub)
    {
        cout << "UNSUBSCRIBE Successful" << endl;
    }
    return;
}


int main(int argc, char *argv[])
{
    
    bool ret_sub = subcriber.init();

    if (!ret_sub)
    {
        cout<<"Init failed"<<endl;
        return 0;
    }

    ret_sub = subcriber.connect();

    if (!ret_sub)
    {
        cout << "Connect failed" << endl;
        return 0;
    }

    //阻塞等待连接返回成功
    while (!subcriber.ConnectBack())
    Sleep(50);
    
    //测试
    ret_sub = subcriber.RedisAsyncCommand(getCallback, NULL, "GET key");

    if (!ret_sub)
    {
        cout << "Get key failed" << endl;
        return 0;
    }

    ret_sub = subcriber.RedisAsyncCommand(getsubCallback, NULL, "SUBSCRIBE %s","Chat");

    boost::thread th(Unsubscribe);

    boost::this_thread::sleep(boost::posix_time::seconds(60));

    ret_sub = subcriber.RedisAsyncCommand(getsubCallback, NULL, "SUBSCRIBE %s", "Chat");
    
    boost::this_thread::sleep(boost::posix_time::seconds(60));

    subcriber.uninit();
    subcriber.disconnect();    
    getchar();
    return 0;

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值