图床项目Redis连接池代码阅读

CachePool.h

宏定义

#define REDIS_COMMAND_SIZE 300            /* redis Command 指令最大长度 */
#define FIELD_ID_SIZE 100                /* redis hash表field域字段长度 */
#define VALUES_ID_SIZE 1024                /* redis    value域字段长度 */
typedef char (*RFIELDS)[FIELD_ID_SIZE]; /* redis hash表存放批量field字符串数组类型 */
 
//数组指针类型,其变量指向 char[1024]
typedef char (*RVALUES)[VALUES_ID_SIZE]; /* redis 表存放批量value字符串数组类型 */

注:RFIELDS是指向存放字段名的数组,RVALUES存放某一字段对应的值。

Redis连接类

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

    int Init();
    void DeInit();
    const char *GetPoolName();
    // 通用操作
    // 判断一个key是否存在
    bool isExists(string &key);
    // 删除某个key
    long del(string key);

    // ------------------- 字符串相关 -------------------
    string get(string key);
    string set(string key, string &value);
    string setex(string key, int timeout, string value);

    // string mset(string key, map);
    //批量获取
    bool mget(const vector<string> &keys, map<string, string> &ret_value);
    //原子加减1
    long incr(string key);
    long decr(string key);

    // ---------------- 哈希相关 ------------------------
    long hdel(string key, string field);
    string hget(string key, string field);
    int hget(string key, char *field, char *value);
    bool hgetAll(string key, map<string, string> &ret_value);
    long hset(string key, string field, string value);

    long hincrBy(string key, string field, long value);
    long incrBy(string key, long value);
    string hmset(string key, map<string, string> &hash);
    bool hmget(string key, list<string> &fields, list<string> &ret_value);

    // ------------ 链表相关 ------------
    long lpush(string key, string value);
    long rpush(string key, string value);
    long llen(string key);
    bool lrange(string key, long start, long end, list<string> &ret_value);

    // zset 相关
    int ZsetExit(string key, string member);
    int ZsetAdd(string key, long score, string member);
    int ZsetZrem(string key, string member);
    int ZsetIncr(string key, string member);
    int ZsetZcard(string key);
    int ZsetZrevrange(string key, int from_pos, int end_pos, RVALUES values, int &get_num);
    int ZsetGetScore(string key, string member);

    bool flushdb();

    private:
    CachePool *m_pCachePool;
    redisContext *m_pContext; // 每个redis连接 redisContext redis客户端编程的对象
    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;
};

Redis连接池

Redis为缓存所以取名为CachePool

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

    int Init();
    // 获取空闲的连接资源
    CacheConn *GetCacheConn(const int timeout_ms = 0);
    // Pool回收连接资源
    void RelCacheConn(CacheConn *pCacheConn);

    const char *GetPoolName() { return m_pool_name.c_str(); }
    const char *GetServerIP() { return m_server_ip.c_str(); }
    const char *GetPassword() { return m_password.c_str(); }
    int GetServerPort() { return m_server_port; }
    int GetDBIndex() { 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; // 链表:存放空闲的redis连接

    std::mutex m_mutex;
    std::condition_variable m_cond_var;
    bool m_abort_request = false;
};

连接池管理类

class CacheManager
{
public:
	virtual ~CacheManager();

	static CacheManager *getInstance();

	int Init();
	CacheConn *GetCacheConn(const char *pool_name);
	void RelCacheConn(CacheConn *pCacheConn);

private:
	CacheManager();

private:
	static CacheManager *s_cache_manager;
	map<string, CachePool *> m_cache_pool_map;
};
  • 构造函数放在 private表明只有成员函数可调用 ,并且提供一个static指针对象,使得全局只唯一一个对象,采用了单例模式
  • m_cache_pool_map表明有多个连接池

用于自动释放Redis连接的类

class AutoRelCacheCon
{
public:
	AutoRelCacheCon(CacheManager *manger, CacheConn *conn) : manger_(manger), conn_(conn) {}
	~AutoRelCacheCon()
	{
		if (manger_)
		{
			manger_->RelCacheConn(conn_);
		}
	} //在析构函数规划
private:
	CacheManager *manger_ = NULL;
	CacheConn *conn_ = NULL;
};

#define AUTO_REAL_CACHECONN(m, c) AutoRelCacheCon autorelcacheconn(m, c)

创建一个该类对象,需传入连接池管理对象指针和连接对象 指针。当该类生命周期结束时,会自动调用析构通过连接池管理对象来释放连接对象。

CachePool.cpp

Redis连接初始化

redis初始化和重连,类似于 mysql_ping

int CacheConn::Init()
{
	if (m_pContext) // 非空,连接是正常的
	{
		return 0;
	}

	// 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);
		return 1;
	}
	// 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");
		}

		return 1;
	}

	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);
		return 0;
	}
	else
	{
		if (reply)
			log_error("select cache db failed:%s\n", reply->str);
		return 2;
	}
}

判断某个key是否存在

