共计 7073 个字符,预计需要花费 18 分钟才能阅读完成。
作者 | Eaton
导语 | 随着微服务与云的倒退,分布式架构的需要变得越来越广泛,传统的 SQL 结构化存储计划曾经跟不上脚步,于是 NoSQL 呈现了。DCache 作为基于 TARS 的分布式 NoSQL 缓存零碎,完满反对 TARS 服务。前一篇文章中,咱们介绍了怎么创立并应用 KV 模块,本文将持续介绍如何创立和应用 DCache 中的 K-K-Row 缓存模块。
系列文章
- 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
类型就是一种结构化存储计划。
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/bu…
获取《TARS 官网培训电子书》:https://wj.qq.com/s2/7849909/…
或扫码获取: