Hiredis是一个Redis的C客户端库函数,基本实现了Redis的协议的最小集。这里对hiredis的api作基本的介绍以及应用,主要参考hiredis的README文件以及相关源码。

1、Ubuntu安装redis服务端

Redis全称为Remote Dictionary Server(远程数据服务),是一款开源的基于内存的键值对存储系统,其主要被用作高性能缓存服务器使用,当然也可以作为消息中间件和Session共享等。Redis独特的键值对模型使之支持丰富的数据结构类型,即它的值可以是字符串、哈希、列表、集合、有序集合,而不像Memcached要求的键和值都是字符串。同时由于Redis是基于内存的方式,免去了磁盘I/O速度的影响,因此其读写性能极高。

1、在Ubuntu中打开终端,输入下列命令,下载Redis安装包:

wget http://download.redis.io/releases/redis-4.0.9.tar.gz

2、对安装包进行解压,并将其移动放到usr/local⽬录下 命令如下:解压:

tar xzf redis-4.0.9.tar.gz
移动到
sudo mv ./redis-4.0.9 /usr/local/redis/

3、进入redis⽬录,编译生成 命令:

cd /usr/local/redis/
sudo make

测试
sudo make test

4、安装,将redis的命令安装到/usr/local/bin/⽬录

sudo make install

5、安装完成后,我们进入目录/usr/local/bin中查看

cd /usr/local/bin
ls -all

6、配置⽂件,移动到/etc/⽬录下 配置⽂件⽬录为/usr/local/redis/redis.conf

sudo cp /usr/local/redis/redis.conf /etc/redis/

7、Redis的配置信息在/etc/redis/redis.conf下

sudo vi /etc/redis/redis.conf

绑定ip:如果需要远程访问,可将此⾏注释,或绑定⼀个真实ip
bind 127.0.0.1

端⼝,默认为6379
port 6379

是否以守护进程运⾏
如果以守护进程运⾏,则不会在命令⾏阻塞,类似于服务
如果以⾮守护进程运⾏,则当前终端被阻塞
设置为yes表示守护进程,设置为no表示⾮守护进程
推荐设置为yes
daemonize yes

数据⽂件
dbfilename dump.rdb

数据⽂件存储路径
dir /var/lib/redis

⽇志⽂件
logfile "/var/log/redis/redis-server.log"

数据库,默认有16个
database 16

主从复制,类似于双机备份。
slaveof

连接服务端

./redis-cli -h 127.0.0.1 -p 6379

2、hiredis库的安装

官网:https://redislabs.com/lp/hiredis/ 发行版本:https://github.com/redis/hiredis/releases 目前最新的版本:https://codeload.github.com/redis/hiredis/tar.gz/v0.14.0

1、解压:tar -zxvf hiredis-0.14.0.tar.gz 2、编译:make 3、安装:make install

也可以直接将文件编译到自己的工程代码。

3、同步API接口的使用

我们的项目中使用的hireds接口都是同步的API,所谓同步意思就是使用阻塞的方式向redis server下发消息。接口的主要部分为下面三个部分,下面分别介绍。

/**连接数据库*/
redisContext *redisConnect(const char *ip, int port);
/**发送命令请求*/
void *redisCommand(redisContext *c, const char *format, ...);
void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
void redisAppendCommand(redisContext *c, const char *format, ...);
void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
/*释放资源*/
void freeReplyObject(void *reply);
void redisFree(redisContext *c);

3.1、连接redis数据库redisConnect

redisContext *redisConnect(const char *ip, int port);

参数说明 ● port:为redis数据监听的端口号,redis默认监听的端口号为6379 ● ip:为redis数据库的IP地址,可以是远程的,也可以是本地的127.0.0.1 返回值 返回值是一个指向redisContext对象,可以不用了解这个对象的具体组成部分,只需要知道怎么使用就可以了。下面是其定义。

typedef struct redisContext {
int err;/* Error flags, 0 when there is no error */
char errstr[128];/* String representation of error when applicable */
int fd;
int flags;
char*obuf;/* Write buffer */
    redisReader *reader;/* Protocol reader */
enum redisConnectionType connection_type;
struct timeval *timeout;
struct {
char*host;
char*source_addr;
int port;
} tcp;
struct {
char*path;
} unix_sock;
} redisContext;

3.2、发送需要执行的命令redisCommand

void *redisCommand(redisContext *c, const char *format, ...);

参数说明 这个函数是一个带有不定参数的。可以按着format格式给出对应的参数,这就和printf函数类似。c 是一个reidsConnect函数返回的一个对象。返回值 返回值是一个void类型的指针,实际为指向一个redisReply类型的指针。

/* This is the reply object returned by redisCommand() */
typedefstruct redisReply {
/*命令执行结果的返回类型*/
int type;/* REDIS_REPLY_* */
/*存储执行结果返回为整数*/
longlong integer;/* The integer when type is REDIS_REPLY_INTEGER */
/*字符串值的长度*/
size_t len;/* Length of string */
/*存储命令执行结果返回是字符串*/
char*str;/* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING */
/*返回结果是数组的大小*/
size_t elements;/* number of elements, for REDIS_REPLY_ARRAY */
/*存储执行结果返回是数组*/
struct redisReply **element;/* elements vector for REDIS_REPLY_ARRAY */
} redisReply;

返回结果的类型reply->type,reply 为redisReply*类型。
● REDIS_REPLY_STRING ==1:返回值是字符串,字符串储存在redis->str当中,字符串长度为redis->len。
● REDIS_REPLY_ARRAY ==2:返回值是数组,数组大小存在redis->elements里面,数组值存储在redis->element[i]里面。数组里面存储的是指向redisReply的指针,数组里面的返回值可以通过redis->element[i]->str来访问,数组的结果里全是type==REDIS_REPLY_STRING的redisReply对象指针。
● REDIS_REPLY_INTEGER ==3:返回值为整数longlong。
● REDIS_REPLY_NIL==4:返回值为空表示执行结果为空。
● REDIS_REPLY_STATUS ==5:返回命令执行的状态,比如set foo bar 返回的状态为OK,存储在str当中 reply->str =="OK"。
● REDIS_REPLY_ERROR ==6:命令执行错误,错误信息存放在 reply->str当中。

3.3、redisCommandArgv函数

void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);

参数说明 argvlen这个数组存储了命令参数中,每一个参数的长度,包含命令本身,比如 set foo bar 则argvlen ={3,3,3},如果argvlen为空,那么这个函数内部会自动调用strlen函数对每个参数进行求长度。argv 存放每个命令参数的指针,argv={"set","foo","bar"} argc 存放命令参数的个数上面的例子中argc=3 c 为redisContext对象。为每一个参数指定长度,可以是二进制安全的函数。函数会按着长度来决定字符串的终止,而不是'\0'.

char hkey[]="123456";
char hset[]="hset";
char key[]="testkey";
char hvalue[]="3210";
int argc =4;
char*argv[]={hset,key,hkey,hvalue};
size_t argvlen[]={4,6,4,3};
redisCommandArgv(context,argc,argv,argvlen);

hgetall testkey 会得到321并不会得到和hvalue一样的值"3210",因为在hset命令中指定了长度,只会读取前面的三个字符。

3.4、redisAppendCommand*函数支持管道命令

void redisAppendCommand(redisContext *c, const char *format, ...);
void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
int redisGetReply(redisContext *context,redisReply** reply);

参数说明:redisAppendCommand函数和redisCommand函数参数一致,format可以指定特定参数的类型。c 为redisContext对象 redisAppendCommandArgv函数和redisCommandArgv函数类似,参数含义也相同。redisGetReply函数用来获得执行的结果的一条返回,并存储在reply所指的对象当中。成功返回REDIS_OK,否则返回REIDS_ERR。多条命令的一次性返回结果都存放在redisContext里面。所不同的是,这个两个命令的结果。这两个函数是把多个命令存放在缓冲区内,然后一起发送给redis服务器,一次执行。可以通过redisGetReply函数从 redisContext中取出返回的结果。使用例子:

redisReply *reply;
/*添加命令set */
redisAppendCommand(context,"SET foo bar");
/*添加命令get */
redisAppendCommand(context,"GET foo");
/*获取set命令结果*/
redisGetReply(context,&reply); // reply for SET
freeReplyObject(reply);
/*获取get命令结果*/
redisGetReply(context,&reply); // reply for GET
freeReplyObject(reply);

3.5、释放资源

void freeReplyObject(void *reply);
void redisFree(redisContext *c);

参数说明 freeReplyObject函数中reply 实际为指向redisReply结构体的指针,可能是redisCommand的返回值,后续可以看到以也能是管道命令执行结果的返回值。redisFree函数中c实际为指向redisContext对象,这个函数会清理连接资源并释放连接。

3.6、同步连接代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "hiredis/hiredis.h"

intmain(int argc,char *argv[])
{
unsignedint j,isunix =0;
    redisContext *c;
    redisReply *reply;
constchar*hostname ="127.0.0.1";
int port =6379;

struct timeval timeout ={1,500000};// 1.5 seconds
    c = redisConnectWithTimeout(hostname,port,timeout);
if(c ==NULL|| c->err){
if(c){
printf("Connection error:%s\n",c->errstr);
            redisFree(c);
}else{
printf("Connection error:can't allocate redis context\n");
}
exit(1);
}

int num =1000;
for(int i=0;i<num;i++){
// INCR counter 是 Redis 的一个命令,它用于将名为 "counter" 的键的值递增1。如果该键不存在,它将被创建并初始化为0
        reply = redisCommand(c,"INCR counter");
printf("INCR counter:%lld\n",reply->integer);
        freeReplyObject(reply);
}
    redisFree(c);
return0;
}