bool CacheConn::isExists(string &key)
{
	if (Init()) // Init()失败返回非0
	{
		return false;
	}

	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;
		return false;
	}
	long ret_value = reply->integer;
	freeReplyObject(reply);
	if (0 == ret_value)
	{
		return false;
	}
	else
	{
		return true;
	}
}

Redis连接池类 Cachepool

析构函数
CachePool::~CachePool()
{
	{
		std::lock_guard<std::mutex> lock(m_mutex);
		m_abort_request = true;
		m_cond_var.notify_all(); // 通知所有在等待的
	}
	{
		std::lock_guard<std::mutex> lock(m_mutex);
		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;
}
连接池初始化
int CachePool::Init()
{
	// 初始 m_cur_conn_cnt = 2
	for (int i = 0; i < m_cur_conn_cnt; i++)
	{
		CacheConn *pConn = new CacheConn(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;
			return 1;
		}

		m_free_list.push_back(pConn);
	}

	log_info("cache pool: %s, list size: %lu\n", m_pool_name.c_str(), m_free_list.size());
	return 0;
}
从连接池中获取一条Redis连接
CacheConn *CachePool::GetCacheConn(const int timeout_ms)
{
	std::unique_lock<std::mutex> lock(m_mutex);
	if (m_abort_request)
	{
		log_info("have aboort\n");
		return NULL;
	}

	if (m_free_list.empty()) // 2 当没有连接可以用时
	{
		// 第一步先检测 当前连接数量是否达到最大的连接数量
		if (m_cur_conn_cnt >= m_max_conn_cnt) // 等待的逻辑
		{
			// 如果已经到达了,看看是否需要超时等待
			if (timeout_ms <= 0) // 死等,直到有连接可以用 或者 连接池要退出
			{
				log_info("wait ms:%d\n", timeout_ms);
				m_cond_var.wait(lock, [this]
								{
					// 当前连接数量小于最大连接数量 或者请求释放连接池时退出,即return true时才会退出wait
					return (!m_free_list.empty()) | m_abort_request; });
			}
			else
			{
				// return如果返回 false,继续wait(或者超时),  如果返回true退出wait
				// 1.m_free_list不为空
				// 2.超时退出
				// 3. m_abort_request被置为true,要释放整个连接池
				m_cond_var.wait_for(lock, std::chrono::milliseconds(timeout_ms), [this]
									{ return (!m_free_list.empty()) | m_abort_request; });
				// 带超时功能时还要判断是否为空
				if (m_free_list.empty()) // 如果连接池还是没有空闲则退出
				{
					return NULL;
				}
			}

			if (m_abort_request)
			{
				log_warn("have aboort\n");
				return NULL;
			}
		}
		else // 还没有到最大连接则创建连接
		{
			CacheConn *pDBConn = new CacheConn(m_server_ip.c_str(), m_server_port,
											   m_db_index, m_password.c_str(), m_pool_name.c_str()); //新建连接
			int ret = pDBConn->Init();
			if (ret)
			{
				log_error("Init DBConnecton failed\n\n");
				delete pDBConn;
				return NULL;
			}
			else
			{
				m_free_list.push_back(pDBConn);
				m_cur_conn_cnt++;
				// log_info("new db 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();

	return pConn;
}

m_free_list存放空闲可用的连接

Redis连接池管理类 CacheManager

初始化函数
int CacheManager::Init()
{
	CConfigFileReader config_file("tc_http_server.conf");

	char *cache_instances = config_file.GetConfigName("CacheInstances");
	if (!cache_instances)
	{
		log("not configure CacheIntance");
		return 1;
	}

	char host[64];
	char port[64];
	char db[64];
	char maxconncnt[64];
	CStrExplode instances_name(cache_instances, ',');
	for (uint32_t i = 0; i < instances_name.GetItemCnt(); i++)
	{
		char *pool_name = instances_name.GetItem(i);
		// printf("%s", pool_name);
		snprintf(host, 64, "%s_host", pool_name);
		snprintf(port, 64, "%s_port", pool_name);
		snprintf(db, 64, "%s_db", pool_name);
		snprintf(maxconncnt, 64, "%s_maxconncnt", pool_name);

		char *cache_host = config_file.GetConfigName(host);
		char *str_cache_port = config_file.GetConfigName(port);
		char *str_cache_db = config_file.GetConfigName(db);
		char *str_max_conn_cnt = config_file.GetConfigName(maxconncnt);
		if (!cache_host || !str_cache_port || !str_cache_db || !str_max_conn_cnt)
		{
			log("not configure cache instance: %s", pool_name);
			return 2;
		}

		CachePool *pCachePool = new CachePool(pool_name, cache_host, atoi(str_cache_port),
											  atoi(str_cache_db), "", atoi(str_max_conn_cnt));
		if (pCachePool->Init())
		{
			log("Init cache pool failed");
			return 3;
		}
		// 可以有多个连接池
		m_cache_pool_map.insert(make_pair(pool_name, pCachePool));
	}

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值