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

220 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情

今天是实践课程,我们一起跟着大佬来构建一个 KV server。在此之前,我们先来学习下 Rust 中一个 package 典型的目录结构。

01-典型的 package 目录结构

.
├── Cargo.lock
├── Cargo.toml
├── src/
│   ├── lib.rs
│   ├── main.rs
│   └── bin/
│       ├── named-executable.rs
│       ├── another-executable.rs
│       └── multi-file-executable/
│           ├── main.rs
│           └── some_module.rs
├── benches/
│   ├── large-input.rs
│   └── multi-file-bench/
│       ├── main.rs
│       └── bench_module.rs
├── examples/
│   ├── simple.rs
│   └── multi-file-example/
│       ├── main.rs
│       └── ex_module.rs
└── tests/
    ├── some-integration-tests.rs
    └── multi-file-test/
        ├── main.rs
        └── test_module.rs

这是 Cargo 推荐的目录结构,其中主要的目录解释如下:

  • src 是源码目录
  • src/lib.rs 是默认的 lib 包根
  • src/main.rs 是默认的二进制包根,src/bin/ 是其他二进制的包跟
  • tests 是继承测试代码目录
  • benches 是基准测试代码目录
  • examples 是示例代码

Rust package 相关的更多内容,可以参考:[1] crate 和 module [2] cargo target

02-一个短平糙的实现

首先,要实现课程中给出的“短平糙”示例,并能成功运行。包目录结构:

.
├── Cargo.lock
├── Cargo.toml
├── api.proto                        # proto 定义文件
├── build.rs                         # prost_build 脚本,将 proto 编译成 rs 
├── src/
│   ├── lib.rs
│   ├── error.rs
│   └── pb/
│       ├── abi.rs                   # 根据 abi.proto 生成的 rs
│       └── mod.rs
└── examples/
    ├── client.rs
    └── server.rs

接下来,我们将一步步拆分,最终达到能够运行的目的。项目的源码我放在自己的 gitee 上,感兴趣的朋友可以克隆下来运行。

# 以下代码需要在两个 terminal 上执行
cargo run --example server

cargo run --example client
===============> 输出结果:
2022-08-25T05:23:00.668291Z  INFO server: Start listening on 127.0.0.1:9527
2022-08-25T05:23:18.802592Z  INFO server: Client 127.0.0.1:53575 connected
2022-08-25T05:23:18.803378Z  INFO server: Got a new command: CommandRequest { request_data: Some(Hset(Hset { table: "table1", pair: Some(Kvpair { key: "hello", value: Some(Value { value: Some(String("world")) }) }) })) }
2022-08-25T05:23:18.804176Z  INFO server: Got response: CommandResponse { status: 200, message: "", values: [Value { value: None }], pairs: [] }

02.1-初始化项目

可以通过cargo new kv --lib初始化一个项目。下文默认的当前目录是 package 根目录。编辑 Cargo.toml 添加 prost 和 prost-build 依赖。

02.2-编译 proto

在 package 根目录下,添加 abi.proto (proto 定义文件)和 build.rs (prost-build 编译脚本)

与 proto 编译相关的依赖包括:prostprost-build

prost is a Protocol Buffers implementation for the Rust Language. prost-build makes it easy to generate Rust code from .proto files as part of a Cargo build.

如果想了解更多使用 prost-build 的例子,可以参考 [1] Snazzy

运行cargo build会将 abi.proto 文件编译生成 src/pb/abi.rs 文件。

02.3-使用 module

通过 proto 定义并生成的数据结构作为当前 crate 的一个 module,需要在 src/pb 下放一个 mod.rs 引入该模块的其他文件。

// src/pb/mod.rs
pub mod abi;

// src/lib.rs
mod pb; 

pub use pb::abi::*;

mod.rs 与 Python 中的 init.py 有异曲同工之妙。lib.rs 作为库的入口文件(对应的,main.rs 作为可执行项目的入口文件),在它之中可以通过mod pb;将 module 加载进来。

除了加载相关地文件外,mod.rs 中还为 abi.rs 中定义的 struct 实现了一些辅助 trait 和辅助方法。mod.rs 完整内容在 gitee.

因为 mod.rs 中用到了 KvError 类型,所以在 src 目录下添加一个 error.rs

02.4-运行示例

现在到了最后一个环节了,在 examples 下添加 server.rsclient.rs

server 的代码来自于课程中的示例,主要思路是:

  • 通过 tokio 中的 TcpListener 监听本机的9527端口,
  • 在无限循环中接受客户端的链接
  • 当客户端连接后,使用 tokio::spawn 创建新的线程来处理客户端链接

client 的代码来自于课程 github,主要思路是:

  • 通过 tokio 中的 TcpStream 链接到本地 9527 端口
  • 生成一个 HSET 命令,发送给服务端,等待响应

好了,到此为止,我们所有的要素都准备好了,可以尝试运行一下了。运行效果如下图所示:

Untitled.png

本节课程链接:

21|阶段实操(1):构建一个简单的KV server-基本流程

22|阶段实操(2):构建一个简单的KV server-基本流程