本文基于ACL_CPP,提供对Redis操作的封装。ACL_CPP是一个C++库,用于与Redis进行交互。它提供了一组类和函数,用于执行各种Redis命令和操作。该库基于BSD许可证,具有良好的跨平台支持,可用于多种操作系统和编译器。
特性:
- ACL_CPP:
- 提供了全面的Redis命令支持
- 支持多种数据类型(字符串、列表、集合等)
- 支持事务和管道操作
- 支持发布/订阅模式
- 提供连接池和负载均衡功能
优点:
- ACL_CPP:
- 使用C++语言,符合面向对象的设计理念
- 提供了很多的高级功能,如事务和管道操作
- 支持丰富的数据类型和Redis命令
- 具有良好的跨平台支持
缺点:
- ACL_CPP:
- 学习曲线可能较陡峭,需要熟悉C++语言和Redis命令
#ifdef _WIN32
#include <WinSock2.h>
#endif
#include <chrono>
#include <thread>
#include <condition_variable>
#include <mutex>
#include <list>
#include <vector>
#include <future>
#include <iostream>
#include <unordered_map>
#include "acl_cpp/lib_acl.hpp"
#include "lib_acl.h"
#define MAX_CONNECTION_NUM (5)
enum class RedisCmdType {
RedisCmdType_SET = 1,
RedisCmdType_GET,
RedisCmdType_INCR,
RedisCmdType_HGET,
RedisCmdType_HSET,
RedisCmdType_HSETFV,
RedisCmdType_HSETFVSYNC,
RedisCmdType_HDEL,
RedisCmdType_EXPIRE,
RedisCmdType_SNDLIST,
RedisCmdType_RECVLIST,
RedisCmdType_RECVLISTNOWAIT,
RedisCmdType_GETLISTSIZE
};
// 封装类 RedisCache
class RedisCache
{
public:
static void Enable() {
LocalRedis::inst = new LocalRedis();
}
static int GetListSize(const string &key) {
return LocalRedis::instance()->getListSize(key);
}
static void SendList(const string &key, const string &buffer, int timeOut) {
LocalRedis::instance()->sendList(key, buffer, timeOut);
}
static string RecvList(const string &key, int t) {
return LocalRedis::instance()->recvList(key, t);
}
static string RecvList(const string &key) {
return LocalRedis::instance()->recvList(key);
}
static string Get(const string &key) {
return LocalRedis::instance()->get(key);
}
static std::map<string, string> Hget(const string &key) {
return LocalRedis::instance()->hget(key);
}
static void Set(const string &key, const string &value) {
LocalRedis::instance()->set(key, value);
}
static void Hset(const string &key, const string &field, const string &value, int timeOut) {
LocalRedis::instance()->hset(key, field, value, timeOut);
}
static void Hset(const string &key, std::map<string, string> &fv, int timeOut) {
LocalRedis::instance()->hset(key, fv, timeOut);
}
static void HsetSync(const string &key, std::map<string, string> &fv, int timeOut) {
LocalRedis::instance()->hsetSync(key, fv, timeOut);
}
static void Hdel(const string &key, const string &field) {
LocalRedis::instance()->hdel(key, field);
}
static void Expire(const string &key, int timeOut) {
LocalRedis::instance()->expire(key, timeOut);
}
static int Incr(const string &key, int begin, int end) {
return LocalRedis::instance()->incr(key, begin, end);
}
};
class ClientTh {
public:
ClientTh(acl::redis_client_cluster& cluster, LocalRedis::Queue &queue)
: cluster_(cluster)
, queue_(queue) {
thread_ = new std::thread(std::bind(&ClientTh::MainLoop, this));
}
~ClientTh() {
if (thread_) {
delete thread_;
}
}
private:
void MainLoop() {
while(1) {
while(1) {
auto msg = queue_.get();
try {
acl::redis cmd;
cmd.set_cluster(&cluster_, MAX_CONNECTION_NUM);
switch (msg.cmd) {
case RedisCmdType_SET:
cmd.set(msg.key.c_str(), msg.value.c_str());
break;
case RedisCmdType_GET:
acl::string result;
cmd.get(msg.key.c_str(), result);
msg.prom->set_value(result.c_str());
break;
case RedisCmdType_INCR:
long long result = 0;
cmd.incr(msg.key.c_str(), &result);
msg.prom->set_value(std::to_string(result));
break;
case RedisCmdType_HGET:
std::map<acl::string, acl::string> result;
cmd.hgetall(msg.key.c_str(), result);
msg.prom->set_value(std::to_string(result));
break;
case RedisCmdType_HSET:
cmd.hset(msg.key.c_str(), msg.field.c_str(), msg.value.c_str());
if (0 != msg.t) {
cmd.expire(msg.key.c_str(), msg.t);
}
break;
case RedisCmdType_HSETFV:
std::map<acl::string, acl::string> fv;
for (auto iter = msg.fv.begin(); iter != msg.fv.end(); ++iter) {
fv[iter->first.c_str()] = iter->second.c_str();
}
cmd.hmset(msg.key.c_str(), fv);
if (0 != msg.t) {
cmd.expire(msg.key.c_str(), msg.t);
}
break;
case RedisCmdType_HSETFVSYNC:
std::map<acl::string, acl::string> fv;
for (auto iter = msg.fv.begin(); iter != msg.fv.end(); ++iter) {
fv[iter->first.c_str()] = iter->second.c_str();
}
cmd.hmset(msg.key.c_str(), fv);
if (0 != msg.t) {
cmd.expire(msg.key.c_str(), msg.t);
}
msg.prom->set_value("");
break;
case RedisCmdType_HDEL:
cmd.hdel(msg.key.c_str(), msg.field.c_str());
break;
case RedisCmdType_EXPIRE:
if (0 != msg.t) {
cmd.expire(msg.key.c_str(), msg.t);
}
break;
case RedisCmdType_SNDLIST:
cmd.lpush(msg.key.c_str(), msg.value.c_str(), NULL);
if (0 != msg.t) {
cmd.expire(msg.key.c_str(), msg.t);
}
break;
case RedisCmdType_RECVLIST:
acl::string result;
if (0 != msg.t) {
cmd.rpop(msg.key.c_str(), result);
}else {
std::pair<acl::string, acl::string> result2;
cmd.brpop(result2, msg.t, msg.key.c_str(), NULL);
result = result2.second;
}
msg.prom->set_value(result.c_str());
break;
case RedisCmdType_RECVLISTNOWAIT:
acl::string result;
cmd.rpop(msg.key.c_str(), result);
msg.prom->set_value(result.c_str());
break;
case RedisCmdType_GETLISTSIZE:
int result = cmd.llen(msg.key.c_str());
msg.prom->set_value(std::to_string(result));
break;
default:
break;
}
} catch (std::exception e) {
queue_.setFront(msg);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
break;
} catch (...) {
queue_.setFront(msg);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
break;
}
}
}
}
private:
std::thread *thread_;
int port_;
string host_;
string password_;
acl::redis_client_cluster& cluster_;
LocalRedis::Queue &queue_;
};
class ClientMgr {
public:
ClientMgr(const string &host, const int &port, const string &password, LocalRedis::Queue &queue)
: host_(host)
, port_(port)
, password_(password)
, queue_(queue) {
cluster_.set((host_ + ":" + std::to_string(port_)).c_str(), MAX_CONNECTION_NUM, 10, 10);
if ("" != password_) {
cluster.set_password("default", password_.c_str());
}
for (int i = 0; MAX_CONNECTION_NUM > i; ++i) {
clients_.push_back(createClient());
}
}
private:
ClientTh* createClient() {
std::unique_ptr<ClientTh> client(new ClientTh(cluster_, queue_));
return client.release();
}
private:
std::mutex mutex_;
std::list<ClientTh*> clients_;
string host_;
int port_;
string password_;
LocalRedis::Queue &queue_;
acl::redis_client_cluster cluster_;
};
class Queue {
public:
struct Msg {
RedisCmdType cmd;
string key;
string field;
string value;
std::map<string, string> fv;
int t;
std::shared_ptr<std::promise<string>> prom;
};
public:
Msg get();
void hset(const string &key, const string &field, const string &value, int timeOut);
void hset(const string &key, const std::map<string, string> &fv, int timeOut);
void expire(const string &key, int timeOut);
void set(const RedisCmdType &cmd, const string &key, const string &value, int timeOut);
void set(const RedisCmdType &cmd, const string &key, std::shared_ptr<std::promise<string> > prom, int t);
void set(const Msg &m);
void setFront(const Msg &m);
private:
std::list<Msg> buffers;
std::condition_variable cv;
std::mutex mutex;
std::mutex mutex2;
};
class LocalRedis
{
friend class RedisCache;
public:
LocalRedis();
~LocalRedis();
static LocalRedis *instance() {
return inst;
}
int getListSize(const string &key);
void sendList(const string &key, const string &buffer, int timeOut);
string recvList(const string &key, int t);
string recvList(const string &key);
string get(const string &key);
std::map<string, string> hget(const string &key);
void set(const string &key, const string &value);
void hset(const string &key, const string &field, const string &value, int timeOut);
void hset(const string &key, const map<string, string> &fv, int timeOut);
void hsetSync(const string &key, const map<string, string> &fv, int timeOut);
void hdel(const string &key, const string &field);
void expire(const string &key, int timeOut);
int incr(const string &key, int begin, int end);
private:
ClientMgr *ClientMgr;
Queue write_queue_;
std::condition_variable cv;
std::mutex mutex;
static LocalRedis *inst;
};
LocalRedis *LocalRedis::inst;
LocalRedis::Queue::Msg LocalRedis::Queue::get() {
Msg msg;
while (1) {
{
std::unique_lock<std::mutex> lock(mutex2);
if (buffers.empty() == false) {
msg = buffers.front();
buffers.pop_front();
return msg;
}
}
{
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock);
}
}
}
void LocalRedis::Queue::hset(const string &key, const string &field, const string &value, int timeOut) {
Msg msg;
msg.cmd = RedisCmdType_HSET;
msg.key = key;
msg.field = field;
msg.value = value;
msg.t = timeOut;
{
std::unique_lock<std::mutex> lock(mutex2);
buffers.push_back(msg);
}
cv.notify_one();
}
void LocalRedis::Queue::hset(const string &key, const map<string, string> &fv, int timeOut) {
Msg msg;
msg.cmd = RedisCmdType_HSET;
msg.key = key;
msg.fv = fv;
msg.t = timeOut;
{
std::unique_lock<std::mutex> lock(mutex2);
buffers.push_back(msg);
}
cv.notify_one();
}
void LocalRedis::Queue::expire(const string &key, int timeOut) {
Msg msg;
msg.cmd = RedisCmdType_EXPIRE;
msg.key = key;
msg.t = timeOut;
{
std::unique_lock<std::mutex> lock(mutex2);
buffers.push_back(msg);
}
cv.notify_one();
}
void LocalRedis::Queue::set(const RedisCmdType &cmd, const string &key, const string &value, int timeOut) {
Msg msg;
msg.cmd = cmd;
msg.key = key;
msg.value = value;
msg.t = timeOut;
{
std::unique_lock<std::mutex> lock(mutex2);
buffers.push_back(msg);
}
cv.notify_one();
}
void LocalRedis::Queue::set(const RedisCmdType &cmd, const string &key, std::shared_ptr<std::promise<string> > prom, int t) {
Msg msg;
msg.cmd = cmd;
msg.key = key;
msg.prom = prom;
msg.t = t;
{
std::unique_lock<std::mutex> lock(mutex2);
buffers.push_back(msg);
}
cv.notify_one();
}
void LocalRedis::Queue::set(const Msg &msg) {
{
std::unique_lock<std::mutex> lock(mutex2);
buffers.push_back(msg);
}
cv.notify_one();
}
void LocalRedis::Queue::setFront(const Msg &m) {
{
std::unique_lock<std::mutex> lock(mutex2);
buffers.push_front(m);
}
cv.notify_one();
}
LocalRedis::LocalRedis() {
acl::acl_cpp_init();
acl::log::stdout_open(true);
client_mgr_ = new ClientMgr("127.0.0.1", 6379, "", write_queue_);
}
LocalRedis::~LocalRedis() {
if (nullptr != client_mgr_) {
delete client_mgr_;
}
}
void LocalRedis::sendList(const string &key, const string &buffer, int timeOut) {
write_queue_.set(RedisCmdType_SNDLIST, key, buffer, timeOut);
}
string LocalRedis::recvList(const string &key, int t) {
std::shared_ptr<std::promise<string> > prom(new std::promise<string>());
auto future = prom->get_future();
write_queue_.set(RedisCmdType_RECVLIST, key, prom, t);
int waitTime = max(t + 5, 5);
if (std::future_status::ready != future.wait_for(std::chrono::seconds(waitTime))) {
return "";
}
return future.get();
}
string LocalRedis::recvList(const string &key) {
std::shared_ptr<std::promise<string> > prom(new std::promise<string>());
auto future = prom->get_future();
write_queue_.set(RedisCmdType_RECVLIST, key, prom, 0);
if (std::future_status::ready != future.wait_for(std::chrono::seconds(5))) {
return "";
}
return future.get();
}
void LocalRedis::set(const string &key, const string &value) {
write_queue_.set(RedisCmdType_SET, key, value, 0);
}
void LocalRedis::hset(const string &key, const std::map<string, string> &fv, int timeOut) {
write_queue_.hset(key, fv, timeOut);
}
void LocalRedis::hset(const string &key, const string &field, const string &value, int timeOut) {
write_queue_.hset(key, field, value, timeOut);
}
void LocalRedis::hsetSync(const string &key, const std::map<string, string> &fv, int timeOut) {
std::shared_ptr<std::promise<string>> prom(new std::promise<string>());
auto future = prom->get_future();
Queue::Msg msg;
msg.cmd = RedisCmdType_HSETFVSYNC;
msg.key = key;
msg.fv = fv;
msg.prom = prom;
msg.t = 0;
write_queue_.set(msg);
future.wait_for(std::chrono::seconds(5));
}
void LocalRedis::hdel(const string &key, const string &field) {
Queue::Msg msg;
msg.cmd = RedisCmdType_HDEL;
msg.key = key;
msg.field = field;
msg.t = 0;
write_queue_.set(msg);
}
void LocalRedis::expire(const string &key, int timeOut) {
write_queue_.expire(key, timeOut);
}
int LocalRedis::incr(const string &key, int begin, int end) {
std::shared_ptr<std::promise<string>> prom(new std::promise<string>());
auto future = prom->get_future();
Queue::Msg msg;
msg.cmd = RedisCmdType_INCR;
msg.key = key;
msg.field = std::to_string(begin);
msg.value = std::to_string(end);
msg.prom = prom;
msg.t = 0;
write_queue_.set(RedisCmdType_INCR, key, prom, 0);
if (std::future_status::ready != future.wait_for(std::chrono::seconds(5))) {
return begin;
}
unsigned int ret = std::atoi(future.get().c_str());
ret = begin + (ret % (end - begin));
return ret;
}
string LocalRedis::get(const string &key) {
std::shared_ptr<std::promise<string>> prom(new std::promise<string>());
auto future = prom->get_future();
write_queue_.set(RedisCmdType_GET, key, prom, 0);
if (std::future_status::ready != future.wait_for(std::chrono::seconds(5))) {
return "";
}
return future.get();
}
map<string, string> LocalRedis::hget(const string &key) {
map<string, string> ret;
std::shared_ptr<std::promise<string>> prom(new std::promise<string>());
auto future = prom->get_future();
write_queue_.set(RedisCmdType_HGET, key, prom, 0);
if (std::future_status::ready != future.wait_for(std::chrono::seconds(5))) {
return ret;
}
string retStr = future.get();
if (false == retStr.empty()) {
// todo
}
return ret;
}
int LocalRedis::getListSize(const string &key) {
std::shared_ptr<std::promise<string>> prom(new std::promise<string>());
auto future = prom->get_future();
write_queue_.set(RedisCmdType_GETLISTSIZE, key, prom, 0);
if (std::future_status::ready != future.wait_for(std::chrono::seconds(5))) {
return 0;
}
return atoi(future.get().c_str());
}