如果执行的时候报这个错误

Redis的C客户端(hiredis库)使用_数组

在这里插入图片描述

解决办法:确保你的/etc/ld.so.conf里面有 /usr/local/lib 这一行 没有的话vim编辑在尾行加上 然后 sudo ldconfig

2、 ldconfig介绍 ldconfig是一个动态链接库管理命令,其目的为了让动态链接库为系统所共享。当进程需要链接相应的库文件时候,会默认搜寻/lilb和/usr/lib,以及配置文件/etc/ld.so.conf内所列的目录下的库文件。若找不到的话,就会出现如上图的错误。ldconfig通常在系统启动时运行,而当用户安装了一个新的动态链接库时,就需要手工运行这个命令。ldconfig需要注意的地方:

往/lib和/usr/lib里面加东西,是不用修改/etc/ld.so.conf文件的,但是添加完后需要调用下ldconfig,不然添加的library会找不到。

如果添加的library不在/lib和/usr/lib里面的话,就一定要修改/etc/ld.so.conf文件,往该文件追加library所在的路径,然后也需要重新调用下ldconfig命令。比如在安装MySQL的时候,其库文件/usr/local/mysql/lib,就需要追加到/etc/ld.so.conf文件中。命令如下:

echo "/usr/local/mysql/lib" >> /etc/ld.so.conf

ldconfig -v | grep mysql

最后注意:
如果添加的library不在/lib或/usr/lib下,但是却没有权限操作写/etc/ld.so.conf文件的话,这时就需要往export里写一个全局变量LD_LIBRARY_PATH,就可以了。

3.7、异步连接

#include "hiredis/hiredis.h"
#include "hiredis/async.h"
#include "reactor.h"
#include "adapter.h"
#include <time.h>

staticreactor_t*R;
staticint cnt, before, num;


intcurrent_tick(){
int t =0;
struct timespec ti;
    clock_gettime(CLOCK_MONOTONIC,&ti);
    t =(int)ti.tv_sec *1000;
    t += ti.tv_nsec /1000000;
return t;
}

voidgetCallback(redisAsyncContext *c, void *r, void *privdata){
    redisReply *reply = r;
if(reply ==NULL)return;
printf("argv[%s]: %lld\n",(char*)privdata, reply->integer);

/* Disconnect after receiving the reply to GET */
    cnt++;
if(cnt == num){
int used = current_tick()-before;
printf("after %d exec redis command, used %d ms\n", num, used);
        redisAsyncDisconnect(c);
}
}


voidconnectCallback(const redisAsyncContext *c, int status){
if(status != REDIS_OK){
printf("Error: %s\n", c->errstr);
        stop_eventloop(R);
return;
}

printf("Connected...\n");
}

voiddisconnectCallback(const redisAsyncContext *c, int status){
if(status != REDIS_OK){
printf("Error: %s\n", c->errstr);
        stop_eventloop(R);
return;
}

printf("Disconnected...\n");
    stop_eventloop(R);
}

intmain(int argc, char **argv){
    redisAsyncContext *c = redisAsyncConnect("127.0.0.1",6379);
if(c->err){
/* Let *c leak for now... */
printf("Error: %s\n", c->errstr);
return1;
}
    R = create_reactor();

    redisAttach(R, c);

    redisAsyncSetConnectCallback(c, connectCallback);
    redisAsyncSetDisconnectCallback(c, disconnectCallback);

    before = current_tick();
    num =(argc >1)? atoi(argv[1]):1000;

for(int i =0; i < num; i++){
        redisAsyncCommand(c, getCallback,"count","INCR counter");
}

    eventloop(R);

    release_reactor(R);
return0;
}

// gcc main.c -o main -L./hiredis -lhiredis

4、redis连接池实现

1、Thread.h头文件

#ifndef __THREAD_H__
#define __THREAD_H__
#include <stdint.h>
#include <pthread.h>

class CThreadNotify
{
public:
CThreadNotify()
{
        pthread_mutexattr_init(&m_mutexattr);
        pthread_mutexattr_settype(&m_mutexattr, PTHREAD_MUTEX_RECURSIVE);
        pthread_mutex_init(&m_mutex,&m_mutexattr);

        pthread_cond_init(&m_cond,NULL);
}
~CThreadNotify()
{
        pthread_mutexattr_destroy(&m_mutexattr);
        pthread_mutex_destroy(&m_mutex);
        pthread_cond_destroy(&m_cond);
}
voidLock()
{
        pthread_mutex_lock(&m_mutex);
}
voidUnlock()
{
        pthread_mutex_unlock(&m_mutex);
}
voidWait()
{
        pthread_cond_wait(&m_cond,&m_mutex);
}
// 返回0则正常,其他值为异常
intWaitTime(int ms)
{
//获取时间
struct timespec outtime;
        clock_gettime(CLOCK_MONOTONIC,&outtime);
//ms为毫秒,换算成秒
        outtime.tv_sec += ms /1000;

//在outtime的基础上,增加ms毫秒
//outtime.tv_nsec为纳秒,1微秒=1000纳秒
//tv_nsec此值再加上剩余的毫秒数 ms%1000,有可能超过1秒。需要特殊处理
uint64_t us = outtime.tv_nsec /1000+1000*(ms %1000);//微秒
//us的值有可能超过1秒,
        outtime.tv_sec += us /1000000;

        us = us %1000000;
        outtime.tv_nsec = us *1000;//换算成纳秒
return pthread_cond_timedwait(&m_cond,&m_mutex,&outtime);
}
voidSignal()
{
        pthread_cond_signal(&m_cond);
}

private:
pthread_mutex_t m_mutex;
pthread_mutexattr_t m_mutexattr;

pthread_cond_t m_cond;
};

#endif

2、头文件CachePool.h

/*
 * @Author: your name
 * @Date: 2019-12-07 10:54:57
 * @LastEditTime : 2020-01-10 16:35:13
 * @LastEditors  : Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: \src\cache_pool\CachePool.h
 */
#ifndef CACHEPOOL_H_
#define CACHEPOOL_H_

#include <iostream>
#include <vector>
#include <map>
#include <list>

#include "Thread.h"

#include "hiredis.h"


usingstd::string;
usingstd::list;
usingstd::map;
usingstd::vector;

class CachePool;

class CacheConn {
public:
CacheConn(constchar* server_ip,int server_port,int db_index,constchar* password,
constchar*pool_name ="");
CacheConn(CachePool* pCachePool);
virtual~CacheConn();

intInit();
voidDeInit();
constchar*GetPoolName();
// 通用操作
// 判断一个key是否存在
boolisExists(string &key);
// 删除某个key
longdel(string &key);

// ------------------- 字符串相关 -------------------
stringget(string key);
stringset(string key, string& value);
stringsetex(string key, int timeout, string value);

// string mset(string key, map);
//批量获取
boolmget(const vector<string>& keys, map<string, string>& ret_value);
//原子加减1
longincr(string key);
longdecr(string key);


// ---------------- 哈希相关 ------------------------
longhdel(string key, string field);
stringhget(string key, string field);
boolhgetAll(string key, map<string, string>& ret_value);
longhset(string key, string field, string value);

longhincrBy(string key, string field, long value);
longincrBy(string key, long value);
stringhmset(string key, map<string, string>& hash);
boolhmget(string key, list<string>& fields, list<string>& ret_value);



// ------------ 链表相关 ------------
longlpush(string key, string value);
longrpush(string key, string value);
longllen(string key);
boollrange(string key, long start, long end, list<string>& ret_value);


boolflushdb();

private:
CachePool*   m_pCachePool;
    redisContext*  m_pContext;
uint64_t  m_last_connect_time;
uint16_t   m_server_port;
string    m_server_ip;
string          m_password;
uint16_t        m_db_index;
string    m_pool_name;
};


class CachePool {
public:
// db_index和mysql不同的地方 
CachePool(constchar* pool_name,constchar* server_ip,int server_port,int db_index,
constchar*password,int max_conn_cnt);
virtual~CachePool();

intInit();
// 获取空闲的连接资源
CacheConn*GetCacheConn();
// Pool回收连接资源
voidRelCacheConn(CacheConn* pCacheConn);

constchar*GetPoolName(){return m_pool_name.c_str();}
constchar*GetServerIP(){return m_server_ip.c_str();}
constchar*GetPassword(){return m_password.c_str();}
intGetServerPort(){return m_server_port;}
intGetDBIndex(){return m_db_index;}
private:
string   m_pool_name;
string  m_server_ip;
string   m_password;
int   m_server_port;
int   m_db_index;// mysql 数据库名字, redis db index

int   m_cur_conn_cnt;
int   m_max_conn_cnt;
list<CacheConn*> m_free_list;
CThreadNotify  m_free_notify;
};



#endif /* CACHEPOOL_H_ */

3、CachePool.cpp文件

#include "CachePool.h"

#include <stdlib.h>
#include <string.h>
#include "Thread.h"

#define log_error printf
#define log_info printf

#define MIN_CACHE_CONN_CNT 2
#define MAX_CACHE_CONN_FAIL_NUM 10

CacheConn::CacheConn(constchar*server_ip,int server_port,int db_index,constchar*password,
constchar*pool_name)
{
    m_server_ip = server_ip;
    m_server_port = server_port;

    m_db_index = db_index;
    m_password = password;
    m_pool_name = pool_name;
    m_pContext =NULL;
    m_last_connect_time =0;
}

CacheConn::CacheConn(CachePool*pCachePool)
{
    m_pCachePool = pCachePool;
if(pCachePool)
{
        m_server_ip = pCachePool->GetServerIP();
        m_server_port = pCachePool->GetServerPort();
        m_db_index = pCachePool->GetDBIndex();
        m_password = pCachePool->GetPassword();
        m_pool_name = pCachePool->GetPoolName();
}
else
{
        log_error("pCachePool is NULL\n");
}

    m_pContext =NULL;
    m_last_connect_time =0;
}

CacheConn::~CacheConn()
{
if(m_pContext)
{
        redisFree(m_pContext);
        m_pContext =NULL;
}
}

/*
 * redis初始化连接和重连操作,类似mysql_ping()
 */
intCacheConn::Init()
{
if(m_pContext)// 非空,连接是正常的
{
return0;
}

// 1s 尝试重连一次
uint64_t cur_time =(uint64_t)time(NULL);
if(cur_time < m_last_connect_time +1)// 重连尝试 间隔1秒 
{
printf("cur_time:%lu, m_last_connect_time:%lu\n", cur_time, m_last_connect_time);
return1;
}
// printf("m_last_connect_time = cur_time\n");
    m_last_connect_time = cur_time;

// 1000ms超时
struct timeval timeout ={0,1000000};
// 建立连接后使用 redisContext 来保存连接状态。
// redisContext 在每次操作后会修改其中的 err 和  errstr 字段来表示发生的错误码(大于0)和对应的描述。
    m_pContext = redisConnectWithTimeout(m_server_ip.c_str(), m_server_port, timeout);

if(!m_pContext || m_pContext->err)
{
if(m_pContext)
{
            log_error("redisConnect failed: %s\n", m_pContext->errstr);
            redisFree(m_pContext);
            m_pContext =NULL;
}
else
{
            log_error("redisConnect failed\n");
}

return1;
}

    redisReply *reply;
// 验证
if(!m_password.empty())
{
        reply =(redisReply *)redisCommand(m_pContext,"AUTH %s", m_password.c_str());

if(!reply || reply->type == REDIS_REPLY_ERROR)
{
            log_error("Authentication failure:%p\n", reply);
if(reply)
                freeReplyObject(reply);
return-1;
}
else
{
// log_info("Authentication success\n");
}

        freeReplyObject(reply);
}

    reply =(redisReply *)redisCommand(m_pContext,"SELECT %d",0);

if(reply &&(reply->type == REDIS_REPLY_STATUS)&&(strncmp(reply->str,"OK",2)==0))
{
        freeReplyObject(reply);
return0;
}
else
{
if(reply)
            log_error("select cache db failed:%s\n", reply->str);
return2;
}
}

voidCacheConn::DeInit()
{
if(m_pContext)
{
        redisFree(m_pContext);
        m_pContext =NULL;
}
}

constchar*CacheConn::GetPoolName()
{
return m_pool_name.c_str();
}

stringCacheConn::get(string key)
{
string value;

if(Init())
{
return value;
}

    redisReply *reply =(redisReply *)redisCommand(m_pContext,"GET %s", key.c_str());
if(!reply)
{
        log_error("redisCommand failed:%s\n", m_pContext->errstr);
        redisFree(m_pContext);
        m_pContext =NULL;
return value;
}

if(reply->type == REDIS_REPLY_STRING)
{
        value.append(reply->str, reply->len);
}

    freeReplyObject(reply);
return value;
}

stringCacheConn::set(string key, string &value)
{
string ret_value;

if(Init())
{
return ret_value;
}
// 返回的结果存放在redisReply
    redisReply *reply =(redisReply *)redisCommand(m_pContext,"SET %s %s", key.c_str(), value.c_str());
if(!reply)
{
        log_error("redisCommand failed:%s\n", m_pContext->errstr);
        redisFree(m_pContext);
        m_pContext =NULL;
return ret_value;
}

    ret_value.append(reply->str, reply->len);
    freeReplyObject(reply);// 释放资源
return ret_value;
}

stringCacheConn::setex(string key, int timeout, string value)
{
string ret_value;

if(Init())
{
return ret_value;
}

    redisReply *reply =(redisReply *)redisCommand(m_pContext,"SETEX %s %d %s", key.c_str(), timeout, value.c_str());
if(!reply)
{
        log_error("redisCommand failed:%s\n", m_pContext->errstr);
        redisFree(m_pContext);
        m_pContext =NULL;
return ret_value;
}

    ret_value.append(reply->str, reply->len);
    freeReplyObject(reply);
return ret_value;
}

