![]()
![]()
DCache 是一个基于 TARS 框架开发的分布式 NoSQL 存储系统,支持多种数据结构,包括了 key-value(键值对),k-k-row(多键值),list(列表),set(集合),zset(有序集合)等,满足多种业务需求。
![]()
list 即链表,常用于消息的排列,比如 QQ、微信的聊天消息排序。常用的有单向链表和双向链表,由若干链表节点组成,如下图。
![]()
单向链表,每个节点存储该节点的数据和下一个节点的地址;双向链表的每个节点则额外包含上一个节点的地址。
DCache 中的 List 模块是基于双向链表实现的,每一个内存数据单元 Block 包含两个指针,分别指向前一个 Block 和后一个 Block。DCache 还提供了操作 list 中某一段的 API,你可以直接查询、删除、替换和裁剪 list 中某一段的元素。如下图为双向链表的结构及替换操作的原理。
![]()
同样地,与其它模块相似,我们完成以下步骤即可在服务中使用 list 缓存服务
-
-
-
-
本文将继续基于 TestDemo 介绍如何创建 List 缓存模块,以及怎么在 TARS 服务中调用该服务来缓存数据。
本文使用的示例可以在 GitHub 仓库 DCacheDemo(文末附链接) 中查看。
![]()
前面的文章我们已经介绍过缓存模块的创建,各类型缓存模块创建流程是相似的,这部分不再赘述过程类似,这里命名为 TestDemoList , cache 类型 选择 List(MKVCache)
![]()
![]()
我们提到过,DCache 是基于 TARS 开发的,因此使用上和 TARS 服务一样,也是通过 .tars 接口文件来调用对应缓存服务的接口。不同的是,DCache 的接口文件是固定的,我们只需复制 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 ,新增一个模块名 ModuleTestDemoList ,值为我们前面创建的模块名 TestDemoList ,用于之后通过代理调用该模块,如下
![]()
![]()
![]()
通过 TestDemo 代理服务的代理对象和模块名 TestDemoList ,我们就能够调用前面创建的 List 缓存模块的接口了。本部分将通过简单示例,介绍 list 类型缓存模块部分接口的使用。关于其它接口的信息,参见 Proxy 接口指南(文末附链接)。
![]()
那么接下来,我们来看看怎么使用 DCache 的 List 缓存模块。
List模块读写操作
List 模块即列表缓存模块。这里介绍写接口 pushList 和读接口 getList,其它接口用法类似。
向列表插入数据
接口 pushList 用于向列表头部或者末尾插入数据,定义如下
int pushList(const PushListReq &req)
其中结构 PushList 定义如下
struct PushListReq{ 1 require string moduleName; // 模块名 2 require string mainKey; // 主key 3 require vector<InsertKeyValue> data; // 待插入数据 4 require bool atHead = true; // true 表示插入到 list 头部,false 表示插入尾部};
使用示例如下
void testPushList(const string &mainKey, const vector<map<string, string>> &data, DCache::ProxyPrx prx){ // 构造请求 DCache::PushListReq req; req.moduleName = ModuleTestDemoList; req.mainKey = mainKey; req.atHead = false;
for(auto item : data){ DCache::InsertKeyValue insertValue; insertValue.mainKey = mainKey; insertValue.expireTimeSecond = 60* 60* 24; map<string, string>::const_iterator it = item.begin(); while(it != item.end()) { // 构建 UpdateValue insertValue.mpValue[it->first] = genUpdateValue(DCache::SET, it->second); ++it; } req.data.push_back(insertValue); } prx->pushList(req);}
获取列表数据
接口 getList 用于根据指定的主 key 和索引查询列表的数据,定义如下
int getList(constGetListReq&req, GetListRsp&rsp)
其中请求消息结构 GetListReq 和返回消息结构 GetListRsp 及其嵌套结构 Entry 的定义如下
struct GetListReq{ 1 require string moduleName; // 模块名 2 require string mainKey; // 主key 3 require string field; // 需要查询的字段集,多个字段用','分隔如 "a,b", "*"表示所有 4 require long index; //索引 5 require string idcSpecified = ""; //idc区域};struct GetListRsp{ 1 require Entry entry; // 查询结果}; struct Entry{ 1 require map<string, string> data;};
使用示例如下
void testGetList(const string &mainKey, constlong &index, DCache::ProxyPrx prx){// 构造请求 DCache::GetListReq req; req.moduleName = ModuleTestDemoList; req.mainKey = mainKey; req.field = "*"; req.index = index;
DCache::GetListRsp rsp; prx->getList(req, rsp);
// 打印返回值 printMapData(rsp.entry.data);}
实例
我们来实际运行一下上面的使用示例。完整的使用示例可以在 GitHub 仓库 DCacheDemo (文末附链接)中获取。
我们通过 testList 测试上节提到的两个 List 读写接口,我们向主键为 test 的列表中插入一个值 test,如下
void testList(DCache::ProxyPrx prx){ cout << START << " testList"<< endl;
string mainKey = "test"; vector<map<string, string>> data; map<string, string> item; item["VALUE"] = "test"; data.push_back(item); testPushList(mainKey, data, prx); long index = 0; testGetList(mainKey, index, prx);
cout << END<< " testList"<< endl;}
接着,在 main 函数中执行
int main(int argc, char*argv[]){ ... auto prx = comm->stringToProxy<DCache::ProxyPrx>(DCacheTestDemoObj); // 调用 DCache 缓存服务 testList(prx); ...}
![]()
除了获取列表数据接口 getList 和读取键值接口 pushList,DCache 中还提供了丰富的 List 操作接口,包括批量插入(insertMKVBatch), 删除(delMKV), 更新(updateMKV) 等,如下
// 获取列表上指定的某一数据int getList(GetListReq req, out GetListRsp rsp);// 获取列表上指定范围的数据int getRangeList(GetRangeListReq req, out BatchEntry rsp);// 向列表头部或尾部插入数据int pushList(PushListReq req);// 弹出列表头部或尾部的数据int popList(PopListReq req, out PopListRsp rsp);// 替换列表中的数据int replaceList(ReplaceListReq req);// 裁剪列表,只保留指定区间,删除区间外的数据int trimList(TrimListReq req);// 从列表头部或者尾部删除一条或多条数据int remList(RemListReq req);
接口的使用方式与前面介绍的 getList 和 pushList 是类似的,关于接口的具体入参和出参结构可以参考 Proxy 接口指南。
![]()
本文简要介绍了 DCache 中的 list 缓存模块的原理和使用流程,同时通过具体实例对部分接口的使用进行了详细介绍,帮助读者理解并能够快速上手使用 list 缓存模块。
文中链接:
DCacheDemo:
https://github.com/ETZhangSX/DCacheDemo
CacheShare.tars:
https://github.com/Tencent/DCache/blob/master/src/TarsComm/CacheShare.tars
ProxyShare.tars:
https://github.com/Tencent/DCache/blob/master/src/TarsComm/ProxyShare.tars
Proxy.tars:
https://github.com/Tencent/DCache/blob/master/src/Proxy/Proxy.tars
Proxy 接口指南:
https://github.com/Tencent/DCache/blob/master/docs/proxy_api_guide.md
TARS基金会是Linux基金会下的非营利性、微服务基金会,致力于建设一个强大而灵活的微服务生态系统。无论你在哪个行业,无论你使用什么技术栈,这里能助你快速实现你的创意。
点“在看”让TARS小姐姐变好看![]()