陈天 · Rust 编程第一课
陈天
Tubi TV 研发副总裁
23195 人已学习
新⼈⾸单¥68
登录后,你可以任选4讲全文学习
课程目录
已完结/共 65 讲
基础篇 (21讲)
陈天 · Rust 编程第一课
15
15
1.0x
00:00/00:00
登录|注册

36|阶段实操(4):构建一个简单的KV server-网络处理

decode_frame()
encode_frame()
消息长度
\r\n
tarpaulin
使用LengthDelimitedCodec
完整的命令行程序
Frame设计
cargo-tarpaulin
src/client.rs
src/server.rs
单元测试
read_frame()
DummyStream
recv()
send()
execute()
recv()
send()
process()
依赖添加
使用方法
FrameCoder trait
分隔符
延伸阅读
思考题
测试覆盖率
kv-client
kv-server
测试
ProstClientStream
ProstServerStream
LengthDelimitedCodec
Frame设计
网络开发

该思维导图由 AI 生成,仅供参考

你好,我是陈天。
经历了基础篇和进阶篇中两讲的构建和优化,到现在,我们的 KV server 核心功能已经比较完善了。不知道你有没有注意,之前一直在使用一个神秘的 async-prost 库,我们神奇地完成了 TCP frame 的封包和解包。是怎么完成的呢?
async-prost 是我仿照 Jonhoo 的 async-bincode 做的一个处理 protobuf frame 的库,它可以和各种网络协议适配,包括 TCP / WebSocket / HTTP2 等。由于考虑通用性,它的抽象级别比较高,用了大量的泛型参数,主流程如下图所示:
主要的思路就是在序列化数据的时候,添加一个头部来提供 frame 的长度,反序列化的时候,先读出头部,获得长度,再读取相应的数据。感兴趣的同学可以去看代码,这里就不展开了。
今天我们的挑战就是,在上一次完成的 KV server 的基础上,来试着不依赖 async-prost,自己处理封包和解包的逻辑。如果你掌握了这个能力,配合 protobuf,就可以设计出任何可以承载实际业务的协议了。

如何定义协议的 Frame?

protobuf 帮我们解决了协议消息如何定义的问题,然而一个消息和另一个消息之间如何区分,是个伤脑筋的事情。我们需要定义合适的分隔符。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入介绍了如何构建一个简单的KV server,并重点讲解了网络处理的相关内容。作者首先介绍了async-prost库的使用,然后挑战读者在不依赖async-prost的情况下,自己处理封包和解包的逻辑。接着讲解了如何定义协议的Frame,以及使用tokio-util库来处理和frame相关的封包解包的主要需求。最后给出了一个示例代码,展示了如何使用tokio-util库来构建一个简单的KV server。通过本文的学习,读者可以了解到如何处理网络通信中的封包和解包逻辑,以及如何使用tokio-util库来简化网络处理的代码。文章还介绍了如何处理gzip压缩以及如何实现Frame的序列化和反序列化,并通过测试验证了其正确性。文章内容涉及网络通信、异步处理和数据结构实现等方面,对于想要深入了解网络开发的读者来说,具有很高的参考价值。文章还提出了一些思考题,引导读者深入思考和探讨相关技术问题。同时,还介绍了如何在生产环境中运行的代码,都要求至少有80%以上的测试覆盖率,并提出了一些关于测试覆盖率的建议。整体而言,本文内容丰富,涵盖了网络开发的多个方面,对于想要深入了解Rust下网络开发的读者具有很高的参考价值。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《陈天 · Rust 编程第一课》
新⼈⾸单¥68
立即购买
登录 后留言

全部留言(6)

  • 最新
  • 精选
  • 罗杰
    越来越接近实际工作了,老师特别用心,目前没找到网络这块讲解这么详细的内容了。

    作者回复: :)

    2021-11-23
    2
  • 荒野林克
    老师,代码里当 frame 刚好是 2G 时,按理说应该已经越界了吧?

    作者回复: 对,确实如此。多谢指正。

    2021-12-17
    1
  • Rex Wang
    GitHub代码里36_kv/src/error.rs中,KvError去掉了PartialEq属性宏,这是因为std::io::Error不支持binary操作符。 为了保证之前的test依然有效,可以自己定义一个IoError替换原文中KvError中的std::io::Error,手动实现impl From<std::io::Error> for KvError。
    2022-08-19归属地:北京
    4
  • 乌龙猹
    内容夯实 思路清晰 结构完整 循序渐进 每周都期待着老师更新课程内容
    2021-11-22
    3
  • 进击的Lancelot
    思考题 1: 如果要压缩方式需要同时支持 gzip、lz4、zstd 这三种,则需要 2bit 的标记位,00 表示不压缩、01 表示 gzip、10 表示 lz4、11 表示 zstd,同样也是提取出一个 compressor 的 trait 并针对不同的压缩算法实现相应的 compress 和 decompress 方法,具体可以参考我的代码仓库:https://github.com/Phoenix500526/simple_kv/blob/main/src/network/compress 下的文件 思考题 2: 我采用了 shellfish 实现了 simple-kv-cli,代码可以参考:https://github.com/Phoenix500526/simple_kv/blob/main/src/kvc-cli.rs
    2022-10-13归属地:广东
    2
  • sonald
    ``` /// 从 stream 中读取一个完整的 frame pub async fn read_frame<S>(stream: &mut S, buf: &mut BytesMut) -> Result<(), KvError> where S: AsyncRead + Unpin + Send, { let header = stream.read_u32().await? as usize; let (len, _compressed) = decode_header(header); // 如果没有这么大的内存,就分配至少一个 frame 的内存,保证它可用 buf.reserve(LEN_LEN + len); buf.put_u32(header as _); // advance_mut 是 unsafe 的原因是,从当前位置 pos 到 pos + len, // 这段内存目前没有初始化。我们就是为了 reserve 这段内存,然后从 stream // 里读取,读取完,它就是初始化的。所以,我们这么用是安全的 unsafe { buf.advance_mut(len) }; stream.read_exact(&mut buf[LEN_LEN..]).await?; Ok(()) } ``` 这里最后面的read_exact参数是有问题的吧,假设连续两次调用read_frame而没有消费buf的话,应该改成下面这样? ``` let start = len - size; stream.read_exact(&mut buf[start..]).await?; ```
    2023-01-17归属地:北京
收起评论
显示
设置
留言
6
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部