boolCacheConn::mget(const vector<string> &keys, map<string, string> &ret_value)
{
if(Init())
{
returnfalse;
}
if(keys.empty())
{
returnfalse;
}

string strKey;
bool bFirst =true;
for(vector<string>::const_iterator it = keys.begin(); it != keys.end();++it)
{
if(bFirst)
{
            bFirst =false;
            strKey =*it;
}
else
{
            strKey +=" "+*it;
}
}

if(strKey.empty())
{
returnfalse;
}
    strKey ="MGET "+ strKey;
    redisReply *reply =(redisReply *)redisCommand(m_pContext, strKey.c_str());
if(!reply)
{
        log_info("redisCommand failed:%s\n", m_pContext->errstr);
        redisFree(m_pContext);
        m_pContext =NULL;
returnfalse;
}
if(reply->type == REDIS_REPLY_ARRAY)
{
for(size_t i =0; i < reply->elements;++i)
{
            redisReply *child_reply = reply->element[i];
if(child_reply->type == REDIS_REPLY_STRING)
{
                ret_value[keys[i]]= child_reply->str;
}
}
}
    freeReplyObject(reply);
returntrue;
}

boolCacheConn::isExists(string &key)
{
if(Init())
{
returnfalse;
}

    redisReply *reply =(redisReply *)redisCommand(m_pContext,"EXISTS %s", key.c_str());
if(!reply)
{
        log_error("redisCommand failed:%s\n", m_pContext->errstr);
        redisFree(m_pContext);
        m_pContext =NULL;
returnfalse;
}
long ret_value = reply->integer;
    freeReplyObject(reply);
if(0== ret_value)
{
returnfalse;
}
else
{
returntrue;
}
}

longCacheConn::del(string &key)
{
if(Init())
{
return0;
}

    redisReply *reply =(redisReply *)redisCommand(m_pContext,"DEL %s", key.c_str());
if(!reply)
{
        log_error("redisCommand failed:%s\n", m_pContext->errstr);
        redisFree(m_pContext);
        m_pContext =NULL;
return0;
}

long ret_value = reply->integer;
    freeReplyObject(reply);
return ret_value;
}

longCacheConn::hdel(string key, string field)
{
if(Init())
{
return0;
}

    redisReply *reply =(redisReply *)redisCommand(m_pContext,"HDEL %s %s", key.c_str(), field.c_str());
if(!reply)
{
        log_error("redisCommand failed:%s\n", m_pContext->errstr);
        redisFree(m_pContext);
        m_pContext =NULL;
return0;
}

long ret_value = reply->integer;
    freeReplyObject(reply);
return ret_value;
}

stringCacheConn::hget(string key, string field)
{
string ret_value;
if(Init())
{
return ret_value;
}

    redisReply *reply =(redisReply *)redisCommand(m_pContext,"HGET %s %s", key.c_str(), field.c_str());
if(!reply)
{
        log_error("redisCommand failed:%s\n", m_pContext->errstr);
        redisFree(m_pContext);
        m_pContext =NULL;
return ret_value;
}

if(reply->type == REDIS_REPLY_STRING)
{
        ret_value.append(reply->str, reply->len);
}

    freeReplyObject(reply);
return ret_value;
}

boolCacheConn::hgetAll(string key, map<string, string> &ret_value)
{
if(Init())
{
returnfalse;
}

    redisReply *reply =(redisReply *)redisCommand(m_pContext,"HGETALL %s", key.c_str());
if(!reply)
{
        log_error("redisCommand failed:%s\n", m_pContext->errstr);
        redisFree(m_pContext);
        m_pContext =NULL;
returnfalse;
}

if((reply->type == REDIS_REPLY_ARRAY)&&(reply->elements %2==0))
{
for(size_t i =0; i < reply->elements; i +=2)
{
            redisReply *field_reply = reply->element[i];
            redisReply *value_reply = reply->element[i +1];

stringfield(field_reply->str, field_reply->len);
stringvalue(value_reply->str, value_reply->len);
            ret_value.insert(make_pair(field, value));
}
}

    freeReplyObject(reply);
returntrue;
}

longCacheConn::hset(string key, string field, string value)
{
if(Init())
{
return-1;
}

    redisReply *reply =(redisReply *)redisCommand(m_pContext,"HSET %s %s %s", key.c_str(), field.c_str(), value.c_str());
if(!reply)
{
        log_error("redisCommand failed:%s\n", m_pContext->errstr);
        redisFree(m_pContext);
        m_pContext =NULL;
return-1;
}

long ret_value = reply->integer;
    freeReplyObject(reply);
return ret_value;
}

