做者 | 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/
或扫码获取: