做者 | Eatonios
導語 | 隨着微服務與雲的發展,分佈式架構的需求變得愈來愈廣泛,傳統的 SQL 結構化存儲方案已經跟不上腳步,因而 NoSQL 出現了。DCache 做爲基於 TARS 的分佈式 NoSQL 緩存系統,完美支持 TARS 服務。前一篇文章中,咱們介紹了怎麼建立並使用 KV 模塊,本文將繼續介紹如何建立和使用 DCache 中的 K-K-Row 緩存模塊。git
系列文章github
- DCache 分佈式存儲系統|DCache 部署與應用建立
- DCache 分佈式存儲系統|Key-Value 緩存模塊的建立與使用
- DCache 分佈式存儲系統|K-K-Row 緩存模塊的建立與使用
目錄
- K-K-Row 模塊簡介
- 建立 K-K-Row 緩存模塊
- 獲取 DCache 接口文件
- 建立緩存服務代理
- 調用緩存模塊服務
- K-K-Row 模塊讀寫操做
- 運行示例
- 總結
DCache 是一個基於 TARS 框架開發的分佈式 NoSQL 存儲系統,支持多種數據結構,包括了 key-value
(鍵值對),k-k-row
(多鍵值),list
(列表),set
(集合),zset
(有序集合)等,知足多種業務需求。數據庫
咱們在文章 Key-Value 緩存模塊的建立與使用 中介紹了 key-value
類型的使用,也提到了其在結構化數據存儲上的缺點。而 k-k-row
類型就是一種結構化存儲方案。api
K-K-Row 模塊簡介
k-k-row
,與 key-value
類似,但這裏 value 不是字符串,而是至關於一張表,可以存儲結構化數據。k-k-row
即 key key row
,指經過兩個 key
,即主索引/主鍵(Main Key
)和聯合索引(Union Key
),可以惟一肯定一條記錄 row
,以下緩存
不難看出,k-k-row
的存儲結構和 SQL 數據庫很像,主鍵至關於表名,映射到 Value。既不須要重複存儲數據,也不會帶來序列化和併發修改控制的問題,很好的解決了問題。數據結構
與 KV 模塊類似,咱們只需完成如下步驟便可在服務中使用 k-k-row
緩存服務多線程
- 建立 K-K-Row 緩存模塊
- 獲取 DCache 接口文件
- 建立緩存服務代理
- 調用緩存模塊服務
本文將繼續基於 TestDemo
介紹如何建立 K-K-Row 緩存模塊,以及怎麼在 TARS 服務中調用該服務來緩存數據。架構
本文使用的示例能夠在 GitHub 倉庫 DCacheDemo 中查看。併發
建立 K-K-Row 緩存模塊
在文章 Key-Value 緩存模塊的建立與使用 中,咱們已經介紹過如何建立 Key-Value 緩存模塊,各種型緩存模塊建立流程是類似的,這部分再也不贅述,僅介紹不一樣的部分。
這裏咱們將緩存模塊服務命名爲 TestDemoKKRow
,cache 類型
選擇 k-k-row(MKVCache)
,以下
K-K-Row 爲多鍵值類型,配置字段時能夠新增多個聯合索引或數據字段,點擊 添加
,以下
確認好已配置信息後,點擊 安裝發佈
便可完成發佈。
到這裏,咱們就能夠在其它服務中使用該緩存模塊來緩存 K-K-Row 數據了。
獲取 DCache 接口文件
DCache 是基於 TARS 開發的,所以使用上和 TARS 服務同樣,也是經過 .tars
接口文件來調用對應緩存服務的接口。
咱們複製 DCache/src/TarsComm
下的 CacheShare.tars, ProxyShare.tars 和 DCache/src/Proxy
下的 Proxy.tars 到本身項目目錄下便可。
本文 Demo 獲取 DCache 接口文件後的項目文件結構以下
DCacheDemo ├── CacheShare.tars ├── ProxyShare.tars ├── Proxy.tars ├── config.conf ├── main.cpp └── makefile
建立緩存服務代理
前一篇文章咱們提到過,建立一個應用後會自動建立一個路由服務和代理服務,並經過 TestDemo
介紹瞭如何建立緩存服務代理來調用服務。
咱們繼續使用 TestDemo
,新增一個模塊名 ModuleTestDemoKKRow
,值爲咱們前面建立的模塊名 TestDemoKKRow
,用於以後經過代理調用該模塊,以下。
// main.cpp #include <iostream> #include <map> #include "servant/Communicator.h" #include "servant/ServantProxy.h" #include "Proxy.h" using namespace std; using namespace tars; // TestDemo 代理服務對象名 static string DCacheTestDemoObj = "DCache.TestDemoProxyServer.ProxyObj"; // 緩存模塊名 static string ModuleTestDemoKV = "TestDemoKV"; static string ModuleTestDemoKKRow = "TestDemoKKRow"; int main(int argc, char *argv[]) { CommunicatorPtr comm = new Communicator(); try { TC_Config conf; // 解析配置文件 conf.parseFile("config.conf"); // 加載配置 comm->setProperty(conf); // 生成代理 auto prx = comm->stringToProxy<DCache::ProxyPrx>(DCacheTestDemoObj); // TODO: 調用 DCache 緩存服務 } catch (exception &e) { cerr << "error: " << e.what() << endl; } catch (...) { cerr << "Unknown Error" << endl; } }
調用 K-K-Row 緩存模塊服務
經過 TestDemo
代理服務的代理對象和模塊名 TestDemoKKRow
,咱們就可以調用前面建立的K-K-Row 緩存模塊的接口了。
本部分將經過簡單示例,介紹 k-k-row
類型緩存模塊部分接口的使用。關於其它接口的信息,參見 Proxy 接口指南。
接口調用流程與 TARS 服務接口調用流程一致。若是你還不清楚 TARS 服務的調用方式和流程,能夠閱讀文章 TARS RPC 通訊框架|提供多種遠程調用方式 瞭解 TARS 服務的調用方式。
後面的示例中,會使用到三個工具函數,定義以下
// 構建 UpdateValue DCache::UpdateValue genUpdateValue(DCache::Op op, const string &value) { DCache::UpdateValue updateValue; updateValue.op = op; updateValue.value = value; return updateValue; } // 打印 map<string, string> 類型數據 void printMapData(const map<string, string> &data) { map<string, string>::const_iterator it = data.begin(); while (it != data.end()) { cout << "|" << it->first << ":" << it->second; ++it; } cout << endl; } // 打印 vector<map> 數據 void printVectorMapData(const vector<map<string, string>> &data) { for (auto item : data) { printMapData(item); } }
genUpdateValue
用於構建 DCache::UpdateValue
結構,該結構用於存儲插入或更新的數據值,在其它類型模塊的接口中,常常會用到。printMapData
和 printVectorMapData
用於方便打印返回的數據。
那麼接下來,咱們來看看怎麼使用 K-K-Row 緩存模塊。
K-K-Row 模塊讀寫操做
K-K-Row 即多鍵值模塊,一個主鍵能夠對應多條記錄。這裏咱們僅介紹寫接口 insertMKV
和讀接口 getMKV
,其它接口相似。
插入數據
接口 insertMKV
用於插入鍵值對數據,定義以下
int insertMKV(const InsertMKVReq &req)
其中結構 InsertMKVReq
及其嵌套結構 InsertKeyValue
的定義以下
struct InsertMKVReq { 1 require string moduleName; // 模塊名 2 require InsertKeyValue data; // 待寫入數據 }; struct InsertKeyValue { 1 require string mainKey; // 主key 2 require map<string, UpdateValue> mpValue; // 除主key外的其餘字段數據 3 require byte ver = 0; // 版本號 4 require bool dirty = true; // 是否設置爲髒數據 5 require bool replace = false; // 若是記錄已存在且replace爲true時則覆蓋舊記錄 6 require int expireTimeSecond = 0; // 數據過時時間 };
使用示例以下
void testInsertMKV(const string &mainKey, const map<string, string> &data, DCache::ProxyPrx prx) { cout << "\t-- " << "insertMKV "; // 打印準備插入的數據 printMapData(data); // 構造插入數據 DCache::InsertKeyValue insertData; insertData.mainKey = mainKey; map<string, string>::const_iterator it = data.begin(); while (it != data.end()) { // 構造 UpdateValue insertData.mpValue[it->first] = genUpdateValue(DCache::SET, it->second); ++it; } // 構造請求 DCache::InsertMKVReq insertReq; insertReq.moduleName = ModuleTestDemoKKRow; insertReq.data = insertData; prx->insertMKV(insertReq); }
獲取數據
接口 getMKV
用於根據主鍵獲取主鍵對應的鍵值對,定義以下
int getMKV(const GetMKVReq &req, GetMKVRsp &rsp)
請求消息結構 GetMKVReq
及返回消息結構 GetMKVRsp
的定義以下
struct GetMKVReq { 1 require string moduleName; // 模塊名 2 require string mainKey; // 主key 3 require string field; // 須要查詢的字段集,多個字段用','分隔如 "a,b", "*"表示全部 4 require vector<Condition> cond; // 查詢條件集合,除主Key外的其餘字段,多個條件直間爲And關係 5 require bool retEntryCnt = false; // 是否返回主key下的總記錄條數 6 require string idcSpecified = ""; // idc區域 }; struct GetMKVRsp { 1 require vector<map<string, string> > data; //查詢結果 };
使用示例以下
void testGetMKV(const string &key, DCache::ProxyPrx prx) { cout << "\t-- " << "getMKV " << '\n'; // 構造請求 DCache::GetMKVReq req; req.moduleName = ModuleTestDemoKKRow; req.mainKey = key; req.field = "*"; DCache::GetMKVRsp rsp; prx->getMKV(req, rsp); // 打印返回數據 printVectorMapData(rsp.data); }
運行示例
咱們來實際運行一下上面的使用示例。完整的使用示例能夠在 GitHub 倉庫 DCacheDemo 中獲取。
咱們經過 testKKRow
測試上節提到的模塊讀寫接口,咱們向同一主鍵插入兩條記錄,UID
分別爲 test1
, test2
,以下
void testKKRow(DCache::ProxyPrx prx) { cout << START << " testKKRow" << endl; string mainKey = "Key"; map<string, string> data; data["UID"] = "test1"; data["VALUE"] = "hello"; testInsertMKV(mainKey, data, prx); data["UID"] = "test2"; data["VALUE"] = "hey"; testInsertMKV(mainKey, data, prx); testGetMKV(mainKey, prx); cout << END << " testKKRow" << endl; }
接着,在 main
函數中執行
int main(int argc, char *argv[]) { ... auto prx = comm->stringToProxy<DCache::ProxyPrx>(DCacheTestDemoObj); // 調用 DCache 緩存服務 testKKRow(prx); ... }
編譯構建並運行示例,結果以下
能夠看到,getMKV
返回了兩條記錄。以上就是DCache緩存模塊的具體使用流程。到此,咱們成功調用了 DCache 的 K-K-Row 緩存服務。
K-K-Row 緩存模塊服務接口
除了設置鍵值接口 insertMKV
和讀取鍵值接口 getMKV
,DCache 中還提供了豐富的 K-K-Row 操做接口,包括批量插入(insertMKVBatch
), 刪除(delMKV
), 更新(updateMKV
) 等,以下
// 按主key查詢,支持'and'條件匹配 int getMKV(GetMKVReq req, out GetMKVRsp rsp); // 按主key批量數據查詢,給定多個主key,用統一的條件進行匹配查詢 int getMKVBatch(MKVBatchReq req, out MKVBatchRsp rsp); // 按主鍵批量查詢 int getMUKBatch(MUKBatchReq req, out MUKBatchRsp rsp); // 按主key批量查詢,針對每一個主key支持'and','or'複雜條件匹配 int getMKVBatchEx(MKVBatchExReq req, out MKVBatchExRsp rsp); // 獲取主key下的記錄數,返回值爲正數時,爲主key下記錄數 int getMainKeyCount(MainKeyReq req); // 獲取cache中全部的主key,不包含落地db的key int getAllMainKey(GetAllKeysReq req, out GetAllKeysRsp rsp); // 插入一條記錄到Cache int insertMKV(InsertMKVReq req); // 插入批量數據到Cache int insertMKVBatch(InsertMKVBatchReq req, out MKVBatchWriteRsp rsp); // 批量更新接口。只支持指定聯合key的更新 int updateMKVBatch(UpdateMKVBatchReq req, out MKVBatchWriteRsp rsp); // 更新Cache記錄,更新接口不能更新聯合key字段。 int updateMKV(UpdateMKVReq req); // 原子更新接口。適用於對數據作自增自減操做,多線程操做能保證數據原子更新。 int updateMKVAtom(UpdateMKVAtomReq req); // 刪除 Cache記錄 int eraseMKV(MainKeyReq req); // 刪除Cache和Db記錄 int delMKV(DelMKVReq req); // 批量刪除, rsp.rspData中存儲了每一個刪除請求的結果 int delMKVBatch(DelMKVBatchReq req, out MKVBatchWriteRsp rsp);
接口的使用方式與前面介紹的 insertMKV
和 getMKV
是相似的,關於接口的具體入參和出參結構能夠參考 Proxy 接口指南。
總結
本文經過使用示例,介紹了 DCache 中 K-K-Row 緩存模塊的建立和使用方式,知足開發者對結構化緩存數據的需求。
TARS 能夠在考慮到易用性和高性能的同時快速構建系統並自動生成代碼,幫助開發人員和企業以微服務的方式快速構建本身穩定可靠的分佈式應用,從而令開發人員只關注業務邏輯,提升運營效率。多語言、敏捷研發、高可用和高效運營的特性使 TARS 成爲企業級產品。
TARS微服務助您數字化轉型,歡迎訪問:
TARS官網:https://TarsCloud.org
TARS源碼:https://github.com/TarsCloud
Linux基金會官方微服務免費課程:https://www.edx.org/course/building-microservice-platforms-with-tars
獲取《TARS官方培訓電子書》:https://wj.qq.com/s2/7849909/01b0/
或掃碼獲取: