redis 连接池 hiredis
Posted HenryLiuY
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了redis 连接池 hiredis相关的知识,希望对你有一定的参考价值。
对Hiredis进行了简单封装
1、API进行统一,对外只提供一个接口;
2、屏蔽上层应用对连接的细节处理;
3、底层采用队列的方式保持连接池,保存连接会话;
4、重连时采用时间戳进行控制,每隔一定时间(3s)重连一次,防止频繁重试造成的不必要浪费。
先看一下Hiredis的常用数据结构与API:
//hiredis/hiredis.h
/* Context for a connection to Redis */
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 */
} redisContext;
/* Context for a connection to Redis */
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 */
} redisContext;
/* This is the reply object returned by redisCommand() */
#define REDIS_REPLY_STRING 1
#define REDIS_REPLY_ARRAY 2
#define REDIS_REPLY_INTEGER 3
#define REDIS_REPLY_NIL 4
#define REDIS_REPLY_STATUS 5
#define REDIS_REPLY_ERROR 6
typedef struct redisReply {
int type; /* REDIS_REPLY_* */
long long integer; /* The integer when type is REDIS_REPLY_INTEGER */
int 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;
#define REDIS_REPLY_STRING 1
#define REDIS_REPLY_ARRAY 2
#define REDIS_REPLY_INTEGER 3
#define REDIS_REPLY_NIL 4
#define REDIS_REPLY_STATUS 5
#define REDIS_REPLY_ERROR 6
typedef struct redisReply {
int type; /* REDIS_REPLY_* */
long long integer; /* The integer when type is REDIS_REPLY_INTEGER */
int 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;
redisContext *redisConnectWithTimeout(const char *ip, int port, struct timeval tv);
void redisFree(redisContext *c);
void redisFree(redisContext *c);
封装后的代码:
class KGRedisClient
{
public:
KGRedisClient(string ip, int port, int timeout = 2000);
virtual ~KGRedisClient();
{
public:
KGRedisClient(string ip, int port, int timeout = 2000);
virtual ~KGRedisClient();
bool ExecuteCmd(const char *cmd, size_t len, string &response);
redisReply* ExecuteCmd(const char *cmd, size_t len);
redisReply* ExecuteCmd(const char *cmd, size_t len);
private:
int m_timeout;
int m_serverPort;
string m_setverIp;
CCriticalSection m_lock;
std::queue<redisContext *> m_clients;
int m_timeout;
int m_serverPort;
string m_setverIp;
CCriticalSection m_lock;
std::queue<redisContext *> m_clients;
time_t m_beginInvalidTime;
static const int m_maxReconnectInterval = 3;
static const int m_maxReconnectInterval = 3;
redisContext* CreateContext();
void ReleaseContext(redisContext *ctx, bool active);
bool CheckStatus(redisContext *ctx);
};
void ReleaseContext(redisContext *ctx, bool active);
bool CheckStatus(redisContext *ctx);
};
KGRedisClient::KGRedisClient(string ip, int port, int timeout)
{
m_timeout = timeout;
m_serverPort = port;
m_setverIp = ip;
{
m_timeout = timeout;
m_serverPort = port;
m_setverIp = ip;
m_beginInvalidTime = 0;
}
}
KGRedisClient::~KGRedisClient()
{
CAutoLock autolock(m_lock);
while(!m_clients.empty())
{
redisContext *ctx = m_clients.front();
redisFree(ctx);
m_clients.pop();
}
}
{
CAutoLock autolock(m_lock);
while(!m_clients.empty())
{
redisContext *ctx = m_clients.front();
redisFree(ctx);
m_clients.pop();
}
}
bool KGRedisClient::ExecuteCmd(const char *cmd, size_t len,string &response)
{
redisReply *reply = ExecuteCmd(cmd, len);
if(reply == NULL) return false;
{
redisReply *reply = ExecuteCmd(cmd, len);
if(reply == NULL) return false;
boost::shared_ptr<redisReply> autoFree(reply, freeReplyObject);
if(reply->type == REDIS_REPLY_INTEGER)
{
response = _IntToStrA(reply->integer);
return true;
}
else if(reply->type == REDIS_REPLY_STRING)
{
response.assign(reply->str, reply->len);
return true;
}
else if(reply->type == REDIS_REPLY_STATUS)
{
response.assign(reply->str, reply->len);
return true;
}
else if(reply->type == REDIS_REPLY_NIL)
{
response = "";
return true;
}
else if(reply->type == REDIS_REPLY_ERROR)
{
response.assign(reply->str, reply->len);
return false;
}
else if(reply->type == REDIS_REPLY_ARRAY)
{
response = "Not Support Array Result!!!";
return false;
}
else
{
response = "Undefine Reply Type";
return false;
}
}
if(reply->type == REDIS_REPLY_INTEGER)
{
response = _IntToStrA(reply->integer);
return true;
}
else if(reply->type == REDIS_REPLY_STRING)
{
response.assign(reply->str, reply->len);
return true;
}
else if(reply->type == REDIS_REPLY_STATUS)
{
response.assign(reply->str, reply->len);
return true;
}
else if(reply->type == REDIS_REPLY_NIL)
{
response = "";
return true;
}
else if(reply->type == REDIS_REPLY_ERROR)
{
response.assign(reply->str, reply->len);
return false;
}
else if(reply->type == REDIS_REPLY_ARRAY)
{
response = "Not Support Array Result!!!";
return false;
}
else
{
response = "Undefine Reply Type";
return false;
}
}
redisReply* KGRedisClient::ExecuteCmd(const char *cmd, size_t len)
{
redisContext *ctx = CreateContext();
if(ctx == NULL) return NULL;
{
redisContext *ctx = CreateContext();
if(ctx == NULL) return NULL;
redisReply *reply = (redisReply*)redisCommand(ctx, "%b", cmd, len);
ReleaseContext(ctx, reply != NULL);
return reply;
}
}
redisContext* KGRedisClient::CreateContext()
{
{
CAutoLock autolock(m_lock);
if(!m_clients.empty())
{
redisContext *ctx = m_clients.front();
m_clients.pop();
{
{
CAutoLock autolock(m_lock);
if(!m_clients.empty())
{
redisContext *ctx = m_clients.front();
m_clients.pop();
return ctx;
}
}
}
}
time_t now = time(NULL);
if(now < m_beginInvalidTime + m_maxReconnectInterval) return NULL;
if(now < m_beginInvalidTime + m_maxReconnectInterval) return NULL;
struct timeval tv;
tv.tv_sec = m_timeout / 1000;
tv.tv_usec = (m_timeout % 1000) * 1000;;
redisContext *ctx = redisConnectWithTimeout(m_setverIp.c_str(), m_serverPort, tv);
if(ctx == NULL || ctx->err != 0)
{
if(ctx != NULL) redisFree(ctx);
tv.tv_sec = m_timeout / 1000;
tv.tv_usec = (m_timeout % 1000) * 1000;;
redisContext *ctx = redisConnectWithTimeout(m_setverIp.c_str(), m_serverPort, tv);
if(ctx == NULL || ctx->err != 0)
{
if(ctx != NULL) redisFree(ctx);
m_beginInvalidTime = time(NULL);
return NULL;
}
return NULL;
}
return ctx;
}
}
void KGRedisClient::ReleaseContext(redisContext *ctx, bool active)
{
if(ctx == NULL) return;
if(!active) {redisFree(ctx); return;}
{
if(ctx == NULL) return;
if(!active) {redisFree(ctx); return;}
CAutoLock autolock(m_lock);
m_clients.push(ctx);
}
m_clients.push(ctx);
}
bool KGRedisClient::CheckStatus(redisContext *ctx)
{
redisReply *reply = (redisReply*)redisCommand(ctx, "ping");
if(reply == NULL) return false;
{
redisReply *reply = (redisReply*)redisCommand(ctx, "ping");
if(reply == NULL) return false;
boost::shared_ptr<redisReply> autoFree(reply, freeReplyObject);
if(reply->type != REDIS_REPLY_STATUS) return false;
if(strcasecmp(reply->str,"PONG") != 0) return false;
if(strcasecmp(reply->str,"PONG") != 0) return false;
return true;
}
}
成员变量:m_clients用于保存连接池。
成员变量:m_beginInvalidTime、m_maxReconnectInterval 用于控制断掉时的频繁连接。
对外API:ExecuteCmd(const char *cmd, string &response);
以上是关于redis 连接池 hiredis的主要内容,如果未能解决你的问题,请参考以下文章