设计 c++ web 框架时候,想要一个框架缓存类,很多通用缓存类是用字符保存,作为框架内置就不要序列和反序列了,因为框架内部使用。
想给自己的paozhu c++ web 框架添加缓存类,参考了springboot 于是确定用单例设计模式缓存类模板。
c++11后静态变量已经统一为线程安全了,网络各种茴香豆几种吃法现在变成一种安全吃法。
因为框架时候了多线程,也要求最低c++20,所以直接使用新标准单例模式。
因为需要保存多种类型,于是设计为模版接口,这样一个通用设计 缓存模型想好了,然后就是设计类库API,需要兼容数组和单一对象。
也要有超时,于是我们确定了基础结构
- struct data_cache_t
- {
- std::vector<BASE_TYPE> data;
- unsigned int exptime = 0;
- };
因为我想以后还要动态库也能使用,于是用了一个静态函数做单例
- template <typename BASETYPE_T>
- std::map<std::size_t, BASETYPE_T> &get_pz_cache()
- {
- static std::map<std::size_t, BASETYPE_T> instance;
- return instance;
- }
模版类需要兼顾数组和单个对象于是统一保存为vector数组,然后套入map对象,因为我们要用size_t做hash键值,这样方便统一长度。
然后根据不同api返回不同类型。
先看详细代码,后面讲一个map插入失败情况
- template <typename BASE_TYPE>
- class pzcache
- {
- private:
- pzcache(){};
- ~pzcache(){};
- pzcache(const pzcache &);
- pzcache &operator=(const pzcache &);
-
- public:
- struct data_cache_t
- {
- std::vector<BASE_TYPE> data;
- unsigned int exptime = 0;
- };
-
- public:
- void save(std::size_t hashid, BASE_TYPE &data_list, int expnum = 0, bool cover_data = false)
- {
- std::map<std::size_t, data_cache_t> &obj = get_pz_cache<data_cache_t>();
- struct data_cache_t temp;
- temp.data.push_back(data_list);
- if (expnum != 0)
- {
- temp.exptime = http::timeid() + expnum;
- }
- else
- {
- temp.exptime = 0;
- }
- std::unique_lock<std::mutex> lock(editlock);
- auto [_, success] = obj.insert({hashid, temp});
- if (!success)
- {
- if (cover_data)
- {
- obj[hashid] = temp;
- }
- else
- {
- obj[hashid].exptime = temp.exptime;
- }
- }
- }
- void save(std::size_t hashid, std::vector<BASE_TYPE> &data_list, int expnum = 0, bool cover_data = false)
- {
- std::map<std::size_t, data_cache_t> &obj = get_pz_cache<data_cache_t>();
- struct data_cache_t temp;
- temp.data = data_list;
- if (expnum != 0)
- {
- temp.exptime = http::timeid() + expnum;
- }
- else
- {
- temp.exptime = 0;
- }
- std::unique_lock<std::mutex> lock(editlock);
- auto [_, success] = obj.insert({hashid, temp});
- if (!success)
- {
- if (cover_data)
- {
- obj[hashid] = temp;
- }
- else
- {
- obj[hashid].exptime = temp.exptime;
- }
- }
- }
- bool remove(std::size_t hashid)
- {
- std::map<std::size_t, data_cache_t> &obj = get_pz_cache<data_cache_t>();
- std::unique_lock<std::mutex> lock(editlock);
- auto iter = obj.find(hashid);
- if (iter != obj.end())
- {
- obj.erase(iter++);
- return true;
- }
- return false;
- }
- void remove_exptime()
- {
- std::map<std::size_t, data_cache_t> &obj = get_pz_cache<data_cache_t>();
- unsigned int nowtime = http::timeid();
- std::unique_lock<std::mutex> lock(editlock);
- for (auto iter = obj.begin(); iter != obj.end();)
- {
- if (iter->second.exptime == 0)
- {
- continue;
- }
- if (iter->second.exptime < nowtime)
- {
- obj.erase(iter++);
- }
- }
- }
- void clear()
- {
- std::map<std::size_t, data_cache_t> &obj = get_pz_cache<data_cache_t>();
- std::unique_lock<std::mutex> lock(editlock);
- obj.clear();
- }
- int check(std::size_t hashid)
- {
- std::map<std::size_t, data_cache_t> &obj = get_pz_cache<data_cache_t>();
- unsigned int nowtime = http::timeid();
- std::unique_lock<std::mutex> lock(editlock);
- auto iter = obj.find(hashid);
- if (iter != obj.end())
- {
- if (iter->second.exptime == 0)
- {
- return 0;
- }
- int temp = (int)(iter->second.exptime - nowtime);
- if (temp == -1)
- {
- return -2;
- }
- return temp;
- }
- return -1;
- }
-
- int update(std::size_t hashid, int exptime = 0)
- {
- std::map<std::size_t, data_cache_t> &obj = get_pz_cache<data_cache_t>();
- unsigned int nowtime = http::timeid() + exptime;
- if (exptime == 0)
- {
- nowtime = 0;
- }
- std::unique_lock<std::mutex> lock(editlock);
- auto iter = obj.find(hashid);
- if (iter != obj.end())
- {
- if (iter->second.exptime == 0)
- {
- iter->second.exptime = nowtime;
- return 0;
- }
- iter->second.exptime = nowtime;
- return 1;
- }
- return -1;
- }
- std::vector<BASE_TYPE> get_array(std::size_t hashid)
- {
- std::map<std::size_t, data_cache_t> &obj = get_pz_cache<data_cache_t>();
- unsigned int nowtime = http::timeid();
- std::unique_lock<std::mutex> lock(editlock);
- auto iter = obj.find(hashid);
- if (iter != obj.end())
- {
- if (iter->second.exptime == 0)
- {
- return iter->second.data;
- }
-
- if (iter->second.exptime >= nowtime)
- {
- return iter->second.data;
- }
- else
- {
- obj.erase(iter++);
- }
- }
- lock.unlock();
- std::vector<BASE_TYPE> temp;
- return temp;
- }
- BASE_TYPE get(std::size_t hashid)
- {
- std::map<std::size_t, data_cache_t> &obj = get_pz_cache<data_cache_t>();
- unsigned int nowtime = http::timeid();
- std::unique_lock<std::mutex> lock(editlock);
- auto iter = obj.find(hashid);
- if (iter != obj.end())
- {
- if (iter->second.exptime == 0)
- {
- if (iter->second.data.size() > 0)
- {
- return iter->second.data[0];
- }
- }
-
- if (iter->second.exptime >= nowtime)
- {
- if (iter->second.data.size() > 0)
- {
- return iter->second.data[0];
- }
- }
- else
- {
- obj.erase(iter++);
- }
- }
- lock.unlock();
- BASE_TYPE temp;
- return temp;
- }
- static pzcache &conn()
- {
- static pzcache instance;
- return instance;
- }
-
- public:
- std::mutex editlock;
- };
auto [_, success] = obj.insert({hashid, temp});
这个map insert 方法如果存在会插入失败,于是我用API指定是更新过期时间或删除重新添加,这一步巧妙利用了map这个特性,需要c++17以上。
然后使用方式就是很简单了
- pzcache<std::string> &temp_cache = pzcache<std::string>::conn();
我们缓存一个string 对象,首先取得单例。
- pzcache<std::string> &temp_cache = pzcache<std::string>::conn();
std::string namestring = "testname"; - std::size_t cache_hashid = std::hash<std::string>{}(namestring);
-
- if (temp_cache.check(cache_hashid) > -1)
- {
- client << " 已经存在,不需要再存 ";
- }
- else
- {
- std::string cache_data = "This cache content!";
- temp_cache.save(cache_hashid, cache_data, 30);
-
- client << "缓存新的内容";
- }
然后我们在其它线程使用
- pzcache<std::string> &temp_cache = pzcache<std::string>::conn();
-
- std::string namestring = "testname";
- std::size_t cache_hashid = std::hash<std::string>{}(namestring);
-
- std::string cache_data = temp_cache.get(cache_hashid);
是不是很简单,c++ 强大的模板能力,一个通用类库设计好了,而且简单好用
欢迎使用 国产 C++ web 框架 paozhu 1.2.0 发布
源代码里面更多的设计模式可以参考,框架LICENSE反正为MIT模式,大家商用也没有问题。
https://github.com/hggq/paozhu