设计 c++ web 框架时候,想要一个框架缓存类,很多通用缓存类是用字符保存,作为框架内置就不要序列和反序列了,因为框架内部使用。
想给自己的paozhu c++ web 框架添加缓存类,参考了springboot 于是确定用单例设计模式缓存类模板。
c++11后静态变量已经统一为线程安全了,网络各种茴香豆几种吃法现在变成一种安全吃法。
因为框架时候了多线程,也要求最低c++20,所以直接使用新标准单例模式。
因为需要保存多种类型,于是设计为模版接口,这样一个通用设计 缓存模型想好了,然后就是设计类库API,需要兼容数组和单一对象。
也要有超时,于是我们确定了基础结构
1 2 3 4 5 | struct data_cache_t { std::vector unsigned int exptime = 0; }; |
因为我想以后还要动态库也能使用,于是用了一个静态函数做单例
1 2 3 4 5 6 | template < typename BASETYPE_T> std::map { static std::map return instance; } |
模版类需要兼顾数组和单个对象于是统一保存为vector数组,然后套入map对象,因为我们要用size_t做hash键值,这样方便统一长度。
然后根据不同api返回不同类型。
先看详细代码,后面讲一个map插入失败情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | template < typename BASE_TYPE> class pzcache { private : pzcache(){}; ~pzcache(){}; pzcache( const pzcache &); pzcache &operator=( const pzcache &); public : struct data_cache_t { std::vector unsigned int exptime = 0; }; public : void save(std:: size_t hashid, BASE_TYPE &data_list, int expnum = 0, bool cover_data = false ) { std::map 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 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 int expnum = 0, bool cover_data = false ) { std::map struct data_cache_t temp; temp.data = data_list; if (expnum != 0) { temp.exptime = http::timeid() + expnum; } else { temp.exptime = 0; } std::unique_lock 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::unique_lock auto iter = obj.find(hashid); if (iter != obj.end()) { obj.erase(iter++); return true ; } return false ; } void remove_exptime() { std::map unsigned int nowtime = http::timeid(); std::unique_lock 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::unique_lock obj.clear(); } int check(std:: size_t hashid) { std::map unsigned int nowtime = http::timeid(); std::unique_lock 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 unsigned int nowtime = http::timeid() + exptime; if (exptime == 0) { nowtime = 0; } std::unique_lock 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 size_t hashid) { std::map unsigned int nowtime = http::timeid(); std::unique_lock 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 return temp; } BASE_TYPE get(std:: size_t hashid) { std::map unsigned int nowtime = http::timeid(); std::unique_lock 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以上。
然后使用方式就是很简单了
1 | pzcache |
我们缓存一个string 对象,首先取得单例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | pzcache "testname" ; std:: size_t cache_hashid = std::hash 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 << "缓存新的内容" ; } |
然后我们在其它线程使用
1 2 3 4 5 6 | pzcache std::string namestring = "testname" ; std:: size_t cache_hashid = std::hash std::string cache_data = temp_cache.get(cache_hashid); |
是不是很简单,c++ 强大的模板能力,一个通用类库设计好了,而且简单好用
欢迎使用 国产 C++ web 框架 paozhu 1.2.0 发布
源代码里面更多的设计模式可以参考,框架LICENSE反正为MIT模式,大家商用也没有问题。
https://github.com/hggq/paozhu