Background
Redis client与Redis server在默认情况下的通讯,是不加密的。而且他们之间的通讯基于TCP。
因此,我们可以通过Wireshark来获取它们之间的通讯内容。
Redis server: 192.168.184:6379
Redis client: 192.168.31.169
Experiment
当client在设置一个key后:
192.168.31.184:6379> set sw_test_key4 sw_test_value4
OK
client (192.168.31.169) 向 server(192.168.184)发送一个TCP包(序号为762):
随后,server返回 +OK,并附上ACK flag(表示上一个client发过来的TCP包收到了),对应序号为831的包:
client再发送一个包含ACK flag的TCP 包,以表示自己收到了刚才的831包。
至此,整个通讯过程结束。
RESP(REdis Serialization Protocol)
Redis使用 RESP(REdis Serialization Protocol)协议进行通讯。
这意味着,client需要将一个操作序列化成byte[](在上面的例子中,TCP包(序号为762)中的body内容,就是将上面的set操作序列化后的byte[])。
Data Types
In RESP, the type of some data depends on the first byte:
- For Simple Strings the first byte of the reply is “+”
- For Errors the first byte of the reply is “-”
- For Integers the first byte of the reply is “:”
- For Bulk Strings the first byte of the reply is “$”
- For Arrays the first byte of the reply is “
*
”
RESP Arrays
RESP Arrays are sent using the following format:
- A
*
character as the first byte, followed by the number of elements in the array as a decimal number, followed by CRLF. - An additional RESP type for every element of the Array.
So an empty Array is just the following:
"*0\r\n"
While an array of two RESP Bulk Strings “foo” and “bar” is encoded as:
"*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n"
As you can see after the *CRLF
part prefixing the array, the other data types composing the array are just concatenated one after the other. For example an Array of three integers is encoded as follows:
"*3\r\n:1\r\n:2\r\n:3\r\n"
RESP Bulk Strings
Bulk Strings are used in order to represent a single binary safe string up to 512 MB in length.
Bulk Strings are encoded in the following way:
- A “$” byte followed by the number of bytes composing the string (a prefixed length), terminated by CRLF.
- The actual string data.
- A final CRLF.
So the string “foobar” is encoded as follows:
"$6\r\nfoobar\r\n"
When an empty string is just:
"$0\r\n\r\n"
RESP Bulk Strings can also be used in order to signal non-existence of a value using a special format that is used to represent a Null value. In this special format the length is -1, and there is no data, so a Null is represented as:
"$-1\r\n"
This is called a Null Bulk String.
The client library API should not return an empty string, but a nil object, when the server replies with a Null Bulk String. For example a Ruby library should return ’nil’ while a C library should return NULL (or set a special flag in the reply object), and so forth.
Sending commands to a Redis Server
A client sends the Redis server a RESP Array consisting of just Bulk Strings.
我们把上面的TCP包(序号为762)中的body内容使用ASCII解码,这可以让我们更直观的了解RESP:
因为client发给server的总是一个RESP Array,因此上面的第一个字符是 *
,3表示这个RESP Array有3个元素。
# 这是一个RESP Array,共有3个元素
*3
# 下面是一个 RESP Bulk Strings,字节长度为3
$3
set
# 下面是一个 RESP Bulk Strings,字节长度为12
$12
sw_test_key4
# 下面是一个 RESP Bulk Strings,字节长度为12
$14
sw_test_value4
Sending a command‘s answer to a Redis Client
192.168.31.184:6379> get sw_test_key4
"sw_test_value4"
server的返回内容:
其实也和上面的例子类似:
# $表示这是一个Bulk Strings,长度为12个byte[]
$14
sw_test_value4
如果我们读取一个包含中文的value,中文会被UTF-8编码:
192.168.31.184:6379> set sw_test_key5 测试
OK
192.168.31.184:6379> get sw_test_key5
"\xe6\xb5\x8b\xe8\xaf\x95"