ResPool.h
#ifndef XG_RESPOOL_H
#define XG_RESPOOL_H
#include "stdafx.h"
#include "typedef.h"
#include <ctime>
#include <mutex>
#include <vector>
#include <string>
#include <memory>
#include <thread>
#include <sstream>
#include <iostream>
#include <iterator>
#include <typeinfo>
#include <algorithm>
#include <functional>
using namespace std;
template<typename T> class ResPool
{
class Data
{
public:
int num;
time_t utime;
shared_ptr<T> data;
shared_ptr<T> get()
{
utime = time(NULL);
num++;
return data;
}
Data(shared_ptr<T> data)
{
update(data);
}
void update(shared_ptr<T> data)
{
this->num = 0;
this->data = data;
this->utime = time(NULL);
}
};
protected:
mutex mtx;
int maxlen;
int timeout;
vector<Data> vec;
function<shared_ptr<T>()> func;
public:
shared_ptr<T> get()
{
if (timeout <= 0) return func();
auto grasp = [&](){
int len = 0;
int idx = -1;
shared_ptr<T> tmp;
time_t now = time(NULL);
mtx.lock();
len = vec.size();
for (int i = 0; i < len; i++)
{
Data& item = vec[i];
if (item.data.get() == NULL || item.data.use_count() == 1)
{
if (tmp = item.data)
{
if (item.num < 100 && item.utime + timeout > now)
{
shared_ptr<T> data = item.get();
mtx.unlock();
return data;
}
item.data = NULL;
}
idx = i;
}
}
mtx.unlock();
if (idx < 0)
{
if (len >= maxlen) return shared_ptr<T>();
shared_ptr<T> data = func();
if (data.get() == NULL) return data;
mtx.lock();
if (vec.size() < maxlen) vec.push_back(data);
mtx.unlock();
return data;
}
shared_ptr<T> data = func();
if (data.get() == NULL) return data;
mtx.lock();
vec[idx].update(data);
mtx.unlock();
return data;
};
shared_ptr<T> data = grasp();//调用上面的函数
if (data) return data;
time_t endtime = time(NULL) + 3;
while (true)
{
Sleep(10);
if (data = grasp()) return data;
if (endtime < time(NULL)) break;
}
return data;
}
void clear()
{
lock_guard<mutex> lk(mtx);
vec.clear();
}
int getLength() const
{
return maxlen;
}
int getTimeout() const
{
return timeout;
}
void disable(shared_ptr<T> data)
{
lock_guard<mutex> lk(mtx);
for (Data& item : vec)
{
if (data == item.data)
{
item.data = NULL;
break;
}
}
}
void setLength(int maxlen)
{
lock_guard<mutex> lk(mtx);
this->maxlen = maxlen;
if (vec.size() > maxlen) vec.clear();
}
void setTimeout(int timeout)
{
lock_guard<mutex> lk(mtx);
this->timeout = timeout;
if (timeout <= 0) vec.clear();
}
void setCreator(function<shared_ptr<T>()> func)
{
lock_guard<mutex> lk(mtx);
this->func = func;
this->vec.clear();
}
ResPool(int maxlen = 8, int timeout = 60)
{
this->timeout = timeout;
this->maxlen = maxlen;
}
ResPool(function<shared_ptr<T>()> func, int maxlen = 8, int timeout = 60)
{
this->timeout = timeout;
this->maxlen = maxlen;
this->func = func;
}
};
#endif
RedisConnect.h
#ifndef REDIS_CONNECT_H
#define REDIS_CONNECT_H
#include "stdafx.h"
#include "ResPool.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#pragma comment(lib, "Ws2_32.lib")
using namespace std;
class RedisConnect
{
typedef std::mutex Mutex;
typedef std::lock_guard<mutex> Locker;
friend class Command;
public:
static const int OK;
static const int FAIL;
static const int IOERR;
static const int SYSERR;
static const int NETERR ;
static const int TIMEOUT;
static const int DATAERR ;
static const int SYSBUSY ;
static const int PARAMERR ;
static const int NOTFOUND ;
static const int NETCLOSE ;
static const int NETDELAY ;
static const int AUTHFAIL ;
static int POOL_MAXLEN;
static int SOCKET_TIMEOUT;
public:
class Socket
{
protected:
SOCKET sock_ = INVALID_SOCKET;
public:
static bool IsSocketTimeout();
static void SocketClose(SOCKET sock);
static bool IsSocketClosed(SOCKET sock);
static bool SocketSetSendTimeout(SOCKET sock, int timeout);
static bool SocketSetRecvTimeout(SOCKET sock, int timeout);
SOCKET SocketConnectTimeout(const char* ip, int port, int timeout);
public:
void close();
bool isClosed() const;
bool setSendTimeout(int timeout);
bool setRecvTimeout(int timeout);
bool connect(const string& ip, int port, int timeout);
public:
int tcpWrite(const void* data, int count);
int tcpRead(void* data, int count, bool completed);
};
class Command
{
friend RedisConnect;
protected:
int status_;
string msg_;
vector<string> res_;
vector<string> vec_;
protected:
int parse(const char* msg, int len);
const char* parseNode(const char* msg, int len);
public:
Command();
Command(const string& cmd);
void add(const char* val);
void add(const string& val);
template<class DATA_TYPE> void add(DATA_TYPE val);
template<class DATA_TYPE, class ...ARGS> void add(DATA_TYPE val, ARGS ...args);
public:
string toString() const;
string get(int idx) const;
const vector<string>& getDataList() const;
int getResult(RedisConnect* redis, int timeout);
};
protected:
int code_ = 0;
int port_ = 0;
int memsz_ = 0;
int status_ = 0;
int timeout_ = 0;
char* buffer_ = NULL;
string msg_;
string host_;
Socket sock_;
string passwd_;
public:
~RedisConnect();
int getStatus() const;
int getErrorCode() const;
string getErrorString() const;
void close();
bool reconnect();
int execute(Command& cmd);
template<class DATA_TYPE, class ...ARGS> int execute(DATA_TYPE val, ARGS ...args);
template<class DATA_TYPE, class ...ARGS> int execute(vector<string>& vec, DATA_TYPE val, ARGS ...args);
bool connect(const string& host, int port, int timeout = 3000, int memsz = 2 * 1024 * 1024);
public:
int ping();
int del(const string& key);
int ttl(const string& key);
int hlen(const string& key);
int auth(const string& passwd);
int get(const string& key, string& val);
int decr(const string& key, int val = 1);
int incr(const string& key, int val = 1);
int expire(const string& key, int timeout);
int keys(vector<string>& vec, const string& key);
int hdel(const string& key, const string& filed);
int hget(const string& key, const string& filed, string& val);
int set(const string& key, const string& val, int timeout = 0);
int hset(const string& key, const string& filed, const string& val);
int pop(const string& key, string& val);
int lpop(const string& key, string& val);
int rpop(const string& key, string& val);
int push(const string& key, const string& val);
int lpush(const string& key, const string& val);
int rpush(const string& key, const string& val);
int range(vector<string>& vec, const string& key, int start, int end);
int lrange(vector<string>& vec, const string& key, int start, int end);
int zrem(const string& key, const string& filed);
int zadd(const string& key, const string& filed, int score);
int zrange(vector<string>& vec, const string& key, int start, int end, bool withscore = false);
string get(const string& key);
string hget(const string& key, const string& filed);
protected:
virtual shared_ptr<RedisConnect> grasp() const;
public:
static bool CanUse();
static RedisConnect* GetTemplate();
static void SetMaxConnCount(int maxlen);
static shared_ptr<RedisConnect> Instance();
static void Setup(const string& host, int port, const string& passwd = "", int timeout = 3000, int memsz = 2 * 1024 * 1024 );
};
#endif
RedisConnect.cpp
#include "stdafx.h"
#include "ResPool.h"
#include "RedisConnect.h"
using namespace std;
int RedisConnect::POOL_MAXLEN = 8;
int RedisConnect::SOCKET_TIMEOUT = 10;
const int RedisConnect::OK = 1;
const int RedisConnect::FAIL = -1;
const int RedisConnect::IOERR = -2;
const int RedisConnect::SYSERR = -3;
const int RedisConnect::NETERR = -4;
const int RedisConnect::TIMEOUT = -5;
const int RedisConnect::DATAERR = -6;
const int RedisConnect::SYSBUSY = -7;
const int RedisConnect::PARAMERR = -8;
const int RedisConnect::NOTFOUND = -9;
const int RedisConnect::NETCLOSE = -10;
const int RedisConnect::NETDELAY = -11;
const int RedisConnect::AUTHFAIL = -12;
bool RedisConnect::Socket::IsSocketTimeout()
{
return WSAGetLastError() == WSAETIMEDOUT;
}
void RedisConnect::Socket::SocketClose(SOCKET sock)
{
if (IsSocketClosed(sock)) return;
closesocket(sock);
}
bool RedisConnect::Socket::IsSocketClosed(SOCKET sock)
{
return sock == INVALID_SOCKET || sock < 0;
}
bool RedisConnect::Socket::SocketSetSendTimeout(SOCKET sock, int timeout)
{
return setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char*)(&timeout), sizeof(timeout)) == 0;
}
bool RedisConnect::Socket::SocketSetRecvTimeout(SOCKET sock, int timeout)
{
return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*)(&timeout), sizeof(timeout)) == 0;
}
SOCKET RedisConnect::Socket::SocketConnectTimeout(const char* ip, int port, int timeout)
{
u_long mode = 1;
struct sockaddr_in addr;
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
if (IsSocketClosed(sock)) return INVALID_SOCKET;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip);
ioctlsocket(sock, FIONBIO, &mode); mode = 0;
if (::connect(sock, (struct sockaddr*)(&addr), sizeof(addr)) == 0)
{
ioctlsocket(sock, FIONBIO, &mode);
return sock;
}
struct timeval tv;
fd_set ws;
FD_ZERO(&ws);
FD_SET(sock, &ws);
tv.tv_sec = timeout / 1000;
tv.tv_usec = timeout % 1000 * 1000;
if (select(sock + 1, NULL, &ws, NULL, &tv) > 0)
{
int res = ERROR;
int len = sizeof(res);
getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)(&res), &len);
ioctlsocket(sock, FIONBIO, &mode);
if (res == 0) return sock;
}
SocketClose(sock);
return INVALID_SOCKET;
}
void RedisConnect::Socket::close()
{
SocketClose(sock_);
sock_ = INVALID_SOCKET;
}
bool RedisConnect::Socket::isClosed() const
{
return IsSocketClosed(sock_);
}
bool RedisConnect::Socket::setSendTimeout(int timeout)
{
return SocketSetSendTimeout(sock_, timeout);
}
bool RedisConnect::Socket::setRecvTimeout(int timeout)
{
return SocketSetRecvTimeout(sock_, timeout);
}
bool RedisConnect::Socket::connect(const string& ip, int port, int timeout)
{
close();
sock_ = SocketConnectTimeout(ip.c_str(), port, timeout);
return IsSocketClosed(sock_) ? false : true;
}
int RedisConnect::Socket::tcpWrite(const void* data, int count)
{
const char* str = (const char*)(data);
int num = 0;
int times = 0;
int writed = 0;
while (writed < count)
{
if ((num = send(sock_, str + writed, count - writed, 0)) > 0)
{
if (num > 8)
{
times = 0;
}
else
{
if (++times > 100) return TIMEOUT;
}
writed += num;
}
else
{
if (IsSocketTimeout())
{
if (++times > 100) return TIMEOUT;
continue;
}
return NETERR;
}
}
return writed;
}
int RedisConnect::Socket::tcpRead(void* data, int count, bool completed)
{
char* str = (char*)(data);
if (completed)
{
int num = 0;
int times = 0;
int readed = 0;
while (readed < count)
{
if ((num = recv(sock_, str + readed, count - readed, 0)) > 0)
{
if (num > 8)
{
times = 0;
}
else
{
if (++times > 100) return TIMEOUT;//超时接受
}
readed += num;
}
else if (num == 0)
{
return NETCLOSE;
}
else
{
if (IsSocketTimeout())
{
if (++times > 100) return TIMEOUT;
continue;
}
return NETERR;
}
}
return readed;
}
else
{
int val = recv(sock_, str, count, 0);
if (val > 0) return val;
if (val == 0) return NETCLOSE;
if (IsSocketTimeout()) return 0;
return NETERR;
}
}
int RedisConnect::Command:: parse(const char* msg, int len)
{
if (*msg == '$')
{
const char* end = parseNode(msg, len);
if (end == NULL) return DATAERR;
switch (end - msg)
{
case 0: return TIMEOUT;
case -1: return NOTFOUND;
}
return OK;
}
const char* str = msg + 1;
const char* end = strstr(str, "\r\n");
if (end == NULL) return TIMEOUT;
if (*msg == '+' || *msg == '-' || *msg == ':')
{
status_ = OK;
msg = string(str, end).c_str();
if (*msg == '+') return OK;
if (*msg == '-') return FAIL;
status_ = atoi(str);
return OK;
}
if (*msg == '*')
{
int cnt = atoi(str);
const char* tail = msg + len;
vec_.clear();
str = end + 2;
while (cnt > 0)
{
if (*str == '*') return parse(str, tail - str);
end = parseNode(str, tail - str);
if (end == NULL) return DATAERR;
if (end == str) return TIMEOUT;
str = end;
cnt--;
}
return res_.size();
}
return DATAERR;
}
const char* RedisConnect::Command:: parseNode(const char* msg, int len)
{
const char* str = msg + 1;
const char* end = strstr(str, "\r\n");
if (end == NULL) return msg;
int sz = atoi(str);
if (sz < 0) return msg + sz;
str = end + 2;
end = str + sz + 2;
if (msg + len < end) return msg;
res_.push_back(string(str, str + sz));
return end;
}
RedisConnect::Command::Command()
{
status_ = 0;
}
RedisConnect::Command::Command(const string& cmd)
{
vec_.push_back(cmd);
status_ = 0;
}
void RedisConnect::Command::add(const char* val)
{
vec_.push_back(val);
}
void RedisConnect::Command::add(const string& val)
{
vec_.push_back(val);
}
template<class DATA_TYPE>
void RedisConnect::Command:: add(DATA_TYPE val)
{
add(to_string(val));
}
template<class DATA_TYPE, class ...ARGS>
void RedisConnect::Command::add(DATA_TYPE val, ARGS ...args)
{
add(val);
add(args...);
}
string RedisConnect::Command::toString() const
{
ostringstream out;
out << "*" << vec_.size() << "\r\n";
for (const string& item : vec_)
{
out << "$" << item.length() << "\r\n" << item << "\r\n";
}
return out.str();
}
string RedisConnect::Command::get(int idx) const
{
return res_.at(idx);
}
const vector<string>& RedisConnect::Command::getDataList() const
{
return res_;
}
int RedisConnect::Command::getResult(RedisConnect* redis, int timeout)
{
auto doWork = [&]() {
string msg = toString();
Socket& sock = redis->sock_;
if (sock.tcpWrite(msg.c_str(), msg.length()) < 0) return NETERR;
int len = 0;
int delay = 0;
int readed = 0;
char* dest = redis->buffer_;
const int maxsz = redis->memsz_;
while (readed < maxsz)
{
if ((len = sock.tcpRead(dest + readed, maxsz - readed, false)) < 0) return len;
if (len == 0)
{
delay += SOCKET_TIMEOUT;
if (delay > timeout) return TIMEOUT;
}
else
{
dest[readed += len] = 0;
if ((len = parse(dest, readed)) == TIMEOUT)
{
delay = 0;
}
else
{
return len;
}
}
}
return PARAMERR;
};
status_ = 0;
msg_.clear();
redis->code_ = doWork();
if (redis->code_ < 0 && msg_.empty())
{
switch (redis->code_)
{
case SYSERR:
msg_ = "system error";
break;
case NETERR:
msg_ = "network error";
break;
case DATAERR:
msg_ = "protocol error";
break;
case TIMEOUT:
msg_ = "response timeout";
break;
case NOTFOUND:
msg_ = "element not found";
break;
default:
msg_ = "unknown error";
break;
}
}
redis->status_ = status_;
redis->msg_ = msg_;
return redis->code_;
}
RedisConnect::~RedisConnect()
{
close();
}
int RedisConnect::getStatus() const
{
return status_;
}
int RedisConnect::getErrorCode() const
{
if (sock_.isClosed()) return FAIL;
return code_ < 0 ? code_ : 0;
}
string RedisConnect::getErrorString() const
{
return msg_;
}
void RedisConnect::close()
{
if (buffer_)
{
delete[] buffer_;
buffer_ = NULL;
}
sock_.close();
}
bool RedisConnect::reconnect()
{
if (host_.empty()) return false;
return connect(host_, port_, timeout_, memsz_) && auth(passwd_) > 0;
}
int RedisConnect::execute(Command& cmd)
{
return cmd.getResult(this, timeout_);
}
template<class DATA_TYPE, class ...ARGS>
int RedisConnect::execute(DATA_TYPE val, ARGS ...args)
{
Command cmd;
cmd.add(val, args...);
return cmd.getResult(this, timeout_);
}
template<class DATA_TYPE, class ...ARGS>
int RedisConnect::execute(vector<string>& vec, DATA_TYPE val, ARGS ...args)
{
Command cmd;
cmd.add(val, args...);
cmd.getResult(this, timeout_);
if (code_ > 0) std::swap(vec, cmd.res);
return code_;
}
bool RedisConnect::connect(const string& host, int port, int timeout, int memsz)
{
close();
if (sock_.connect(host, port, timeout))
{
sock_.setSendTimeout(SOCKET_TIMEOUT);
sock_.setRecvTimeout(SOCKET_TIMEOUT);
host_ = host;
port_ = port;
memsz_ = memsz;//最大接受字节数
timeout_ = timeout; //超时时间
buffer_ = new char[memsz + 1];
}
return buffer_ ? true : false;
}
int RedisConnect::ping()
{
return execute("ping");
}
int RedisConnect::del(const string& key)
{
return execute("del", key);
}
int RedisConnect::ttl(const string& key)
{
return execute("ttl", key) == OK ? status_ : code_;
}
int RedisConnect::hlen(const string& key)
{
return execute("hlen", key) == OK ? status_ : code_;
}
int RedisConnect::auth(const string& passwd)
{
passwd_ = passwd;
if (passwd_.empty()) return OK;
return execute("auth", passwd_);
}
int RedisConnect::get(const string& key, string& val)
{
vector<string> vec;
if (execute(vec, "get", key) <= 0) return code_;
val = vec[0];
return code_;
}
int RedisConnect::decr(const string& key, int val)
{
return execute("decrby", key, val);
}
int RedisConnect::incr(const string& key, int val )
{
return execute("incrby", key, val);
}
int RedisConnect::expire(const string& key, int timeout)
{
return execute("expire", key, timeout);
}
int RedisConnect::keys(vector<string>& vec, const string& key)
{
return execute(vec, "keys", key);
}
int RedisConnect::hdel(const string& key, const string& filed)
{
return execute("hdel", key, filed);
}
int RedisConnect::hget(const string& key, const string& filed, string& val)
{
vector<string> vec;
if (execute(vec, "hget", key, filed) <= 0) return code_;
val = vec[0];
return code_;
}
int RedisConnect::set(const string& key, const string& val, int timeout )
{
return timeout > 0 ? execute("setex", key, timeout, val) : execute("set", key, val);
}
int RedisConnect::hset(const string& key, const string& filed, const string& val)
{
return execute("hset", key, filed, val);
}
int RedisConnect::pop(const string& key, string& val)
{
return lpop(key, val);
}
int RedisConnect::lpop(const string& key, string& val)
{
vector<string> vec;
if (execute(vec, "lpop", key) <= 0) return code_;
val = vec[0];
return code_;
}
int RedisConnect::rpop(const string& key, string& val)
{
vector<string> vec;
if (execute(vec, "rpop", key) <= 0) return code_;
val = vec[0];
return code_;
}
int RedisConnect::push(const string& key, const string& val)
{
return rpush(key, val);
}
int RedisConnect::lpush(const string& key, const string& val)
{
return execute("lpush", key, val);
}
int RedisConnect::rpush(const string& key, const string& val)
{
return execute("rpush", key, val);
}
int RedisConnect::range(vector<string>& vec, const string& key, int start, int end)
{
return execute(vec, "lrange", key, start, end);
}
int RedisConnect::lrange(vector<string>& vec, const string& key, int start, int end)
{
return execute(vec, "lrange", key, start, end);
}
int RedisConnect::zrem(const string& key, const string& filed)
{
return execute("zrem", key, filed);
}
int RedisConnect::zadd(const string& key, const string& filed, int score)
{
return execute("zadd", key, score, filed);
}
int RedisConnect::zrange(vector<string>& vec, const string& key, int start, int end, bool withscore )
{
return withscore ? execute(vec, "zrange", key, start, end, "withscores") : execute(vec, "zrange", key, start, end);
}
string RedisConnect::get(const string& key)
{
string res;
get(key, res);
return res;
}
string RedisConnect::hget(const string& key, const string& filed)
{
string res;
hget(key, filed, res);
return res;
}
shared_ptr<RedisConnect> RedisConnect::grasp() const
{
static ResPool<RedisConnect> pool(
/*定义一个内部函数*/
[&]() {
shared_ptr<RedisConnect> redis = make_shared<RedisConnect>();//new 新的连接
if (redis && redis->connect(host_, port_, timeout_, memsz_))
{
if (redis->auth(passwd_)) return redis;
}
return redis = NULL;
},
POOL_MAXLEN);
shared_ptr<RedisConnect> redis = pool.get();
if (redis && redis->getErrorCode())
{
pool.disable(redis);
return grasp();//不断循环
}
return redis;
}
bool RedisConnect::CanUse()
{
return GetTemplate()->port_ > 0;
}
RedisConnect* RedisConnect::GetTemplate()
{
static RedisConnect redis;
return &redis;
}
void RedisConnect::SetMaxConnCount(int maxlen)
{
if (maxlen > 0) POOL_MAXLEN = maxlen;
}
shared_ptr<RedisConnect> RedisConnect::Instance()
{
return GetTemplate()->grasp();
}
void RedisConnect::Setup(const string& host, int port, const string& passwd , int timeout , int memsz )
{
WSADATA data; WSAStartup(MAKEWORD(2, 2), &data);
RedisConnect* redis = GetTemplate();
redis->host_ = host;
redis->port_ = port;
redis->memsz_ = memsz;
redis->passwd_ = passwd;
redis->timeout_ = timeout;
}
main.cpp
#include "stdafx.h"
#include "RedisConnect.h"
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main(int argc, char** argv)
{
string val;
RedisConnect::Setup("192.168.31.129", 6379, "");//初始化连接池
shared_ptr<RedisConnect> redis = RedisConnect::Instance();//从连接池中获取一个连接
//redis->set("key", "654321");//设置一个键值
redis->get("val", val);//获取键值内容
std::cout << "ret=" << val << std::endl;
shared_ptr<RedisConnect> redis2 = RedisConnect::Instance();//从连接池中获取一个连接
redis2->get("val", val);//获取键值内容
std::cout << "ret=" << val << std::endl;
shared_ptr<RedisConnect> redis3 = RedisConnect::Instance();//从连接池中获取一个连接
redis3->get("val", val);//获取键值内容
std::cout << "ret=" << val << std::endl;
getchar();
return 0;
}