详述
基于acl框架库封装redis_c++客户端
(仅供内部使用)
目录
1 案例描述 1
2 案例分析 1
2.1 acl框架库描述 1
2.2 acl框架库结构分析 1
3 解决过程 2
3.1 新建动态/静态库工程 2
3.2 引入库和头文件 3
3.3 具体redis功能实现 3
3.3.1 初始化 3
3.3.2 API定义、使用 4
3.3.3 Redis客户端脚本支持 4
解决结果 5
4 总结 5
关键词:
RedisClient_C、acl框架库、c++、redis客户端、redis服务器、redis集群、redis从服务器
摘 要:
主要讲述如何利用开源acl框架库,编写一个基于c++的redis客户端动态库(RedisClient_C);业务程序可以直接利用该RedisClient_C库和redis服务器交互(添加、删除、处理数据);
acl网络通信与服务器编程框架是一个开源C/C++库;开源代码下载地址:
svn://svn.code.sf.net/p/acl/code/trunk
acl库中有丰富的网络通信功能模块,虽然该模块是对底层系统API的封装,却提供了丰富的高级功能,同时屏蔽了在使用底层系统API容易出错的地方,可以方便程序员快速开发出高效、稳定、安全的网络通信应用;
其中redis客户端和服务器通信模型是acl库中的一种,本文也是利用该模型代码库、头文件实现的客户端封装;该库也是redis官网公布的c++客户端方案的一种;
封装前我们要先了解开源代码结构,分析寻找要利用的开源代码;框架库中代码结构分为:
了解过acl框架库后,就要用它来封装实现c++版本的reids客户端,客户端用vs2010来建立工程,具体实现细节如下:
新建库工程,客户端具体要以动态还是静态形式来生成,可以自行选择;其他主要是添加导入、导出函数控制,本文直接封装为客户端类,对类做导入、导出控制更方便;
需要注意的是工程中需要定义个嵌套类,用作存储有些不需要对外暴漏的对象、成员;
// 通过宏来控制是导入还是导出
#ifdef DLL_EXPORT
#define DLL_SAMPLE_API __declspec(dllexport)
#else
#define DLL_SAMPLE_API __declspec(dllimport)
#endif
class DLL_SAMPLE_API cRedisCli
{
public:
//必须以具体参数来初始化,初始化过程直接和redis-server建链
cRedisCli(char* pAddr, char* pPort, int conn_timeout=10,int rw_timeout=10);
virtual ~cRedisCli(){}
……对外暴漏接口区
private:
class cRedisPrivate; // 嵌套类,定义放在cpp
cRedisPrivate* m_pcPrivate; // 用以存储redis初始化操作对象,不对外暴漏
}
工程首先要包含lib_acl_vc2010d.lib和lib_acl_cpp_vc2010d.lib两个库,两个库通过acl下载代码自行编译即可获得,路径分别为acl\lib_acl、acl\lib_acl_cpp;
两个库依赖的头文件是acl_cpp/lib_acl.hpp和lib_acl.h:
首先要实现的就是和服务器建链,即调用RedisClient_C客户端库时,需要传入构造函数cRedisCli()必须的地址、端口参数,构造函数内部用参数初始化一个局部静态redis_client对象;
redis_client再作为cRedisPrivat私有嵌套类的构造参数new一个私有类对象,私有嵌套类cRedisPrivate的构造函数中采用初始化列表,对我们要用到的redis命令API封装类对象进行实例化;
特别注意,redis_client对象必须定义为static静态对象,该对象是作为指针传入各个redis命令实例类的,程序运行全程都需要其有效。
cRedisCli::cRedisCli(char* pAddr, char* pPort, int conn_timeout/*=10*/,int rw_timeout/*=10*/)
{
acl::string addr(pAddr + pPort);
bool slice_req = false;
acl::acl_cpp_init();
static acl::redis_client client(addr.c_str(), conn_timeout, rw_timeout);
client.set_slice_request(slice_req);
m_pcPrivate = new cRedisPrivate(&client);
if (m_pcPrivate == NULL)
printf("[cRedisCli] New cRedisPrivate failed !\n");
}
// 嵌套类
class cRedisCli::cRedisPrivate
{
friend class cRedisCli;
public:
cRedisPrivate(acl::redis_client* pclient):m_RedisCon(pclient),m_RedisSer(pclient),m_RedisHash(pclient),m_RedisKey(pclient),m_RedisScript(pclient),m_RedisSet(pclient)
{
}
private:
acl::redis_connection m_RedisCon;
acl::redis_key m_RedisKey;
acl::redis_server m_RedisSer;
acl::redis_hash m_RedisHash;
acl::redis_script m_RedisScript;
acl::redis_set m_RedisSet;
};
初始化之后就是针对RedisClient_C客户端库想暴漏的API进行封装即可,简单举例一个,Redis客户端支持向服务器设置客户端名称,对该功能API封装实现如下:
bool cRedisCli::SetClientName(char* pName)
{
if (!pName || !m_pcPrivate)
return false;
acl::string buf(pName);
m_pcPrivate->m_RedisSer.clear();
if (m_pcPrivate->m_RedisSer.client_setname(buf.c_str()) == false)
return false;
printf("ClientSetName ok\r\n");
return true;
}
Acl框架库提供的Redis客户端API中无疑不会放过lua脚本这个大功能,针对脚本acl专门提供了一个操作封装类acl::redis_script,客户端执行函数为acl::redis_script:eval();
需要特别描述的是eval函数执行脚本后的返回值为redis_result对象指针,返回内容分为五类,对于普通的字符串返回,该方法比较简单实用,但对于lua脚本中比较复杂的table对象(即c++中的结构体),redis_result对象返回后提取相对比较麻烦,比较有效的解决方法是使用lua脚本的xml处理功能直接转换为xml字符串再返回,上层业务能够简单明了的拿到一个层级分明的table;
解决结果