如何在windows平台使用c++连接redis
- 1. 下载hiredis的vs工程文件
- 2. 使用vs2022编译hiredis
- 3.项目中调用
- 4. 集群连接
- 5. 简单的封装下
最近需要在windows PC终端读取redis数据。我这里使用hiredis连接redis. 工程是vs2022开发的。
注意:如果是使用的‘hiredis’就不能在项目里面使用windows socket一类的函数了,应该‘hiredis’把这些函数全部替换了。无语!!目前我没找到简单的解决方法,除非改它源码,麻烦。
1. 下载hiredis的vs工程文件
windows平台需要自己编译hiredis这个库,这个是一个静态库,所以使用它的终端程序也要改成静态库模式。如果你的程序是用动态库的方式构架的,恭喜了,用不了, 需要自己在封装一层。
官网地址是https://github.com/redis/hiredis, 这个地址是没有vs工程的,但好心的微软提供了vs工程文件,我们直接到微软提供的地址上下载就可以https://github.com/microsoft/hiredis. 工程文件入下图
把整个项目都下载下来就可以。
2. 使用vs2022编译hiredis
启动vs的工程项目,工程名称见下图
工程就可以打开了
然后编译,hiredis和Win32_Interop这个两个项目就可以。
在编译Win32_Interop时可能会有报错编译不过买这个时候最简单就是将错误的地方注释掉,这边这些倒错都是报异常的代码,可以不用管。
把编译出来的lib放到一个lib目录里面
然后需要把头文件也考出来
deps\hiredies\hiredies.h
src\Win32_Interop\win32_types_hiredis.h
修改一下 hiredies.h 文件中引用 win32_types_hiredis.h 的代码:因为路径变了,将原来的注释掉
3.项目中调用
在项目中引入工厂的include目录和lib目录, 然后添加hiredis.lib,Win32_Interop.lib到以来依赖库中
然后把项目改成静态库模式
我的是MFC项目,所有MFC也要改成静态库模式
这样就大功告成了!!!!!!
4. 集群连接
有第3方库支持主备集群切换,但是还要自己下载编译,太太麻烦了, 直接看了原理,也就是配置多个服务器地址,连接一个服务器以后,查询下服务器的状态,是不是主的就主的就用这个连接,否在查询一个服务器地址。重连也是一样。使用命令”info replication“可以查询服务器的状态,找”role:master“这个字符串就可以了。
5. 简单的封装下
由于redisReply每次返回数据都要手动删除,直接封一个智能指针。这样就不用自己手动释放了, 我的项目都是unicode。
class AutoRedisReply
{
public:
redisReply* m_pRedisReply = NULL;
AutoRedisReply()
{
}
AutoRedisReply(redisReply* pRedisReply) // Conversion constructor
:m_pRedisReply(pRedisReply)
{
}
~AutoRedisReply()
{
Release();
}
void Release()
{
if (m_pRedisReply)
{
freeReplyObject(m_pRedisReply);
m_pRedisReply = NULL;
}
}
void Attach(redisReply* pRedisReply)
{
if (pRedisReply != m_pRedisReply) Release();
m_pRedisReply = pRedisReply;
}
bool IsValid()
{
return m_pRedisReply != NULL;
}
bool IsReplyOK()
{
if (!m_pRedisReply) return false;
return m_pRedisReply->type == REDIS_REPLY_STATUS;
}
bool IsReplyString()
{
if (!m_pRedisReply) return false;
return m_pRedisReply->type == REDIS_REPLY_STRING;
}
std::string GetError()
{
std::string strError;
if (!m_pRedisReply) return strError;
strError = m_pRedisReply->str;
return strError;
}
};
redis连接类头文件
class ServerItem
{
public:
std::string _strUrl;
long _nPost = 0;
};
class JSRedis
{
public:
JSRedis();
~JSRedis();
bool Connect();
bool IsConnect();
redisReply* PopList();
bool LoadConfig();
void Release();
private:
redisContext* m_pRedis = NULL;
std::string m_strAdress;
long m_lPort = 0;
std::string m_strPassword
std::vector<ServerItem> m_aryServer; //多个服务器
long m_lCurServerIndex = 0;
};
cpp实现
#include "pch.h"
#include <codecvt>
#include "JSRedis.h"
#include "JSLog.h"
#include "EnvironmentVariable.h"
std::wstring UTF8ToWString(const std::string& str)
{
std::wstring_convert<std::codecvt_utf8<wchar_t>> myconv;
return myconv.from_bytes(str);
}
JSRedis::JSRedis()
{
}
JSRedis::~JSRedis()
{
Release();
}
void JSRedis::Release()
{
if (m_pRedis)
{
redisFree(m_pRedis);
m_pRedis = NULL;
}
}
bool JSRedis::LoadConfig()
{
const CString& strIni = EnvironmentVariable::GetInstance().GetIniPath();
std::string strValue;
TCHAR szValue[1024] = { 0 };
::GetPrivateProfileString(L"Redis", L"Password", L"", szValue, ARRAYSIZE(szValue) - 1, strIni);
m_strPassword = WStringToUTF8(szValue);
//配置多个服务器
m_aryServer.clear();
int nCount= ::GetPrivateProfileInt(L"Redis", L"ServerCount", 0, strIni);
CString strKey;
for (int i = 0; i < nCount; ++i)
{
ServerItem item;
strKey.Format(L"Server_Url_%d",i);
::GetPrivateProfileString(L"Redis", strKey, L"", szValue, ARRAYSIZE(szValue) - 1, strIni);
strValue = WStringToUTF8(szValue);
if (strValue.empty()) continue;
item._strUrl = strValue;
strKey.Format(L"Server_Port_%d", i);
nValue = ::GetPrivateProfileInt(L"Redis", strKey, 0, strIni);
if (nValue <= 0) continue;
item._nPost = nValue;
m_aryServer.push_back(item);
}
return true;
}
bool JSRedis::Connect()
{
if (m_aryServer.empty()) return false;
int nIndex = m_lCurServerIndex % m_aryServer.size();
const auto& item = m_aryServer[nIndex];
m_strAdress = item._strUrl;
m_lPort = item._nPost;
TRACE_DEBUG(L"[JSRedis::Connect] start connect %s:%d, nIndex=%d", UTF8ToWString(m_strAdress).c_str(), m_lPort, nIndex);
redisContext* pRedis = redisConnect(m_strAdress.c_str(), m_lPort);
if (!pRedis || pRedis->err)
{
TRACE_WARNING(L"[JSRedis::Connect] %s:%d, connect failed", UTF8ToWString(m_strAdress).c_str(), m_lPort);
m_lCurServerIndex ++;
if (pRedis) redisFree(pRedis);
return false;
}
{
AutoRedisReply reply = (redisReply*)redisCommand(pRedis, "AUTH %s", m_strPassword.c_str());
if (!reply.IsValid())
{
redisFree(pRedis);
return false;
}
if (!reply.IsReplyOK())
{
TRACE_WARNING(L"[JSRedis::Connect] %s:%d, password error.", UTF8ToWString(m_strAdress).c_str(), m_lPort);
redisFree(pRedis);
return false;
}
}
{ //查询主从
AutoRedisReply reply = (redisReply*)redisCommand(pRedis, "info replication");
if (!reply.IsValid())
{
redisFree(pRedis);
return false;
}
if (!reply.IsReplyString())
{
m_lCurServerIndex++;
redisFree(pRedis);
return false;
}
std::string strInfo = reply.m_pRedisReply->str;
TRACE_NORMAL(L"[JSRedis::Connect] %s:%d,info replication:%s ", UTF8ToWString(m_strAdress).c_str(), m_lPort, UTF8ToWString(strInfo).c_str());
if (strInfo.find("role:master") == std::string::npos) //从的过滤掉
{
TRACE_WARNING(L"[JSRedis::Connect] %s:%d, is not 'role:master'.", UTF8ToWString(m_strAdress).c_str(), m_lPort);
m_lCurServerIndex++;
redisFree(pRedis);
return false;
}
}
{
AutoRedisReply reply = (redisReply*)redisCommand(pRedis, "select %d", 12);
if (!reply.IsValid())
{
redisFree(pRedis);
return false;
}
if (!reply.IsReplyOK())
{
TRACE_WARNING(L"[JSRedis::Connect] %s:%d, select 12 error.", UTF8ToWString(m_strAdress).c_str(), m_lPort);
redisFree(pRedis);
return false;
}
}
m_pRedis = pRedis;
TRACE_DEBUG(L"[JSRedis::Connect] connect %s:%d success, nIndex=%d", UTF8ToWString(m_strAdress).c_str(), m_lPort, nIndex);
return true;
}
bool JSRedis::IsConnect()
{
return m_pRedis != NULL;
}
redisReply* JSRedis::PopList()
{
std::string strKey = "队列名字"
return (redisReply*)redisCommand(m_pRedis, "RPOP %s", strKey.c_str());
}