C++ 封裝 hredis-win32 實現底層操作解耦,並實現自動重連

windows上操作redis貌似一直不是redis官方的主流想法,如果用C#操作的話,我推薦NServiceKit.Redis這個開源庫來操作,但是如果你使用的是VC++操作redis,很抱歉並沒有什麼特別好用庫可以非常方便的操作redis所有功能,經過精挑細選,我找到了

但是這個庫也是有不少坑,尤其是編譯過程中“”結構體成員對齊“”他默認選擇的1字節對齊,所以你這樣編譯出來的lib文件對VC編譯器大部分使用默認8字節對齊簡直就是災難。爲了避免大家踩坑我這裏放一個VS2013 MDd模式,默認結構對齊方式 編譯 好的一個lib庫給大家下載,免走編譯坑。http://pan.baidu.com/s/1kVAxg0Z

剩下就廢話不多說直接上我對這個庫的二次封裝代碼吧。先看主函數調用效果(封裝好後就是這麼Easy使用)
注意hiredisD_VS2013.lib這個lib庫要附加到工程裏編譯額(後續有時間一定放上完整能跑的工程到Csdn下載中心)

// 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);










	}
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章