写点什么

每日一 R「16」实践课之 kv-server(二)

作者:Samson
  • 2022 年 8 月 26 日
    上海
  • 本文字数:1758 字

    阅读完需:约 6 分钟

每日一R「16」实践课之 kv-server(二)

今天仍然是实践课,目标是基本实现 KV server。让我们开始吧!

01-系统架构

在开始之前,我们先大概了解下课程中要构建的 kv server 的基本架构:



与 Redis 一样,kv server 是 C/S 架构的程序,使用 TCP 连接。Client 与 server 之间的协议通过 proto 定义。Disruptor 负责识别并将 client 发来的命令分发给各个命令处理单元。 Storage 负责键值的存储。


整个系统的接口可以分为三个部分:


  • 客户端和服务器的接口或者说协议(proto,9 个命令)

  • 服务器和命令处理流程的接口(CommandService trait)

  • 服务器和存储的接口(Storage trait)


其中第一部分在昨天的 example 示例中已经学习过了,今天的主要内容是其余的两部分。

02-CommandService

在当前版本里,proto 消息中总共定义了 9 种命令,而且将来非常有可能会扩大命令支持的范围。所以,对于命令处理,很有必要定义一个 trait 来规范命令处理的流程。CommandService trait 的定义如下:


pub trait CommandService {    // 参数 store 为实现了 Storage trait 的结构体引用,    // Storage trait 会在后面学习到,它封装了存储相关的一些接口    fn execute(self, store: &impl Storage) -> CommandResponse;}
复制代码


然后我们来看一个具体的命令(Hget)处理过程实现:


impl CommandService for Hget {    fn execute(self, store: &impl Storage) -> CommandResponse {        // 从 store 中取某个 table 中的某个 key        match store.get(&self.table, &self.key) {            Ok(Some(v)) => v.into(),    // 如果从 table 中取得值            Ok(None) => KvError::NotFound(self.table, self.key).into(),  // 如果 storage 中不包含值            Err(e) => e.into(), // 过程中遇到其他问题        }    }}
复制代码


课程示例代码只实现了 Hget / Hgetall / Hset 命令,其他的实现作为了课后作业。我们自己来实现一下 Hdel 命令(逻辑可能不太完善,无论 storage 中是否包含 key,只要最终结果是 storage 不包含 key 了即返回成功):


impl CommandService for Hdel {    fn execute(self, store: &impl Storage) -> CommandResponse {        match store.del(&self.table, &self.key) {            Ok(_) => CommandResponse { status: StatusCode::OK.as_u16() as _, ..Default::default() },            Err(v) => v.into(),        }    }}
复制代码


按照这个思路,如果我们后面要增加对新命令的支持,也只需要为其实现 CommandService trait 即可。新增的命令可以很平滑地融入之前的代码中。

03-Storage

在前一节的实现中,table 和 key 都是从某个实现了 Storage trait 的结构中获得。Storage trait 是对存储的抽象。


pub trait Storage {    // 从 table 中获取 key 对应的值,可能有、也可能没有、也可能报错,    // 所以返回值是 Result<Option<Value>, KvError>    fn get(&self, table: &str, key: &str) -> Result<Option<Value>, KvError>;    // 向 table 中插入 key : value 对    fn set(&self, table: &str, key: String, value: Value) -> Result<Option<Value>, KvError>;    // 判断 table 中是否包含某个 key    fn contains(&self, table: &str, key: &str) -> Result<bool, KvError>;    // 删除 table 中的某个 key:value 对,    // 返回值可能为有值(key 原来对应的 value)、可能为 None 表示不包含 key、可能为 Err    fn del(&self, table: &str, key: &str) -> Result<Option<Value>, KvError>;    // 返回所有的 kvpair    fn get_all(&self, table: &str) -> Result<Vec<Kvpair>, KvError>;    // 返回 kvpair 的迭代器    fn get_iter(&self, table: &str) -> Result<Box<dyn Iterator<Item = Kvpair>>, KvError>;}
复制代码


我们可以以内存作为存储介质,这样只需要为其实现 Storage trait,它就能与程序集成在一起;当那天需要持久化到磁盘时,可以以文件作为存储介质,再为其实现 Storage trait,程序就可以无缝切换到支持持久化的介质了。同样,如果要存到数据库中,也可以按照这个范式实现。


课程示例中定义了一个基于 DashMap 的数据结构,MemTable。


本节课程链接:《21|阶段实操(1):构建一个简单的KV server-基本流程》《22|阶段实操(2):构建一个简单的KV server-基本流程

发布于: 刚刚阅读数: 4
用户头像

Samson

关注

还未添加个人签名 2019.07.22 加入

还未添加个人简介

评论

发布
暂无评论
每日一R「16」实践课之 kv-server(二)_学习笔记_Samson_InfoQ写作社区