#ifndef _MY_REDIS_POOL_HPP_
#define _MY_REDIS_POOL_HPP_
#include <cstdio>
#include <cstdlib>
#include <string>
#include <time.h>
#include <vector>
#include <queue>
#include<mutex>
#include <hiredis/hiredis.h>
#ifdef _WIN32
//编译 hiredis 会生成这2个库
#pragma comment(lib, "hiredis.lib")
#pragma comment(lib,"Win32_Interop.lib")
#endif // _WIN32
typedef struct RedisNode
{
int id;
redisContext *c;
bool isUsed;
};
class MyRedisPool
{
public:
static MyRedisPool& GetInstance();
protected:
MyRedisPool();
~MyRedisPool();
private:
MyRedisPool(const MyRedisPool& rhs) {}
MyRedisPool& operator = (const MyRedisPool& rhs) {}
public:
bool InitConnect(std::string const& host, std::string const& password, int port,int count);
RedisNode& GetConnect(bool& isSuccess);
bool GetRedisReply(redisReply *&reply);
bool AddConnect();
bool DisConnect();
bool BackConnect(RedisNode& node);
bool GetLPOP(std::string const&key, std::string&value, int dbName);
bool SetLPUSH(std::string const&key, std::string&value, int dbName);
bool GetLRange(std::string const&key, int dbName, int start, int end, std::vector<std::string>& result);
bool SetString(std::string const&key, std::string&value, int dbName);
bool GetString(std::string const&key, std::string&value, int dbName);
bool SetInt(std::string const&key, int &number, int dbName);
bool GetInt(std::string const&key, int &number, int dbName);
bool SUBSCRIBE(std::string const&theme);
bool PUBLISH(std::string const& theme, std::string const& content);
bool GetLPOPByPipeline(std::string const&key, std::string&value, int dbName);
bool SetLPUSHByPipeline(std::string const&key, std::string&value, int dbName);
bool GetLRangeByPipeline(std::string const&key, int dbName, int start, int end, std::vector<std::string>& result);
bool SetStringByPipeline(std::string const&key, std::string&value, int dbName);
bool GetStringByPipeline(std::string const&key, std::string&value, int dbName);
bool SetIntByPipeline(std::string const&key, int &number, int dbName);
bool GetIntByPipeline(std::string const&key, int &number, int dbName);
protected:
bool CreateOneConnect(redisContext *&c);
private:
std::string m_Host;
int m_Port;
std::string m_PassWord;
int m_MaxConnects;
int m_CurConnects;
std::vector<RedisNode> m_RedisVec;
std::queue<int> m_QueueId;
std::mutex m_Lock;
std::mutex m_PrintLock;
bool m_runing;
};
MyRedisPool::MyRedisPool()
{
m_runing = true;
}
MyRedisPool::~MyRedisPool()
{
DisConnect();
printf("析构了...\n");
}
bool MyRedisPool::GetRedisReply(redisReply *&reply)
{
if (reply)
{
if (reply->type == REDIS_REPLY_ERROR)
{
std::lock_guard<std::mutex> lck(m_PrintLock);
fprintf(stderr, "error: %s\n", reply->str);
freeReplyObject(reply);
return false;
}
else
{
return true;
}
}
else
{
return false;
}
}
bool MyRedisPool::GetLPOPByPipeline(std::string const&key, std::string&value, int dbName)
{
bool isSuccess = false;
bool ret = false;
RedisNode &node = GetConnect(isSuccess);
if (isSuccess)
{
redisAppendCommand(node.c, "SELECT %d", dbName);
redisAppendCommand(node.c, "LPOP %s", key.data());
redisReply *reply = NULL;
redisGetReply(node.c,(void**)&reply);
ret = GetRedisReply(reply);
if (ret)
{
freeReplyObject(reply);
redisGetReply(node.c, (void**)&reply);
ret = GetRedisReply(reply);
if (ret)
{
value = reply->str;
freeReplyObject(reply);
}
}
BackConnect(node);
}
}
bool MyRedisPool::SetLPUSHByPipeline(std::string const&key, std::string&value, int dbName)
{
bool isSuccess = false;
bool ret = false;
RedisNode &node = GetConnect(isSuccess);
if (isSuccess)
{
redisAppendCommand(node.c, "SELECT %d", dbName);
redisAppendCommand(node.c, "LPUSH %s %s", key.data(), value.data());
redisReply *reply = NULL;
redisGetReply(node.c, (void**)&reply);
ret = GetRedisReply(reply);
if (ret)
{
freeReplyObject(reply);
redisGetReply(node.c, (void**)&reply);
ret = GetRedisReply(reply);
if (ret)
{
freeReplyObject(reply);
}
}
BackConnect(node);
}
}
bool MyRedisPool::GetLRangeByPipeline(std::string const&key, int dbName, int start, int end, std::vector<std::string>& result)
{
bool isSuccess = false;
bool ret = false;
RedisNode &node = GetConnect(isSuccess);
if (isSuccess)
{
redisAppendCommand(node.c, "SELECT %d", dbName);
redisAppendCommand(node.c, "LRANGE %s %d %d", key.data(), start, end);
redisReply *reply = NULL;
redisGetReply(node.c, (void**)&reply);
ret = GetRedisReply(reply);
if (ret)
{
freeReplyObject(reply);
redisGetReply(node.c, (void**)&reply);
ret = GetRedisReply(reply);
if (ret)
{
if (reply->type == REDIS_REPLY_ARRAY) {
for (int j = 0; j < reply->elements; j++) {
//printf("%u) %s\n", j, reply->element[j]->str);
result.push_back(reply->element[j]->str);
}
}
freeReplyObject(reply);
}
}
BackConnect(node);
}
}
bool MyRedisPool::SetStringByPipeline(std::string const&key, std::string&value, int dbName)
{
bool isSuccess = false;
bool ret = false;
RedisNode &node = GetConnect(isSuccess);
if (isSuccess)
{
redisAppendCommand(node.c, "SELECT %d", dbName);
redisAppendCommand(node.c, "SET %s %s", key.data(), value.data());
redisReply *reply = NULL;
redisGetReply(node.c, (void**)&reply);
ret = GetRedisReply(reply);
if (ret)
{
freeReplyObject(reply);
redisGetReply(node.c, (void**)&reply);
ret = GetRedisReply(reply);
if (ret)
{
freeReplyObject(reply);
}
}
BackConnect(node);
}
}
bool MyRedisPool::GetStringByPipeline(std::string const&key, std::string&value, int dbName)
{
bool isSuccess = false;
bool ret = false;
RedisNode &node = GetConnect(isSuccess);
if (isSuccess)
{
redisAppendCommand(node.c, "SELECT %d", dbName);
redisAppendCommand(node.c, "GET %s", key.data());
redisReply *reply = NULL;
redisGetReply(node.c, (void**)&reply);
ret = GetRedisReply(reply);
if (ret)
{
freeReplyObject(reply);
redisGetReply(node.c, (void**)&reply);
ret = GetRedisReply(reply);
if (ret)
{
if (reply->len > 0)
{
value = reply->str;
}
freeReplyObject(reply);
}
}
BackConnect(node);
}
}
bool MyRedisPool::SetIntByPipeline(std::string const&key, int &number, int dbName)
{
bool isSuccess = false;
bool ret = false;
RedisNode &node = GetConnect(isSuccess);
if (isSuccess)
{
redisAppendCommand(node.c, "SELECT %d", dbName);
redisAppendCommand(node.c, "SET %s %s", key.data(), number);
redisReply *reply = NULL;
redisGetReply(node.c, (void**)&reply);
ret = GetRedisReply(reply);
if (ret)
{
freeReplyObject(reply);
redisGetReply(node.c, (void**)&reply);
ret = GetRedisReply(reply);
if (ret)
{
freeReplyObject(reply);
}
}
BackConnect(node);
}
}
bool MyRedisPool::GetIntByPipeline(std::string const&key, int &number, int dbName)
{
bool isSuccess = false;
bool ret = false;
RedisNode &node = GetConnect(isSuccess);
if (isSuccess)
{
redisAppendCommand(node.c, "SELECT %d", dbName);
redisAppendCommand(node.c, "GET %s", key.data());
redisReply *reply = NULL;
redisGetReply(node.c, (void**)&reply);
ret = GetRedisReply(reply);
if (ret)
{
freeReplyObject(reply);
redisGetReply(node.c, (void**)&reply);
ret = GetRedisReply(reply);
if (ret)
{
if (reply->len > 0)
{
number = std::atoi(reply->str);
}
freeReplyObject(reply);
}
}
BackConnect(node);
}
}
bool MyRedisPool::SetLPUSH(std::string const&key, std::string&value, int dbName)
{
bool isSuccess = false;
bool ret = false;
RedisNode &node = GetConnect(isSuccess);
if (isSuccess)
{
redisReply *reply = (redisReply *)redisCommand(node.c, "SELECT %d", dbName);
ret = GetRedisReply(reply);
if (ret)
{
freeReplyObject(reply);
reply = (redisReply *)redisCommand(node.c, "LPUSH %s %s", key.data(),value.data());
ret = GetRedisReply(reply);
if (ret)
{
freeReplyObject(reply);
}
}
BackConnect(node);
}
return ret;
}
bool MyRedisPool::GetLPOP(std::string const&key, std::string&value,int dbName)
{
bool isSuccess = false;
bool ret = false;
RedisNode &node = GetConnect(isSuccess);
if (isSuccess)
{
redisReply *reply = (redisReply *)redisCommand(node.c, "SELECT %d", dbName);
ret = GetRedisReply(reply);
if (ret)
{
value = reply->str;
freeReplyObject(reply);
reply = (redisReply *)redisCommand(node.c, "LPOP %s", key.data());
ret = GetRedisReply(reply);
if (ret)
{
value = reply->str;
freeReplyObject(reply);
}
}
BackConnect(node);
}
return ret;
}
bool MyRedisPool::GetLRange(std::string const&key, int dbName, int start, int end, std::vector<std::string>& result)
{
bool isSuccess = false;
bool ret = false;
result.clear();
RedisNode &node = GetConnect(isSuccess);
if (isSuccess)
{
redisReply *reply = (redisReply *)redisCommand(node.c, "SELECT %d", dbName);
ret = GetRedisReply(reply);
if (ret)
{
freeReplyObject(reply);
reply = (redisReply *)redisCommand(node.c, "LRANGE %s %d %d", key.data(),start,end);
ret = GetRedisReply(reply);
if (ret)
{
if (reply->type == REDIS_REPLY_ARRAY) {
for (int j = 0; j < reply->elements; j++) {
//printf("%u) %s\n", j, reply->element[j]->str);
result.push_back(reply->element[j]->str);
}
}
freeReplyObject(reply);
}
}
BackConnect(node);
}
return ret;
}
bool MyRedisPool::SetString(std::string const&key, std::string&value, int dbName)
{
bool isSuccess = false;
bool ret = false;
RedisNode &node = GetConnect(isSuccess);
if (isSuccess)
{
redisReply *reply = (redisReply *)redisCommand(node.c, "SELECT %d", dbName);
ret = GetRedisReply(reply);
if (ret)
{
freeReplyObject(reply);
reply = (redisReply *)redisCommand(node.c, "SET %s %s", key.data(), value.data());
ret = GetRedisReply(reply);
if (ret)
{
freeReplyObject(reply);
}
}
BackConnect(node);
}
return ret;
}
bool MyRedisPool::GetString(std::string const&key, std::string&value, int dbName)
{
bool isSuccess = false;
bool ret = false;
RedisNode &node = GetConnect(isSuccess);
if (isSuccess)
{
redisReply *reply = (redisReply *)redisCommand(node.c, "SELECT %d", dbName);
ret = GetRedisReply(reply);
if (ret)
{
freeReplyObject(reply);
reply = (redisReply *)redisCommand(node.c, "GET %s", key.data());
ret = GetRedisReply(reply);
if (ret)
{
if (reply->len > 0)
{
value = reply->str;
}
freeReplyObject(reply);
}
}
BackConnect(node);
}
return ret;
}
bool MyRedisPool::SetInt(std::string const&key, int &number, int dbName)
{
bool isSuccess = false;
bool ret = false;
RedisNode &node = GetConnect(isSuccess);
if (isSuccess)
{
redisReply *reply = (redisReply *)redisCommand(node.c, "SELECT %d", dbName);
ret = GetRedisReply(reply);
if (ret)
{
freeReplyObject(reply);
reply = (redisReply *)redisCommand(node.c, "SET %s %d", key.data(), number);
ret = GetRedisReply(reply);
if (ret)
{
freeReplyObject(reply);
}
}
BackConnect(node);
}
return ret;
}
bool MyRedisPool::GetInt(std::string const&key, int &number, int dbName)
{
bool isSuccess = false;
bool ret = false;
RedisNode &node = GetConnect(isSuccess);
if (isSuccess)
{
redisReply *reply = (redisReply *)redisCommand(node.c, "SELECT %d", dbName);
ret = GetRedisReply(reply);
if (ret)
{
freeReplyObject(reply);
reply = (redisReply *)redisCommand(node.c, "GET %s", key.data());
ret = GetRedisReply(reply);
if (ret)
{
if (reply->len > 0)
{
number = std::atoi(reply->str);
}
freeReplyObject(reply);
}
}
BackConnect(node);
}
return ret;
}
bool MyRedisPool::PUBLISH(std::string const& theme,std::string const& content)
{
bool isSuccess = false;
RedisNode &node = GetConnect(isSuccess);
if (isSuccess)
{
redisReply *reply = (redisReply *)redisCommand(node.c, "PUBLISH %s %s", theme.data(), content.data());
bool ret = GetRedisReply(reply);
if (ret)
{
freeReplyObject(reply);
}
BackConnect(node);
return ret;
}
return isSuccess;
}
bool MyRedisPool::SUBSCRIBE(std::string const&theme)
{
redisContext *c = NULL;
bool ret = CreateOneConnect(c);
if (ret)
{
redisReply *reply = (redisReply *)redisCommand(c, "SUBSCRIBE %s", theme.data());
freeReplyObject(reply);
while (redisGetReply(c,(void**)&reply) == REDIS_OK) {
// consume message
ret = GetRedisReply(reply);
if (ret)
{
if (reply->elements>0)
{
for (size_t i = 0; i < reply->elements; i++)
{
std::cout << reply->element[reply->elements-1]->str << std::endl;
break;
}
}
freeReplyObject(reply);
}
}
if (c)
{
redisFree(c);
}
}
return ret;
}
bool MyRedisPool::BackConnect(RedisNode& node)
{
std::lock_guard<std::mutex> lck(m_Lock);
for (size_t i = 0; i < m_RedisVec.size(); i++)
{
RedisNode& n = m_RedisVec[i];
if (node.id == n.id)
{
n.isUsed = false;
break;
}
}
return true;
}
bool MyRedisPool::DisConnect()
{
std::lock_guard<std::mutex> lck(m_Lock);
for (size_t i = 0; i < m_RedisVec.size(); i++)
{
RedisNode& node = m_RedisVec[i];
if (node.c)
{
redisFree(node.c);
}
}
return true;
}
bool MyRedisPool::AddConnect()
{
size_t size = m_MaxConnects - m_CurConnects;
struct timeval timeout = { 1, 500000 }; // 1.5 seconds
for (size_t i = 0; i < size; i++)
{
RedisNode node;
#ifdef _WIN32
node.c = redisConnectWithTimeout(m_Host.data(), m_Port, timeout);
#else
node.c = redisConnectUnixWithTimeout(hostname, timeout);
#endif
if (node.c == NULL || node.c->err) {
if (node.c) {
std::lock_guard<std::mutex> lck(m_PrintLock);
printf("Connection error: %s\n", node.c->errstr);
redisFree(node.c);
}
else {
std::lock_guard<std::mutex> lck(m_PrintLock);
printf("Connection error: can't allocate redis context\n");
}
continue;
}
if (m_PassWord.length()>0)
{
redisReply *reply = (redisReply *)redisCommand(node.c, "AUTH %s ", m_PassWord.data());
bool ret = GetRedisReply(reply);
if (ret)
{
freeReplyObject(reply);
}
else
{
redisFree(node.c);
continue;
}
}
if (m_QueueId.size()>0)
{
node.id = m_QueueId.front();
m_QueueId.pop();
}
else
{
node.id = i;
}
node.isUsed = false;
m_RedisVec.push_back(node);
m_CurConnects++;
}
return true;
}
bool MyRedisPool::CreateOneConnect(redisContext *&c)
{
struct timeval timeout = { 1, 500000 }; // 1.5 seconds
#ifdef _WIN32
c = redisConnectWithTimeout(m_Host.data(), m_Port, timeout);
#else
c = redisConnectUnixWithTimeout(hostname, timeout);
#endif
if (c == NULL || c->err) {
if (c) {
printf("Connection error: %s\n", c->errstr);
redisFree(c);
}
else {
printf("Connection error: can't allocate redis context\n");
}
return false;
}
if (m_PassWord.length()>0)
{
redisReply *reply = (redisReply *)redisCommand(c, "AUTH %s ", m_PassWord.data());
bool ret = GetRedisReply(reply);
if (ret)
{
freeReplyObject(reply);
}
else
{
redisFree(c);
return false;
}
}
return true;
}
RedisNode& MyRedisPool::GetConnect(bool& isSuccess)
{
std::lock_guard<std::mutex> lck(m_Lock);
bool used = false;
isSuccess = false;
for (size_t i = 0; i<m_RedisVec.size(); i++)
{
RedisNode& node = m_RedisVec[i];
if (!node.isUsed)
{
redisReply *reply = (redisReply *)redisCommand(node.c, "PING");
bool ret = GetRedisReply(reply);
if (ret)
{
std::lock_guard<std::mutex> lck(m_PrintLock);
printf("PING: %s\n", reply->str);
freeReplyObject(reply);
node.isUsed = true;
isSuccess = true;
return node;
}
else
{
redisFree(node.c);
m_RedisVec.erase(std::begin(m_RedisVec) + i);
}
}
}
if (m_RedisVec.size()<=0)
{
AddConnect();
for (size_t i = 0; i < m_RedisVec.size(); i++)
{
RedisNode& node = m_RedisVec[i];
if (!node.isUsed)
{
redisReply *reply = (redisReply *)redisCommand(node.c, "PING");
bool ret = GetRedisReply(reply);
if (ret)
{
std::lock_guard<std::mutex> lck(m_PrintLock);
printf("PING: %s\n", reply->str);
freeReplyObject(reply);
node.isUsed = true;
isSuccess = true;
return node;
}
else
{
redisFree(node.c);
m_RedisVec.erase(std::begin(m_RedisVec) + i);
}
}
}
}
return (RedisNode&)isSuccess;
}
bool MyRedisPool::InitConnect(std::string const& host, std::string const& password,int port,int count)
{
m_RedisVec.clear();
m_Host = host;
m_PassWord = password;
m_Port = port;
m_MaxConnects = count;
struct timeval timeout = { 1, 500000 }; // 1.5 seconds
for (size_t i = 0; i < count; i++)
{
RedisNode node;
#ifdef _WIN32
node.c = redisConnectWithTimeout(m_Host.data(), m_Port, timeout);
#else
node.c = redisConnectUnixWithTimeout(hostname, timeout);
#endif
if (node.c == NULL || node.c->err) {
if (node.c) {
printf("Connection error: %s\n", node.c->errstr);
redisFree(node.c);
}
else {
printf("Connection error: can't allocate redis context\n");
}
return false;
}
if (m_PassWord.length()>0)
{
redisReply *reply = (redisReply *)redisCommand(node.c, "AUTH %s ", m_PassWord.data());
bool ret = GetRedisReply(reply);
if (ret)
{
freeReplyObject(reply);
}
else
{
redisFree(node.c);
continue;
}
}
node.id = i;
node.isUsed = false;
m_RedisVec.push_back(node);
m_CurConnects++;
m_QueueId.push(i);
}
}
MyRedisPool& MyRedisPool::GetInstance()
{
static MyRedisPool _instance;
return _instance;
}
#endif // _MY_REDIS_POOL_HPP_