第4章 客户端-客户端通信协议

Redis是用单线程来处理多个客户端的访问,因此作为Redis的开发和运维人员需要了解Redis服务端和客户端的通信协议,以及主流编程语言的Redis客户端使用方法,同时还需要了解客户端管理的相应API以及开发运维中可能遇到的问题。

几乎所有的主流编程语言都有Redis的客户端(http://redis.io/clients),不考虑Redis非常流行的原因,如果站在技术的角度看原因还有两个:第一,客户端与服务端之间的通信协议是在TCP协议之上构建的。第二,Redis制定了RESP(REdis Serialization Protocol,Redis序列化协议)实现客户端与服务端的正常交互,这种协议简单高效,既能够被机器解析,又容易被人类识别。例如客户端发送一条set hello world命令给服务端,按照RESP的标准,客户端需要将其封装为如下格式(每行用\r\n分隔):

powershell 复制代码
*3
$3
SET
$5
hello
$5
world

这样Redis服务端能够按照RESP将其解析为set hello world命令,执行后回复的格式如下:

powershell 复制代码
+OK

可以看到除了命令(set hello world)和返回结果(OK)本身还包含了一些特殊字符以及数字,下面将对这些格式进行说明。

1.发送命令格式

RESP的规定一条命令的格式如下,CRLF代表"\r\n"。

powershell 复制代码
*<参数数量> CRLF
$<参数1的字节数量> CRLF
<参数1> CRLF
...
$<参数N的字节数量> CRLF
<参数N> CRLF

依然以set hell world这条命令进行说明。参数数量为3个,因此第一行为:

powershell 复制代码
*3

参数字节数分别是355,因此后面几行为:

powershell 复制代码
$3
SET
$5
hello
$5
world

有一点要注意的是,上面只是格式化显示的结果,实际传输格式为如下

代码,整个过程如图4-1所示:

powershell 复制代码
*3\r\n$3\r\nSET\r\n$5\r\nhello\r\n$5\r\nworld\r\n

2.返回结果格式

Redis的返回结果类型分为以下五种,如图4-2所示:

  • 状态回复:在RESP中第一个字节为"+"。
  • 错误回复:在RESP中第一个字节为"-"。
  • 整数回复:在RESP中第一个字节为":"。
  • 字符串回复:在RESP中第一个字节为"$"。
  • 多条字符串回复:在RESP中第一个字节为"*"。

    我们知道redis-cli只能看到最终的执行结果,那是因为redis-cli本身就是按照RESP进行结果解析的,所以看不到中间结果,redis-cli.c源码对命令结果的解析结构如下:
powershell 复制代码
static sds cliFormatReplyTTY(redisReply *r, char *prefix) {
	sds out = sdsempty();
	switch (r->type) {
	case REDIS_REPLY_ERROR:
	// 处理错误回复
	case REDIS_REPLY_STATUS:
	// 处理状态回复
	case REDIS_REPLY_INTEGER:
	// 处理整数回复
	case REDIS_REPLY_STRING:
	// 处理字符串回复
	case REDIS_REPLY_NIL:
	// 处理空
	case REDIS_REPLY_ARRAY:
	// 处理多条字符串回复
	return out;
}

为了看到Redis服务端返回的"真正"结果,可以使用nc命令、telnet命令、甚至写一个socket程序进行模拟。下面以nc命令进行演示,首先使用nc127.0.0.16379连接到Redis:

powershell 复制代码
nc 127.0.0.1 6379

状态回复:set hello world的返回结果为+OK:

powershell 复制代码
set hello world
+OK

错误回复:由于sethx这条命令不存在,那么返回结果就是"-"号加上错误消息:

powershell 复制代码
sethx
-ERR unknown command 'sethx'

整数回复:当命令的执行结果是整数时,返回结果就是整数回复,例如incr、exists、del、dbsize返回结果都是整数,例如执行incr counter返回结果就是":"加上整数:

powershell 复制代码
incr counter
:1

字符串回复:当命令的执行结果是字符串时,返回结果就是字符串回复。例如get、hget返回结果都是字符串,例如get hello的结果为"$5\r\nworld\r\n":

powershell 复制代码
get hello
$5
world

多条字符串回复:当命令的执行结果是多条字符串时,返回结果就是多条字符串回复。例如mget、hgetall、lrange等命令会返回多个结果,例如下面操作:首先使用mset设置多个键值对:

powershell 复制代码
mset java jedis python redis-py
+OK

然后执行mget命令返回多个结果,第一个*2代表返回结果的个数,后面的格式是和字符串回复一致的:

powershell 复制代码
mget java python
*2
$5
252
jedis
$8
redis-py

有一点需要注意,无论是字符串回复还是多条字符串回复,如果有nil值,那么会返回$-1。

例如,对一个不存在的键执行get操作,返回结果为:

powershell 复制代码
get not_exist_key
$-1

如果批量操作中包含一条为nil值的结果,那么返回结果如下:

powershell 复制代码
mget hello not_exist_key java
*3
$5
world
$-1
$5
jedis

有了RESP提供的发送命令和返回结果的协议格式,各种编程语言就可以利用其来实现相应的Redis客户端。

相关推荐
用户094248568033 小时前
第19章:库存扣减与秒杀系统:Redis 扛峰值流量
redis·架构·源码
爱写Bug的浮游3 小时前
RTA高频请求下的Redis短缓存工程闭环
redis
没有气的汽水3 小时前
redis加了个分片,把线上写挂了一个多小时
redis
xixiaoyunya5 小时前
Redis缓存三大问题实战:穿透、击穿、雪崩,我用一个电商秒杀场景讲清楚
数据库·redis·缓存
鶴哥只手遮天6 小时前
《Redis 8.10.3 深度剖析》系列 · 第 1 章·全景与源码地图:版本谱系、构建体系、全局状态与启动链路
redis·后端·开源
70后写java的老码农6 小时前
docker-compose 部署 Redis Stack Server 6.2.6-v19
redis·docker
小南家的青蛙8 小时前
Redis监控与告警体系化实践
redis·监控方案·告警方案
我不会起名字3228 小时前
Redis 从 0 到 1(二十七):JSON 体积是 Protobuf 的 3.7 倍?3 种序列化的 Go 基准测试
redis·go·序列化·protobuf·messagepack
我不会起名字3228 小时前
Redis 从 0 到 1(二十六):INFO memory 里 8 个关键数字——内存构成与容量规划
redis·容量规划·内存·info·listpack