您的位置:首页 > 数据库 > Redis

C++ 封装 hredis-win32 实现底层操作解耦,并实现自动重连

2017-02-04 03:27 781 查看
windows上操作redis貌似一直不是redis官方的主流想法,如果用C#操作的话,我推荐NServiceKit.Redis这个开源库来操作,但是如果你使用的是VC++操作redis,很抱歉并没有什么特别好用库可以非常方便的操作redis所有功能,经过精挑细选,我找到了
开源库hredis-win32:https://github.com/texnician/hiredis-win32

但是这个库也是有不少坑,尤其是编译过程中“”结构体成员对齐“”他默认选择的1字节对齐,所以你这样编译出来的lib文件对VC编译器大部分使用默认8字节对齐简直就是灾难。为了避免大家踩坑我这里放一个VS2013 MDd模式,默认结构对齐方式 编译 好的一个lib库给大家下载,免走编译坑。http://pan.baidu.com/s/1kVAxg0Z

剩下就废话不多说直接上我对这个库的二次封装代码吧。先看主函数调用效果(封装好后就是这么Easy使用)
注意hiredisD_VS2013.lib这个lib库要附加到工程里编译额(后续有时间一定放上完整能跑的工程到Csdn下载中心)
完整源码工程VS2013 http://download.csdn.net/detail/lightspear/9762453

// PBHelper_hiredis-win32-Test.cpp : 定义控制台应用程序的入口点。
//

#include <iostream>
#include "src/hiredis/hiredishelper.h"
#include <vector>
int main()
{
PBLIB::Redis::hiredisUtility redisclass;

//redisclass.Start();// 内部异步实现了自动重连

redisclass.Connect(1000);//开始连接Redis

redisclass.Set("ABC", "123");//设置key
redisclass.Set("QWE", "123456789", 4);//设置key

std::string str;
redisclass.Get("ABC", str);//获取key
redisclass.LPush("listA", "123546");//压入一个list值
redisclass.LPush("listA", "654321");//压入一个list值
std::string popval;
redisclass.LPop("listA", popval);//弹出一个list值

std::vector<std::string> vec;
redisclass.GetList("listA", vec);//获取一个list读到vector中

return 0;
}
接下来直接上封装过程
#pragma once

#include <iostream>
#include <functional>
#include <mutex>
#include <vector>
namespace PBLIB
{
namespace Redis
{

enum class PB_HIREDISUTILITY_EVENT
{
Connect = 100,
TryConnect,
DisConnect
};

class   hiredisUtility
{
public:
typedef std::function<void(const char * msg, PB_HIREDISUTILITY_EVENT, void*)>  EventHandler;

hiredisUtility();

~hiredisUtility();

void Start();

void Connect(int millisecond);

void Disonnect();

bool Ping();

bool CmdGetString(std::string &restr, const char *format, ...);

bool CmdGetInteger(long long &val, const char *format, ...);

bool  CmdAndEuqalString(const char * equalval, const char *format, ...);

//Key,Value
void Get(const char *key, std::string &restr);
bool Set(const char *key, const std::string &valstr);
bool Set(const char *key, char * buff, int size);

//List
long long LPush(const char * listId, char * buff, int size);
long long LPush(const char * listId, const std::string &valstr);

bool LPop(const char * listId, std::string &valstr);
int LLen(const char * listId);
bool LRange(const char * listId, int startingFrom, int endingAt,std::vector<std::string> &vec);

bool GetList(const char * listId, std::vector<std::string> &vec);

EventHandler evenhandler;
std::mutex m_Cmdmutex;
private:
void* Cmd(const char *format, ...);
bool IsAutoReconnect;
char m_ip[15];
int m_port;
void * m_Context;

std::string m_errorDetail;
};
}
}
#include "hiredishelper.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <exception>
#include <thread>

#include "WinSock2.h"
#include "../../external/hiredis-win32-master/include/hiredis.h"

#pragma comment(lib, "ws2_32.lib")

