英文:
How come the redis-benchmark command is not following the redis protocol?
问题
我正在直接从运行redis-benchmark
命令后的TCP连接中读取数据,据我所知,redis-benchmark
并没有遵循Redis协议。
Redis协议在其网站上的说明如下:
> RESP在Redis中作为请求-响应协议的使用方式如下:
>
> - 客户端将命令作为RESP数组的批量字符串发送给Redis服务器。
> - 服务器根据命令的实现情况回复一个RESP类型。
这意味着正确的客户端实现必须始终发送RESP数组的批量字符串。
如果这是真的,那么任何不以*开头的内容都被视为语法错误(因为它不是RESP数组)。
因此,如果要向Redis服务器发送ping命令,它必须作为长度为1的RESP数组发送,其中包含一个批量字符串,该字符串包含单词ping。例如:
> "*1\r\n$4\r\nPING\r\n"
然而,当我直接监听redis-benchmark命令并读取其TCP连接时,我得到的是:
> "PING\r\n"
这不符合Redis协议。这是一个bug还是Redis协议中有一些隐含的特殊情况?据我所知,我找不到任何关于ping命令特殊或长度为1的命令特殊的说明。有人知道发生了什么吗?
要自己复现这些结果,你可以复制我的代码进行直接检查:
package main
import (
"fmt"
"log"
"net"
)
func main() {
RedisBenchmark()
}
func RedisBenchmark() {
url := "127.0.0.1:6379"
fmt.Println("listen: ", url)
ln, err := net.Listen("tcp", url) //在本地网络上进行通告
if err != nil {
log.Fatal(err)
}
for {
conn, err := ln.Accept() //等待并返回到侦听器的下一个连接
if err != nil {
log.Fatal(err)
}
tcpConn := conn.(*net.TCPConn)
go HandleConnection(tcpConn)
}
}
func HandleConnection(tcpConn *net.TCPConn) {
b := make([]byte, 256) //TODO 一次应该读取多少字节?
n, err := tcpConn.Read(b)
if err != nil {
fmt.Println("n: ", n)
log.Fatal(err)
}
fmt.Printf("+++++> 原始输入字符串(b):%q\n", string(b))
msg := string(b[:n])
fmt.Printf("+++++> 原始输入消息:%q\n", msg)
}
使用以下命令在go中运行它:
go run main.go
然后在另一个终端(或tmux窗格)中运行:
redis-benchmark
如果你只想使用一个客户端运行ping命令:
redis-benchmark -c 1 -t ping -n 1
你可以在这里查看我使用的标志的详细信息:http://redis.io/topics/benchmarks
英文:
I was reading in directly from a tcp connection after running the redis-benchmark
command and as far as I can tell, redis-benchmark
is NOT following the redis protocol.
The redis protocol is as stated in its website:
> The way RESP is used in Redis as a request-response protocol is the
> following:
>
> - Clients send commands to a Redis server as a RESP Array of Bulk Strings.
> - The server replies with one of the RESP types according to the command implementation.
Meaning that a correct client implementation must always send RESP arrays of bulk strings.
If that is true, then, anything that does not start with a * is considered a syntax error (since its not an RESP array).
Thus, if one were to send a ping command to a redis-server, then it must be sent as a resp array of length 1 with 1 bulk string containing the word ping. For example:
> "*1\r\n$4\r\nPING\r\n"
However, whenever I listen directly to the redis-benchmark command and read its tcp connection I get instead:
> "PING\r\n"
which does not follow the redis protocol. Is that a bug or is there something implied in the redis protocol that makes pings special? As far as I could tell I couldn't find anything that said that pings were special, nor that length 1 commands were special. Does someone know whats going on?
To see reproduce these results yourself you can copy my code to inspect it directly:
package main
import (
"fmt"
"log"
"net"
)
func main() {
RedisBenchmark()
}
func RedisBenchmark() {
url := "127.0.0.1:6379"
fmt.Println("listen: ", url)
ln, err := net.Listen("tcp", url) //announces on local network
if err != nil {
log.Fatal(err)
}
for {
conn, err := ln.Accept() //waits and returns the next connection to the listener
if err != nil {
log.Fatal(err)
}
tcpConn := conn.(*net.TCPConn)
go HandleConnection(tcpConn)
}
}
func HandleConnection(tcpConn *net.TCPConn) {
b := make([]byte, 256) //TODO how much should I read at a time?
n, err := tcpConn.Read(b)
if err != nil {
fmt.Println("n: ", n)
log.Fatal(err)
}
fmt.Printf("+++++> raw input string(b): %q\n", string(b))
msg := string(b[:n])
fmt.Printf("+++++> raw input msg: %q\n", msg)
}
and run it using go with:
go run main.go
followed on a different terminal (or tmux pane):
redis-benchmark
for all the test or if you only want to run ping with 1 client:
redis-benchmark -c 1 -t ping -n 1
you can see the details of how I am running it with the flags at: http://redis.io/topics/benchmarks
答案1
得分: 5
那被称为内联命令。请查看Redis协议文章中的内联命令部分。
英文:
That is called an inline command. Check the Inline Commands section of the Redis Protocol article.
答案2
得分: 0
你可以参考源代码来了解内联命令和RESP之间的区别。
readQueryFromClient
|--> 如果命令以 * 开头 --> processInlineBuffer()将其处理为RESP
|
|--> 如果命令不以 * 开头 --> processMultibulkBuffer():将其处理为内联命令
RESP是解析Redis服务器命令的一种更高效的方式。
英文:
You can refer to the source code to find out the differences between inline command and RESP.
readQueryFromClient
|--> if command begins with * --> processInlineBuffer()process it as RESP
|
|--> if command not begins with * --> processMultibulkBuffer():process it as inline command
RESP is a more efficent way to parse the command for the Redis Server
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论