目录
1.准备工作
2.设计思路
3.技术实现
4.代码
5.多线程采用非连接池和连接池测试代码
6.测试用时
测试环境:linux
安装好:hiredis
1.连接应该具备的功能:连接、CURD,自动管理链接数,以及操作完后自动归还链接到连接池中。
2.采用队列存放已创建好的连接,基于其先进先出的特点。
3.每次更新后的连接插入到队尾,所以队头连接的空闲时长最长,如果超过设定的最大空闲时长,销毁该线程。
1.先封装redis的基本操作为一个单独的.h和.cpp文件。
2.连接池考虑采用生产者消费者模型,其中涉及到线程同步问题。
3.解决线程同步问题:
使用互斥锁+条件变量。
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;
}
}
}
#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
----多线程非连接池-----
多线程非连接池, 用时:149353538纳秒,149毫秒,0秒
----多线程线程池测试----
连接池,多线程,用时142527557纳秒, 142毫秒, 0秒