longCacheConn::hincrBy(string key, string field, long value)
{
if(Init())
{
return-1;
}

    redisReply *reply =(redisReply *)redisCommand(m_pContext,"HINCRBY %s %s %ld", key.c_str(), field.c_str(), value);
if(!reply)
{
        log_error("redisCommand failed:%s\n", m_pContext->errstr);
        redisFree(m_pContext);
        m_pContext =NULL;
return-1;
}

long ret_value = reply->integer;
    freeReplyObject(reply);
return ret_value;
}

longCacheConn::incrBy(string key, long value)
{
if(Init())
{
return-1;
}

    redisReply *reply =(redisReply *)redisCommand(m_pContext,"INCRBY %s %ld", key.c_str(), value);
if(!reply)
{
        log_error("redis Command failed:%s\n", m_pContext->errstr);
        redisFree(m_pContext);
        m_pContext =NULL;
return-1;
}
long ret_value = reply->integer;
    freeReplyObject(reply);
return ret_value;
}

stringCacheConn::hmset(string key, map<string, string> &hash)
{
string ret_value;

if(Init())
{
return ret_value;
}

int argc = hash.size()*2+2;
constchar**argv =newconstchar*[argc];
if(!argv)
{
return ret_value;
}

    argv[0]="HMSET";
    argv[1]= key.c_str();
int i =2;
for(map<string,string>::iterator it = hash.begin(); it != hash.end(); it++)
{
        argv[i++]= it->first.c_str();
        argv[i++]= it->second.c_str();
}

    redisReply *reply =(redisReply *)redisCommandArgv(m_pContext, argc, argv,NULL);
if(!reply)
{
        log_error("redisCommand failed:%s\n", m_pContext->errstr);
delete[] argv;

        redisFree(m_pContext);
        m_pContext =NULL;
return ret_value;
}

    ret_value.append(reply->str, reply->len);

delete[] argv;
    freeReplyObject(reply);
return ret_value;
}

boolCacheConn::hmget(string key, list<string> &fields, list<string> &ret_value)
{
if(Init())
{
returnfalse;
}

int argc = fields.size()+2;
constchar**argv =newconstchar*[argc];
if(!argv)
{
returnfalse;
}

    argv[0]="HMGET";
    argv[1]= key.c_str();
int i =2;
for(list<string>::iterator it = fields.begin(); it != fields.end(); it++)
{
        argv[i++]= it->c_str();
}

    redisReply *reply =(redisReply *)redisCommandArgv(m_pContext, argc,(constchar**)argv,NULL);
if(!reply)
{
        log_error("redisCommand failed:%s\n", m_pContext->errstr);
delete[] argv;

        redisFree(m_pContext);
        m_pContext =NULL;

returnfalse;
}

if(reply->type == REDIS_REPLY_ARRAY)
{
for(size_t i =0; i < reply->elements; i++)
{
            redisReply *value_reply = reply->element[i];
stringvalue(value_reply->str, value_reply->len);
            ret_value.push_back(value);
}
}

delete[] argv;
    freeReplyObject(reply);
returntrue;
}

longCacheConn::incr(string key)
{
if(Init())
{
return-1;
}

    redisReply *reply =(redisReply *)redisCommand(m_pContext,"INCR %s", key.c_str());
if(!reply)
{
        log_error("redis Command failed:%s\n", m_pContext->errstr);
        redisFree(m_pContext);
        m_pContext =NULL;
return-1;
}
long ret_value = reply->integer;
    freeReplyObject(reply);
return ret_value;
}

longCacheConn::decr(string key)
{
if(Init())
{
return-1;
}

    redisReply *reply =(redisReply *)redisCommand(m_pContext,"DECR %s", key.c_str());
if(!reply)
{
        log_error("redis Command failed:%s\n", m_pContext->errstr);
        redisFree(m_pContext);
        m_pContext =NULL;
return-1;
}
long ret_value = reply->integer;
    freeReplyObject(reply);
return ret_value;
}

longCacheConn::lpush(string key, string value)
{
if(Init())
{
return-1;
}

    redisReply *reply =(redisReply *)redisCommand(m_pContext,"LPUSH %s %s", key.c_str(), value.c_str());
if(!reply)
{
        log_error("redisCommand failed:%s\n", m_pContext->errstr);
        redisFree(m_pContext);
        m_pContext =NULL;
return-1;
}

long ret_value = reply->integer;
    freeReplyObject(reply);
return ret_value;
}

