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;

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

redisContext *redisConnectWithTimeout(const char *ip, int port, struct timeval tv);
void redisFree(redisContext *c);
//Issue a command to Redis, NULL if error, otherwise reply
void *redisCommand(redisContext *c, const char *format, ...);
/* Function to free the reply objects hiredis returns by default. */
void freeReplyObject(void *reply);

下面直接上封装后的代码:

class KGRedisClient
{
public:
    KGRedisClient(string ip, int port, int timeout = 2000);
    virtual ~KGRedisClient();

    bool ExecuteCmd(const char *cmd, string &response);
    redisReply* ExecuteCmd(const char *cmd);

private:
    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;

    redisContext* CreateContext();
    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_beginInvalidTime = 0;
}

KGRedisClient::~KGRedisClient()
{
    CAutoLock autolock(m_lock);
    while(!m_clients.empty())
    {
        redisContext *ctx = m_clients.front();
        redisFree(ctx);
        m_clients.pop();
    }
}

bool KGRedisClient::ExecuteCmd(const char *cmd, string &response)
{
    redisReply *reply = ExecuteCmd(cmd);
    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;
    }
}

redisReply* KGRedisClient::ExecuteCmd(const char *cmd)
{
    redisContext *ctx = CreateContext();
    if(ctx == NULL) return NULL;

    redisReply *reply = (redisReply*)redisCommand(ctx, cmd);

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

            return ctx;
        }
    }

    time_t now = time(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);

        m_beginInvalidTime = time(NULL);

        return NULL;
    }

    return ctx;
}

void KGRedisClient::ReleaseContext(redisContext *ctx, bool active)
{
    if(ctx == NULL) return;
    if(!active) {redisFree(ctx); return;}

    CAutoLock autolock(m_lock);
    m_clients.push(ctx);
}

bool KGRedisClient::CheckStatus(redisContext *ctx)
{
    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;

    return true;
}

稍加解释:

成员变量:m_clients用于保存连接池。

成员变量:m_beginInvalidTime、m_maxReconnectInterval 用于控制断掉时的频繁连接。

对外API:ExecuteCmd(const char *cmd, string &response);

时间: 2024-10-10 14:13:28

Redis客户端连接方式Hiredis简单封装使用,连接池、屏蔽连接细节的相关文章

redis的java客户端Jedis简单封装

经过我们团队的一番讨论,最终决定使用redis来进行我们的业务缓存.redis会将数据缓存到内存中,运行效率会很快.同时异步将数据写入到磁盘中,进行持久化. 且redis支持主从同步,支持分布式部署,支持N多数据结构,这对于我们有着莫大的吸引力. 参见:http://blog.csdn.net/yichenlian/article/details/27207383 我们团队讨论的焦点是在于redis的灾备恢复问题.由于redis的持久化是异步的,总会有一点时间内存中数据和磁盘数据不同步的情况(当

阿里云-Redis-Help-连接实例-Redis客户端连接:Redis客户端、连接前提

ylbtech-阿里云-Redis-Help-连接实例-Redis客户端连接:Redis客户端.连接前提 1.返回顶部 1. Redis客户端 由于云数据库Redis提供的数据库服务与原生的数据库服务完全兼容,连接数据库的方式也基本类似.任何兼容Redis协议的客户端都可以访问云数据库Redis版服务,您可以根据自身应用特点选用任何Redis客户端. 注意 如果同一VPC内的实例开启了免密访问功能,则无需提供密码信息,即可连接数据库. 如果连接遇到问题,请参见Redis连接问题排查与解决. Re

Redis客户端连接

Redis接受上配置监听TCP端口和Unix套接字客户端的连接,如果启用.当一个新的客户端连接被接受,如有以下操作进行: 客户端套接字置于非阻塞状态,因为Redis的使用复用和非阻塞I/O操作. TCP_NODELAY选项设定是为了以确保我们没有连接延迟. 创建一个可读的文件时,这样Redis能够尽快收集客户端的查询作为新的数据可供读取的Socket中. 客户端的最大数量 Redis配置(redis.conf)属性调用MaxClients,它描述客户端可以连接到Redis的最大数量.命令的基本语

