文章目录
redis协议简介
redis使用的通信协议是RESP(REdis Serialization Protocol), 是一种简便, 可读性很好的通信协议
以下内容摘自RESP2的文档内容, 参考地址: https://redis.io/topics/protocol
RESP3的文档地址: https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md, 对RESP2进行了扩充, 比如以","开头的浮点数返回,1.23<CR><LF>
, 感兴趣的可以看看, 帮助你更好的了解和使用redis.
redis响应格式:
简单字符串:
第一个字节是 “+”
用于非二进制安全的简短字符串应答, 以CRLF结束, 网络字节值为0x0d0x0a, 字符串"\r\n"
注意, 一般情况是"\r\n"的字节编码就是0x0d,0x0a, 但是有些编码格式则不同, 这种情况会发生错误
例如:
+PONG<CR><LF>
,+OK<CR><LF>
错误信息:
第一个字节是"-"
类似于简单字符串信息, 只是用于表达错误信息, 也是以CRLF结束
例如:
-Error message<CR><LF>
-ERR unknown command 'foobar'<CR><LF>
-WRONGTYPE Operation against a key holding the wrong kind of value<CR><LF>
整数信息:
第一个字节是":"
用于进行整数信息回复, 比如命令: INCR, LLEN, LASTSAVE等
例如:
:0<CR><LF>
:10000<CR><LF>
大容量字符串:
第一个字节是:"$"
单行二进制安全字符串响应, 最大长度512mb, "$"后面的数组表示内容长度
例如:
$6<CR><LF>foobar<CR><LF>
$-1<CR><LF>
回复一个空(NULL)字符串, 相当于null, nil, 客户端请求一个nil对象时, 会返回这个, 这个不同于"", ""表示格式是$0<CR><LF>
数组列表信息:
第一个字节是: “*”
返回一个列表信息, "*"后面的数字表示元素个数, LRANGE之类的指令会返回这种格式信息
例如:
*0<CR><LF>
表示空列表
*2<CR><LF>$3<CR><LF>foo<CR><LF>$3<CR><LF>bar<CR><LF>
长度为2的列表, 俩个元素内容分别为foo和bar
*-1<CR><LF>
NULL列表, BLPOP如果超时了, 就会返回这种信息
*2<CR><LF> *3<CR><LF>:1<CR><LF>:2<CR><LF>:4<CR><LF> *2<CR><LF>+OK<CR><LF>-ERR<CR><LF>
这是一个嵌套列表信息
使用wireshark 抓取redis数据包
配置抓包过滤 host 10.229.99.168
10.229.99.168是redis的服务地址
点击开始抓取进行抓包
写一个redistemplate 的测试类, 向redis发送数据
RedisSentinelConfiguration rsc = new RedisSentinelConfiguration();
rsc.addSentinel(
new RedisNode("10.229.99.168", 7379));
rsc.setPassword("我是密码");
rsc.setMaster("master1");
JedisPoolConfig jpc = new JedisPoolConfig();
jpc.setMaxTotal(10);
jpc.setMaxIdle(5);
jpc.setTestOnBorrow(true);
jpc.setMaxWaitMillis(500L);
JedisConnectionFactory factory = new JedisConnectionFactory(rsc, jpc);
factory.afterPropertiesSet();
RedisTemplate<String, String> redisT = new RedisTemplate<>();
redisT.setConnectionFactory(factory);
redisT.setDefaultSerializer(new StringRedisSerializer());
redisT.afterPropertiesSet();
redisT.opsForList().leftPush("tlist", "test");
redisT.delete("tlist");
运行, 这时可以在wireshark中查看redis数据包了
分析sentinel数据包
配置显示过滤, 先过滤出sentinel数据包, tcp.port eq 7379
7379是redis sentinel的接口
首先看到三次握手, 建立tcp连接
1 0.000000 172.24.123.111 10.229.99.168 TCP 66 51606 → 7379 [SYN] Seq=467271664 Win=64680 Len=0 MSS=1470 WS=256 SACK_PERM=1
2 0.043827 10.229.99.168 172.24.123.111 TCP 60 7379 → 51606 [SYN, ACK] Seq=4095492181 Ack=467271665 Win=14600 Len=0 MSS=1460
3 0.043964 172.24.123.111 10.229.99.168 TCP 54 51606 → 7379 [ACK] Seq=467271665 Ack=4095492182 Win=64680 Len=0
接着客户端给redis-sentinel服务发送数据:
4 0.051048 172.24.123.111 10.229.99.168 TCP 115 55750 → 7379 [PSH, ACK] Seq=957963677 Ack=344093169 Win=64680 Len=61
数据内容显示如下
*3
$8
SENTINEL
$23
get-master-addr-by-name
$7
master1
服务端发送ack数据包, 确认收到数据:
5 0.094628 10.229.99.168 172.24.123.111 TCP 60 7379 → 55750 [ACK] Seq=344093169 Ack=957963738 Win=14600 Len=0
服务端接着发送客户端指令get-master-addr-by-name
的响应数据
6 0.095346 10.229.99.168 172.24.123.111 TCP 87 7379 → 55750 [PSH, ACK] Seq=344093169 Ack=957963738 Win=14600 Len=33
数据内容显示如下:
*2
$12
10.229.99.168
$4
6480
返回了redis master server 的地址和端口号, 10.229.99.168:6480 , 6480位redis master server的端口号, 接着这个sentinel连接会随着客户端发送rst包强制断开。
新的sentinel连接会开启,顺序如下:
-
三次握手, 建立连接
-
客户端发送指令 *3$8SENTINEL$23get-master-addr-by-name$7master1, 请求获取主服务地址
-
服务端响应, *2$1210.229.99.168$46480
-
客户端会发送订阅主服务切换指令
14 0.219555 172.24.123.111 10.229.99.168 TCP 94 55751 → 7379 [PSH, ACK] Seq=2989715116 Ack=3319979599 Win=64647 Len=40
数据包内容如下:
*2 $9 SUBSCRIBE $14 +switch-master
-
sentinel服务端返回订阅成功的信息
15 0.267211 10.229.99.168 172.24.123.111 TCP 98 7379 → 55751 [PSH, ACK] Seq=3319979599 Ack=2989715156 Win=14600 Len=44
数据包内容如下:
*3 $9 subscribe $14 +switch-master :1
-
保持连接, 如果服务器上redis服务发生主从切换, sentinel服务端会返回信息如下:
*3 $7 message $14 +switch-master $43 master1 10.229.99.168 6480 10.229.99.168 6479
redis主服务从10.229.99.168:6480切换到0.229.99.168:6479
-
这就是redis sentinel的数据交互的过程, 再看下redis服务的数据交互
分析redis server数据包
配置显示过滤为 tcp.port in {6479, 6480}
6480, 6479分别为 redis 主, 从服务端口
这时候, 我们就把redis server交互的数据包过滤出来了。
-
首先是三次握手, 建立连接
17 0.489588 172.24.123.111 10.229.99.168 TCP 66 55752 → 6480 [SYN] Seq=2488491565 Win=64680 Len=0 MSS=1470 WS=256 SACK_PERM=1 18 0.533252 10.229.99.168 172.24.123.111 TCP 60 6480 → 55752 [SYN, ACK] Seq=2932666663 Ack=2488491566 Win=14600 Len=0 MSS=1460 19 0.533311 172.24.123.111 10.229.99.168 TCP 54 55752 → 6480 [ACK] Seq=2488491566 Ack=2932666664 Win=64680 Len=0
-
客户端向redis server服务端发送认证请求
*2 $4 AUTH $14 Password-value
Password-value是redis server配置中的认证密码
-
服务端返回认证成功的指令
+OK<CR><LF>
, 如果密码错误的话, 会返回错误信息:-ERR invalid password<CR><LF>
-
客户端发送PING指令
*1 $4 PING
-
服务端响应PONG
+PONG<CR><LF>
-
客户端发送存储列表数据指令
*3 $5 LPUSH $5 tlist $4 test
-
服务端响应存入列表的元素个数
:1<CR><LF>
-
客户端发送PING
-
服务端响应PONG
-
客户端发送删除tlist指令
*2 $3 DEL $5 tlist
-
服务端响应删除键的个数
:1<CR><LF>