C++11 实现redis连接池

目录

1.准备工作

2.设计思路

3.技术实现

4.代码

5.多线程采用非连接池和连接池测试代码

6.测试用时


1.准备工作

测试环境:linux

安装好:hiredis

2.设计思路

1.连接应该具备的功能:连接、CURD,自动管理链接数,以及操作完后自动归还链接到连接池中。

2.采用队列存放已创建好的连接,基于其先进先出的特点。

3.每次更新后的连接插入到队尾,所以队头连接的空闲时长最长,如果超过设定的最大空闲时长,销毁该线程。

3.技术实现

1.先封装redis的基本操作为一个单独的.h和.cpp文件。

2.连接池考虑采用生产者消费者模型,其中涉及到线程同步问题。

3.解决线程同步问题:

  使用互斥锁+条件变量。

4.线程池自动管理连接数,分离两个子线程分别维护两个连接数,最小连接数和最大连接数。

4.代码

RedisConn.h

class RedisConnection {
private:
    redisContext *rds_ctx = nullptr;
    steady_clock::time_point m_aliveTime;
public:
    static RedisConnection&  get_instance();
    string  virtual_ip = "";
    bool redis_connect();
    void redis_disconnect();
    bool redis_cmd(const char *cmd, redisReply **reply);
    void refreshAliveTime();                      //刷新起始空闲时间
    long long getAliveTime();                    //获得起始时间    
    string replace_blank(const string &value);  
    string recover_blank(const string &value);  

    //string操作
    bool redis_set(const string &key, const string &value);
    bool redis_get(const string &key, string &value);
 
