設計 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