1 个不稳定版本
0.1.0 | 2019年7月19日 |
---|
#1704 in 异步
在 3 个crate 中使用
30KB
606 行
Agilulf KV协议
本协议的设计借鉴了Redis协议。但由于这个KV服务器不提供如此复杂的数据结构,因此协议更为简单。
本协议可用于TCP流。因为它不处理传输错误,也不提供任何正确性的保证(通过校验和或其他技术),因此您必须提供稳定且安全的传输层(TCP在这方面相当出色。也许QUIC或KCP可以提供更好的性能)
由于简洁性是本项目的首要指导原则,此crate仅实现了在TCP流上的序列化和反序列化。但通过此crate中的一些辅助函数(目前是私有的),将此协议绑定到其他传输层并不困难。此crate提供了接口以接受任何AsyncWrite + Unpin
和AsyncRead + Unpin
,并将它们转换为Stream<Command>
和Sink<Reply>
。Agilulf KV服务器旨在使用最新的rust async/await功能,因此使用来自futures
crate的Stream
和Sink
作为接口。
请求-响应模型
服务器对来自客户端的每个请求都做出响应。但是,客户端可以同时发送多个请求并等待它们的响应。在这种情况下,响应的顺序将与请求的顺序一致。
请求
此crate按照以下步骤反序列化请求
1. 检查第一行(以\r\n
结尾)是否为"*"+数字。此数字提供了有关消息中有多少部分的信息。
-
对于每个部分,它以“$”+数字开始。这个数字提供了有关该部分内容长度信息。
-
然后我们得到一个部分列表。可以轻松地从它们中读取命令。
示例
- 简单的PUT请求
*3
$3
PUT
$5
HELLO
$5
WORLD
注意:示例中的每一行都通过\r\n分隔,因此上面的请求实际上是*3\r\n$3\r\nPUT\r\n$5HELLO\r\n$5WORLD
- 简单的GET请求
*2
$3
GET
$5
HELLO
- 删除请求
*2
$6
DELETE
$5
HELLO
- 扫描请求
*3
$4
SCAN
$1
A
$6
AAAAAA
注意
此协议允许将任何二进制存储在内容中(键和值)。因为它为每个部分提供了长度,所以它永远不会解码每个部分的内容,而只是简单地将其复制到RAM中。
响应
响应非常简单。此协议不针对每种请求类型提供响应格式,而只提供一些简单的格式(对于KV服务器来说足够了)。只有四种类型的响应
-
状态。响应将以“+”开头,后跟状态消息。例如,“+OK”表示此请求操作成功。PUT请求和DELETE请求可能会导致此类型的响应。
-
错误。此类型的响应将以“-”开头,后跟错误消息。例如,“-KeyNotFound”表示找不到错误。GET一个不存在的键(或已删除的键)将导致此。
-
一个片段。它以第一行“$”+数字开始。这里的数字告诉客户端片段有多长。然后是响应的内容。例如,一个简单的GET请求可能以“$5\r\nWORLD\r\n”响应。
-
多个片段。它以第一行“*”+数字开始。这个数字告诉客户端它包含多少个片段。然后对于每个片段,协议与单个片段格式相同。例如,SCAN请求可能以“*1\r\n$5\r\nWORLD\r\n”响应。
接口设计
数据流如下所示:TcpStream -> Stream<Command> -> Database -> Sink<Reply> -> TcpSink
。这个crate将流的第一部分和最后一部分。它可以简单地将TcpStream转换为命令流,并将TcpSink转换为回复接收器。
使用此crate,实现Agilulf KV服务器只需要处理核心部分:将每个命令转换为回复(响应每个请求)。
注意:简单地将流传递到接收器将不会工作。需要recv和send循环。很抱歉,我不知道为什么会出现这种情况。当我尝试使用send_all
将它们一起传递时,我注意到我的Sink<Reply>
已经接收并处理了回复,但它从未发送出去。
依赖关系
~5.5MB
~110K SLoC