mini-redis源码分析-概述篇

mini-redis 是一个 Tokio 编程示例性质的项目,其中演示了一些 Tokio 异步编程模式。在进行网络编程时,我们可以参考这些示例使用这些模式。下面我们对 mini-redis 的源码进行分析,并学习其中的异步网络编程模式。并在充分理解的基础上对其进行重新实现。

我们将仓库的实现层次整体绘制为下图所示的架构图。

mini-redis Architecture

mini-redis 整体基于 Tokio 进行开发。在进行日志记录时,使用 Tokio 团队主推的 tracing 作为门面,支持将程序运行中产生的 Metric、Log、Trace 基于 OpenTelemetry 发送到 Prometheus、Jaeger 等可观测方案中。在连接的处理方面,在 Tokio 提供的 TCP 流的基础上封装 Connection 并在这一层实现 Redis 帧到字节流的编解码和收发。在数据的读写方面,mini-redis基于内存实现连接间共享的数据存储,并使用 Mutex 互斥锁进行保护,防止数据竞争问题。在帧结构的基础上,命令模块将若干帧解析为 Redis 命令。Redis 命令在执行时,调用存储模块提供的功能实现数据的读写等动作。为了对外提供统一接口,在命令模块的基础上进行封装,整合为客户端库和服务端库。最终利用客户端库和服务端库的能力,构建客户端和服务端的命令行程序以及端到端的测试模块。

项目创建及日志配置

下面我们开始进行项目的搭建。

  1. 创建 Library 类型的 Rust 项目。完成后我们得到下面的项目结构。其中 lib.rs 包含作为示例的 add 函数和一个测试模块。
1
2
3
4
5
6
.
├── Cargo.lock
├── Cargo.toml
├── src
│   └── lib.rs
└── target
  1. lib.rs 中定义自己的错误类型以供一般路径上使用,使用dyn trait类型作为错误类型可以减少大量转换工作。重新定义结果类型,通过默认的错误类型,简化结果类型的声明。
1
2
3
4
5
/// 应用中普通路径上的错误类型,为了简单起见我们直接使用dyn trait对象。
/// 在热路径上,需要定义枚举类型的错误来提高效率。
pub type Error = Box<dyn std::error::Error + Send + Sync>;
/// 简化项目中的结果的写法,将错误全部转换为dyn trait对象。
pub type Result<T> = std::result::Result<T, Error>;
  1. 下面我们为这个项目引入日志相关的依赖。tracing 提供日志门面,tracing-subscriber 提供日志输出能力。我们暂时不需要考虑将日志写入文件或发送到成熟的日志服务,因此我们不需要引入 opentelemetry 和 opentelemetry 需要的特定实现库。下面的配置步骤也就只需将日志输出到终端即可。
1
2
cargo add tracing
cargo add tracing-subscriber
  1. 在 lib.rs 中定义一个简单的日志初始化函数。将 tracing 输出的日志导出到终端上显示。在测试模块中执行创建测试函数,确认日志功能工作正常。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
fn set_up_logging() -> crate::Result<()> {
// See https://docs.rs/tracing for more info
tracing_subscriber::fmt::try_init()
}
#[cfg(test)]
mod tests {
use tracing::{debug, error, info, Level, span, trace, warn};
use super::*;

#[test]
fn it_works() {
set_up_logging().unwrap();
error!("error level. works");
let span = span!(Level::INFO, "root");
{
let _enter = span.enter();
warn!("warn level. works");
info!("info level. works");
debug!("debug level. works");
trace!("trace level. works");
}
}
}
  1. 运行测试函数,观察终端输出。在运行时可以通过 RUST_LOG=debug 来调整日志输出级别。指定日志级别为 debug 后,终端输出如下图。可以看到 debug 及以上级别日志可以正常输出,且日志级别有高亮显示。同时,在 span 内部的日志输出上都附带了 span 名称 root。

tracing log

总结

这一篇展示了 mini-redis 的模块框架,对其结构进行了简单介绍;定义了项目中的通用错误和结果类型,简化了在后续使用结果类型时的声明工作量;并对项目的日志输出能力进行了配置,使其具备了结构化日志输出能力。下面的几篇中,将对存储模块、帧解析、命令解析、构筑整体工作结构的客户端、服务端库等进行展开介绍。

参考资料

tokio-rs/mini-redis: Incomplete Redis client and server implementation using Tokio - for learning purposes only