文章目录
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;
}