namespace PBLIB
{
namespace Redis
{
#define REDIS_REPLY_STR_OK "OK"

hiredisUtility::hiredisUtility() :m_port(6379)
{
strcpy(m_ip, "127.0.0.1");
m_Context = nullptr;
evenhandler = nullptr;

//main2();
}

hiredisUtility::~hiredisUtility()
{
IsAutoReconnect = false;//停止自动重连线程
}

//http://blog.csdn.net/gdutliuyun827/article/details/44339007

bool hiredisUtility::Ping()
{
return CmdAndEuqalString("PONG", "PING");
}

//万能方案备用
void* hiredisUtility::Cmd(const char *format, ...)
{
if (m_Context == nullptr) return NULL;

redisContext *c = (redisContext*)m_Context;

va_list ap;
void *reply = NULL;
va_start(ap, format);
reply = redisvCommand(c, format, ap);
va_end(ap);

if (!c->err)
{
return reply;
}
return NULL;
}

bool hiredisUtility::CmdGetString(std::string &restr, const char *format, ...)
{
if (m_Context == nullptr) return false;

std::lock_guard<std::mutex> locker(m_Cmdmutex);
redisContext *c = (redisContext*)m_Context;

va_list ap;
void *reply = NULL;
va_start(ap, format);
reply = redisvCommand(c, format, ap);
va_end(ap);

if (!c->err)
{
bool IsErroring = false;
bool flag = false;
redisReply* r = (redisReply*)reply;
if (r->type == REDIS_REPLY_STRING)
{
restr = r->str;
flag = true;
}
if (r->type == REDIS_REPLY_ERROR)
{
IsErroring = true;
m_errorDetail = r->str;
}
freeReplyObject(r);
if (IsErroring)  throw std::exception(m_errorDetail.data());//直接丢错
return flag;
}
return false;
}

bool hiredisUtility::CmdGetInteger(long long &val, const char *format, ...)
{
if (m_Context == nullptr) return false;

std::lock_guard<std::mutex> locker(m_Cmdmutex);
redisContext *c = (redisContext*)m_Context;

va_list ap;
void *reply = NULL;
va_start(ap, format);
reply = redisvCommand(c, format, ap);
va_end(ap);

if (!c->err)
{
bool IsErroring = false;
bool flag = false;
redisReply* r = (redisReply*)reply;
if (r->type == REDIS_REPLY_INTEGER)
{
val = r->integer;
flag = true;
}
if (r->type == REDIS_REPLY_ERROR)
{
IsErroring = true;
m_errorDetail = r->str;
}
freeReplyObject(r);
if (IsErroring)  throw std::exception(m_errorDetail.data());//直接丢错
return flag;
}
return false;
}

bool hiredisUtility::CmdAndEuqalString(const char * equalval, const char *format, ...)
{
if (m_Context == nullptr) return false;
std::lock_guard<std::mutex> locker(m_Cmdmutex);

redisContext *c = (redisContext*)m_Context;

va_list ap;
void *reply = NULL;
va_start(ap, format);
reply = redisvCommand(c, format, ap);
va_end(ap);

if (!c->err)
{
bool IsErroring = false;
bool flag = false;
redisReply* r = (redisReply*)reply;
if (r->str != NULL)
flag = strcmp(equalval, r->str) == 0;//如果返回是str
if (r->type == REDIS_REPLY_ERROR)
{
IsErroring = true;
m_errorDetail = r->str;
}
freeReplyObject(r);
if (IsErroring)  throw std::exception(m_errorDetail.data());//直接丢错
return flag;
}
return false;
}

void hiredisUtility::Get(const char *key, std::string &restr)
{
CmdGetString(restr, "get %s", key);
}

bool hiredisUtility::Set(const char *key, const std::string &valstr)
{
return	CmdAndEuqalString(REDIS_REPLY_STR_OK, "set %b %b", key, strlen(key), valstr.data(), valstr.size());
}

bool hiredisUtility::Set(const char *key, char * buff, int size){
return	CmdAndEuqalString(REDIS_REPLY_STR_OK, "set %b %b", key, strlen(key), buff, size);
}

long long hiredisUtility::LPush(const char * listId, char * buff, int size)
{
long long res = 0;
CmdGetInteger(res, "LPush %b %b", listId, strlen(listId), buff, size);
return res;
}

long long hiredisUtility::LPush(const char * listId, const std::string &valstr){

long long res = 0;
CmdGetInteger(res, "LPush %b %b", listId, strlen(listId), valstr.data(), valstr.size());
return res;
}

bool hiredisUtility::LPop(const char * listId, std::string &valstr)
{
return CmdGetString(valstr, "LPop %s", listId);
}

void hiredisUtility::Connect(int millisecond)
{
if (m_Context != nullptr) return;

int tv_sec = millisecond / 1000;
int millisec = millisecond % 1000;
long tv_usec = millisec * 1000;
struct timeval timeout;
timeout.tv_sec = tv_sec;
timeout.tv_usec = tv_usec;

redisContext  *c;
c = redisConnectWithTimeout(m_ip, m_port, timeout);
if (c != NULL && c->err)
{
redisFree(c);
return;
}

m_Context = c;

if (m_Context != nullptr)
if (evenhandler != nullptr)
evenhandler("Redis连接上", PB_HIREDISUTILITY_EVENT::Connect, NULL);
}

int hiredisUtility::LLen(const char * listId)
{
long long res = 0;
CmdGetInteger(res, "LLen %b", listId, strlen(listId));
return res;
}

bool hiredisUtility::LRange(const char * listId, int startingFrom, int endingAt, std::vector<std::string> &vec)
{
std::lock_guard<std::mutex> locker(m_Cmdmutex);
void *reply = Cmd("LRange %s %d %d", listId, startingFrom, endingAt);
if (reply == NULL) return false;
bool IsErroring = false;
bool flag = false;
redisReply* r = (redisReply*)reply;
if (r->type == REDIS_REPLY_ERROR)
{
IsErroring = true;
m_errorDetail = r->str;
}
if (r->type == REDIS_REPLY_ARRAY)
{
flag = true;
for (int j = 0; j < r->elements; j++)
{
std::string str = r->element[j]->str;
vec.push_back(str);
}
}
freeReplyObject(r);
if (IsErroring)  throw std::exception(m_errorDetail.data());//直接丢错
return flag;
}

bool hiredisUtility::GetList(const char * listId, std::vector<std::string> &vec){
int len = LLen(listId);
return LRange(listId, 0, len - 1, vec);
}

void hiredisUtility::Start()
{
IsAutoReconnect = true;
std::thread th([this](){

while (IsAutoReconnect)
{
std::chrono::milliseconds timespan(1000);
std::this_thread::sleep_for(timespan);
if (Ping() == false)
{
Disonnect();
evenhandler("Redis尝试连接", PB_HIREDISUTILITY_EVENT::TryConnect, NULL);
Connect(1000);
}
}
Disonnect();
});
th.detach();
}

void hiredisUtility::Disonnect()
{

if (m_Context == nullptr) return;

redisContext *c = (redisContext*)m_Context;
if (c != NULL)
{
redisFree(c);
m_Context = nullptr;
evenhandler("Redis断开了", PB_HIREDISUTILITY_EVENT::Connect, NULL);
}
}

//void redisFree(redisContext *c);

}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息