阿里云-Redis-Help-连接实例-Redis客户端连接:.net客户端

ylbtech-阿里云-Redis-Help-连接实例-Redis客户端连接:.net客户端 1.返回顶部 1. .net客户端 操作步骤如下所示: 下载并使用.net客户端. git clone https://github.com/ServiceStack/ServiceStack.Redis 在.net 客户端中新建.net项目. 添加客户端引用,引用文件在库文件的ServiceStack.Redis/lib/tests中. 在新建的.net项目中输入如下代码来连接云数据库Redis.详细

阿里云-Redis-Help-连接实例-Redis客户端连接:Jedis客户端

ylbtech-阿里云-Redis-Help-连接实例-Redis客户端连接:Jedis客户端 1.返回顶部 1. Jedis客户端 Jedis客户端访问云数据库Redis版服务,有以下两种方法: Jedis单链接 JedisPool连接池连接 操作步骤如下: 下载并安装Jedis客户端,详细步骤请参见Jedis使用说明. Jedis单连接示例 打开Eclipse客户端,创建一个Project,输入如下代码段: import redis.clients.jedis.Jedis; public c

Redis 客户端连接

Redis 通过监听一个 TCP 端口或者 Unix socket 的方式来接收来自客户端的连接,当一个连接建立后,Redis 内部会进行以下一些操作: 首先,客户端 socket 会被设置为非阻塞模式,因为 Redis 在网络事件处理上采用的是非阻塞多路复用模型. 然后为这个 socket 设置 TCP_NODELAY 属性,禁用 Nagle 算法 然后创建一个可读的文件事件用于监听这个客户端 socket 的数据发送 最大连接数 在 Redis2.4 中,最大连接数是被直接硬编码在代码里面的

redis数据库操作的C++简单封装

用c++简单封装了redis的基本操作(hiredis) 接口包括:①链接和断开连接.②设置键值对(set).③查询键值对(get).④删除键值对(del).⑤将所有键显示出来 若任何一处发生错误,返回对应的错误状态码,同时可以调用getErrorMsg()查看错误信息 所有码包括: M_REDIS_OK = 0, //执行成功 M_CONNECT_FAIL = -1, //连接redis失败 M_CONTEXT_ERROR = -2, //RedisContext返回错误 M_REPLY_ER

网络协议 finally{ return问题 注入问题 jdbc注册驱动问题 PreparedStatement 连接池目的 1.2.1DBCP连接池 C3P0连接池 MYSQL两种方式进行实物管理 JDBC事务 DBUtils事务 ThreadLocal 事务特性 并发访问 隔离级别

1.1.1 API详解:注册驱动 DriverManager.registerDriver(new com.mysql.jdbc.Driver());不建议使用 原因有2个: >导致驱动被注册2次. >强烈依赖数据库的驱动jar 解决办法: Class.forName("com.mysql.jdbc.Driver"); 1.1.2 API详解:java.sql.Statement接口: 操作sql语句,并返回相应结果 String sql = "某SQL语句&qu

redis客户端连接,最大连接数查询与设置

##redis客户端连接数 ##redis通过监听一个TCP端口或socket的方式接收来自客户端的连接,当与客户端建立连接后,redis内部会进行如下操作:(1)客户端socket会被设置为非阻塞模式,因为redis在网络时间处理上采用的是非阻塞多路复用模型:(2)然后为这个socket设置TCP_NODELAY属性,禁用Nagle算法:(3)然后创建一个可读的文件事件用于监听这个客户端socket的数据发送. ##redis最大连接数 ##(1.1)2.6之后版本,可以修改最大连接数配置,默