longCacheConn::rpush(string key, string value)
{
if(Init())
{
return-1;
}

    redisReply *reply =(redisReply *)redisCommand(m_pContext,"RPUSH %s %s", key.c_str(), value.c_str());
if(!reply)
{
        log_error("redisCommand failed:%s\n", m_pContext->errstr);
        redisFree(m_pContext);
        m_pContext =NULL;
return-1;
}

long ret_value = reply->integer;
    freeReplyObject(reply);
return ret_value;
}

longCacheConn::llen(string key)
{
if(Init())
{
return-1;
}

    redisReply *reply =(redisReply *)redisCommand(m_pContext,"LLEN %s", key.c_str());
if(!reply)
{
        log_error("redisCommand failed:%s\n", m_pContext->errstr);
        redisFree(m_pContext);
        m_pContext =NULL;
return-1;
}

long ret_value = reply->integer;
    freeReplyObject(reply);
return ret_value;
}

boolCacheConn::lrange(string key, long start, long end, list<string> &ret_value)
{
if(Init())
{
returnfalse;
}

    redisReply *reply =(redisReply *)redisCommand(m_pContext,"LRANGE %s %d %d", key.c_str(), start, end);
if(!reply)
{
        log_error("redisCommand failed:%s\n", m_pContext->errstr);
        redisFree(m_pContext);
        m_pContext =NULL;
returnfalse;
}

if(reply->type == REDIS_REPLY_ARRAY)
{
for(size_t i =0; i < reply->elements; i++)
{
            redisReply *value_reply = reply->element[i];
stringvalue(value_reply->str, value_reply->len);
            ret_value.push_back(value);
}
}

    freeReplyObject(reply);
returntrue;
}

boolCacheConn::flushdb()
{
bool ret =false;
if(Init())
{
returnfalse;
}

    redisReply *reply =(redisReply *)redisCommand(m_pContext,"FLUSHDB");
if(!reply)
{
        log_error("redisCommand failed:%s\n", m_pContext->errstr);
        redisFree(m_pContext);
        m_pContext =NULL;
returnfalse;
}

if(reply->type == REDIS_REPLY_STRING &&strncmp(reply->str,"OK",2)==0)
{
        ret =true;
}

    freeReplyObject(reply);

return ret;
}
///////////////
CachePool::CachePool(constchar*pool_name,constchar*server_ip,int server_port,int db_index,
constchar*password,int max_conn_cnt)
{
    m_pool_name = pool_name;
    m_server_ip = server_ip;
    m_server_port = server_port;
    m_db_index = db_index;
    m_password = password;
    m_max_conn_cnt = max_conn_cnt;
    m_cur_conn_cnt = MIN_CACHE_CONN_CNT;
}

CachePool::~CachePool()
{
    m_free_notify.Lock();
for(list<CacheConn*>::iterator it = m_free_list.begin(); it != m_free_list.end(); it++)
{
CacheConn*pConn =*it;
delete pConn;
}

    m_free_list.clear();
    m_cur_conn_cnt =0;
    m_free_notify.Unlock();
}

intCachePool::Init()
{
for(int i =0; i < m_cur_conn_cnt; i++)
{
CacheConn*pConn =newCacheConn(m_server_ip.c_str(), m_server_port,
                                         m_db_index, m_password.c_str(), m_pool_name.c_str());
if(pConn->Init())
{
delete pConn;
return1;
}

        m_free_list.push_back(pConn);
}

    log_info("cache pool: %s, list size: %lu\n", m_pool_name.c_str(), m_free_list.size());
return0;
}

CacheConn*CachePool::GetCacheConn()
{
    m_free_notify.Lock();

while(m_free_list.empty())
{
if(m_cur_conn_cnt >= m_max_conn_cnt)
{
            m_free_notify.Wait();
}
else
{
CacheConn*p_cache_conn =newCacheConn(m_server_ip.c_str(), m_server_port,
                                                    m_db_index, m_password.c_str(), m_pool_name.c_str());
int ret = p_cache_conn->Init();
if(ret)
{
                log_error("Init CacheConn failed\n");
delete p_cache_conn;
                m_free_notify.Unlock();
returnNULL;
}
else
{
                m_free_list.push_back(p_cache_conn);
                m_cur_conn_cnt++;
                log_info("new cache connection: %s, conn_cnt: %d\n", m_pool_name.c_str(), m_cur_conn_cnt);
}
}
}

CacheConn*pConn = m_free_list.front();
    m_free_list.pop_front();

    m_free_notify.Unlock();

return pConn;
}

voidCachePool::RelCacheConn(CacheConn *p_cache_conn)
{
    m_free_notify.Lock();

list<CacheConn*>::iterator it = m_free_list.begin();
for(; it != m_free_list.end(); it++)
{
if(*it == p_cache_conn)
{
break;
}
}

if(it == m_free_list.end())
{
        m_free_list.push_back(p_cache_conn);
}

    m_free_notify.Signal();
    m_free_notify.Unlock();
}