    #endif //REDISCONNECTION_H


RedisConn.cpp

#include "RedisConn.h"

RedisConnection&  RedisConnection :: get_instance() {
    static RedisConnection instance;
    return instance;
}
//redis连接
bool RedisConnection::redis_connect() {
    struct timeval timeout = {REDIS_TIMEOUT,0};
    redisReply *reply = nullptr;
    if(virtual_ip.empty()){
        rds_ctx = redisConnectWithTimeout(REDIS_HOSTNAME, REDIS_PORT, timeout);
    }else {
        cout << "redis::" << virtual_ip << endl;
        rds_ctx = redisConnectWithTimeout((const char *)virtual_ip.c_str(), REDIS_PORT, timeout);
    }
    if (rds_ctx == nullptr) {
        printf("[%s:%d] Msg: redis connect error.\n", __FILE__, __LINE__);
        return false;
    }
    if(rds_ctx->err) {
        printf("[%s:%d] Msg: redis connect error: %s\n", __FILE__, __LINE__, rds_ctx->errstr);
        redisFree(rds_ctx);
        rds_ctx = NULL;
        return false;
    }
#if REDIS_NEED_PWD
    reply = (redisReply *) redisCommand(rds_ctx, "auth 123456");
    if (reply == nullptr || reply->type != REDIS_REPLY_STATUS || strcasecmp(reply->str, "OK") != 0) {
        printf("[%s:%d] Msg: redis cli auth error.\n", __FILE__, __LINE__);
        freeReplyObject(reply);
        redisFree(rds_ctx);
        rds_ctx = NULL;
        return false;
    }
    freeReplyObject(reply);
#endif

    reply = (redisReply *) redisCommand(rds_ctx, "ping");
    if(reply == nullptr) {
        redisFree(rds_ctx);
        rds_ctx = NULL;
        return false;
    }
    if (reply->type != REDIS_REPLY_STATUS || strcasecmp(reply->str, "PONG") != 0) {
        printf("redis ping testing error.\n");
        freeReplyObject(reply);
        redisFree(rds_ctx);
        rds_ctx = NULL;
        return false;
    }
    freeReplyObject(reply);
    return true;
}

void RedisConnection::redis_disconnect() {
    if(rds_ctx) {
        redisFree(rds_ctx);
        rds_ctx = NULL;
    }
    return ;
}


bool RedisConnection::redis_cmd(const char *cmd,redisReply **reply) {
    if (rds_ctx == nullptr) {
        printf("redis cli reconnect.\n");
        if(!redis_connect()) {
            return false;
        }
    }
    *reply = (redisReply *) redisCommand(rds_ctx, cmd);
    if (*reply == nullptr) {
        printf("[%s:%d] Msg: exec redis cmd fail::%s\n", __FILE__, __LINE__,cmd);
        if(rds_ctx) {
            redisFree(rds_ctx);
            rds_ctx = NULL;
        }
        return false;
    }
    return true;
}


bool RedisConnection::redis_set(const string &key, const string &value) {
    string rdsCmd;
    redisReply *reply = nullptr;
    rdsCmd = "set " + key + " " + replace_blank(value) ;
    if(!redis_cmd(rdsCmd.c_str(),&reply)) {
        if(reply) {
            freeReplyObject(reply);
        }
        return false;
    }
    if (reply->type != REDIS_REPLY_STATUS || strcasecmp(reply->str, "OK") != 0) {
	freeReplyObject(reply);
        return false;
    }

    freeReplyObject(reply);
    return true;
}

bool RedisConnection::redis_get(const string &key, string &value) {
    string rdsCmd;
    redisReply *reply = nullptr;
    rdsCmd = "get " + key;

    if(!redis_cmd(rdsCmd.c_str(),&reply)) {
        if(reply) {
            freeReplyObject(reply);
        }
        return false;
    }

    if (reply->type != REDIS_REPLY_STRING) {
        value = reply->str;
        value = recover_blank(value);
        freeReplyObject(reply);
        return false;
    }
    if (reply->type == REDIS_REPLY_NIL)
    {
        value.clear();
    }
    //value = string(reply->str, reply->len);
    value =  reply->str;
    value = recover_blank(value); 
    freeReplyObject(reply);
    return true;
}

void RedisConnection::refreshAliveTime()
{
    m_aliveTime = steady_clock::now();

}
//刷新起始空闲时间
long long RedisConnection::getAliveTime()
{
    //这里是纳秒
    nanoseconds res = steady_clock::now() - m_aliveTime;
    //将纳秒转换成毫秒
    milliseconds mil = duration_cast(res);
    return mil.count();
}

string RedisConnection::replace_blank(const string &value){
    string str(value);
    char blank_buf = {' '};
    size_t sFind = str.find(blank_buf);
    while(sFind != string::npos){
        str.replace(sFind, 1, BLANK_BUF);
        sFind = str.find(blank_buf,sFind);
    }
    return str;
}

string RedisConnection::recover_blank(const string &value){
    string str(value);
    string sSpace (BLANK_BUF);
    int len = sSpace.size();

    size_t sFind = str.find(sSpace);
    while(sFind != string::npos){
        str.replace(sFind, len, " ");
        sFind = str.find(sSpace,sFind);
    }
    return str;
}

RedisPool.h

class RedisConnectionPool {
public:
    static RedisConnectionPool* getInstance(); // 获取单例对象

    // 禁用拷贝构造函数和赋值运算符
    RedisConnectionPool(const RedisConnectionPool&) = delete;
    RedisConnectionPool& operator=(const RedisConnectionPool&) = delete;
    // 获取连接对象
    shared_ptr getConnection();
    void releaseConnection(shared_ptr m_conn);
    void produceConnection();
    void addConnection();
    void destroyConnection();

private:
    RedisConnectionPool();
    ~RedisConnectionPool();

    string hostaddr; 
    int port; 
    int m_maxsize; //连接池上限
    int m_minsize = 3; //连接池下限
  
    //超时时间
    int m_timeout =3000;
    //最大空闲时间
    int m_maxIdleTime = 1000;
    queue> m_connQ; 
    mutex m_mutex; 
    condition_variable m_cond; 

};

RedisPool.cpp

#include "RedisPool.h"


RedisConnectionPool* RedisConnectionPool::getInstance() {
    static RedisConnectionPool instance;
    return &instance;
}

RedisConnectionPool::RedisConnectionPool()
   : hostaddr(REDIS_HOSTNAME), port(REDIS_PORT),m_maxsize(5) {
    for (int i = 0; i redis_disconnect();
            m_connQ.pop();
    }
}

std::shared_ptr RedisConnectionPool::getConnection() {
    std::unique_lock locker(m_mutex);

    while (m_connQ.empty()) {
        if(cv_status::timeout ==  m_cond.wait_for(locker,chrono::milliseconds(m_timeout)))
        {
            if(m_connQ.empty())
            {
                continue;
            }
        }
    }
    //将连接重新放连接池,不是销毁。
    std::shared_ptr  m_conn = m_connQ.front();

    m_connQ.pop();
    m_cond.notify_all();
    return m_conn;
}

void RedisConnectionPool::releaseConnection(std::shared_ptr m_conn) {
    std::unique_lock lock(m_mutex);
    m_connQ.push(m_conn);
    m_cond.notify_one();
}

void RedisConnectionPool::produceConnection()
{
    while(true)
    {
        std::unique_lock locker(m_mutex);
        while(m_connQ.size() >= m_maxsize)
        {
            m_cond.wait(locker);
        }
        addConnection();
        m_cond.notify_all();
    }
}

void RedisConnectionPool::destroyConnection() {
    while(true){
        this_thread::sleep_for(chrono::milliseconds(6000));
        std::lock_guardlocker(m_mutex);
        while(m_connQ.size() > m_minsize){
            std::shared_ptr m_conn = m_connQ.front();
            if (m_conn->getAliveTime() >= m_maxIdleTime)
            {
                m_conn->redis_disconnect();
                m_connQ.pop();
            }
            else
            {
                break;
            }
        }
    }
}

void RedisConnectionPool::addConnection() {
    if (m_connQ.size() < m_maxsize) {
        RedisConnection* new_conn = new RedisConnection();
        if(new_conn->redis_connect())
        {
            new_conn->refreshAliveTime();
            std::shared_ptr m_conn(new_conn);
             m_connQ.push(m_conn);
        }else{
            delete new_conn;
        }
    }
}

5.多线程采用非连接池和连接池测试代码

#include "RedisConn.h"
#include "RedisPool.h"
using namespace std;

void op1(int begin, int end) {
    RedisConnection db;
    for (int i = begin; i < end; ++i) {
        bool res;
        char tmp[1024] = {0};
        sprintf(tmp, "SET key_%d value_%d", i, i);
        string cmd = tmp;
        res = db.redis_set(cmd, "");
    }
}

void op2(RedisConnectionPool *pool, int begin, int end) {
    for (int i = begin; i < end; ++i) {
        // 获取连接对象
        shared_ptr m_conn = pool->getConnection();
        bool res;
        char tmp[1024] = {0};
        sprintf(tmp, "SET key_%d value_%d", i, i);
        string cmd = tmp;
        res =  m_conn->redis_set(cmd, "");
        pool->releaseConnection(m_conn);
    }
}

// 多线程测试函数
void test1() {
    if(10 > 1) {
        cout << "----多线程连接池测试----" << endl;
        RedisConnectionPool *pool = RedisConnectionPool::getInstance();
        steady_clock::time_point begin = steady_clock::now();
        thread t1(op2, pool, 0, 1000);
        thread t2(op2, pool, 1000, 2000);
        thread t3(op2, pool, 2000, 3000);
        thread t4(op2, pool, 3000, 4000);
        thread t5(op2, pool, 4000, 5000);
        t1.join();
        t2.join();
        t3.join();
        t4.join();
        t5.join();
        steady_clock::time_point end = steady_clock::now();
        auto length = end - begin;
        cout << "多线程连接池,用时" << length.count() << "纳秒, "
            << length.count() / 1000000 << "毫秒, "
            << length.count() / 1000000000 << "秒 " << endl;
    } else {
          //多线程非连接池
        cout <<"----多线程非连接池-----"<

编译:

g++ -std=c++11  -pthread test.cpp RedisConn.cpp RedisPool.cpp -o test -L/usr/local/lib/ -lhiredis

6.测试用时

----多线程非连接池-----
多线程非连接池, 用时:149353538纳秒,149毫秒,0秒
----多线程线程池测试----
连接池,多线程,用时142527557纳秒, 142毫秒, 0秒

你可能感兴趣的:(Redis,redis,数据库,缓存)