携手创作,共同成长!这是我参与「掘金日新计划 · 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 编译相关的依赖包括:prost 和 prost-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.rs 和 client.rs。
server 的代码来自于课程中的示例,主要思路是:
- 通过 tokio 中的 TcpListener 监听本机的9527端口,
- 在无限循环中接受客户端的链接
- 当客户端连接后,使用 tokio::spawn 创建新的线程来处理客户端链接
client 的代码来自于课程 github,主要思路是:
- 通过 tokio 中的 TcpStream 链接到本地 9527 端口
- 生成一个 HSET 命令,发送给服务端,等待响应
好了,到此为止,我们所有的要素都准备好了,可以尝试运行一下了。运行效果如下图所示:
本节课程链接: