链条传动

砥砺前行,不忘初心!

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

客户端通信协议

Redis制定了RESP(REdis Serialization Protocol, Redis序列化协议) 实现客户端与服务端的正常交互, 这种协议简单高效, 既能够被机器解析, 又容易被人类识别。

示例:客户端发送一条set hello world命令给服务端。按照RESP的标准, 客户端需要将其封装为如下格式(每行用\r\n分隔):

*3
$3
SET
$5
hello
$5
world

返回:

+OK

 

RESP格式说明:
发送命令格式

RESP的规定一条命令的格式如下, CRLF代表"\r\n"
*<参数数量> CRLF
$<参数1的字节数量> CRLF
<参数1> CRLF
...
$<参数N的字节数量> CRLF
<参数N> CRLF

 

示例解说:以set hello world为例

*3         参数数量为3个,第一行为*3
$3         set有3个字节,第二行为$3
SET        第一个参数set
$5         hello有5个字节,第三行为$5
hello      第二个参数hellot
$5         world有5个字节,第五行为$5
world      第三个参数world

上面只是格式化显示的结果, 实际传输格式为如下代码:

*3\r\n$3\r\nSET\r\n$5\r\nhello\r\n$5\r\nworld\r\n

 

返回结果格式
Redis的返回结果类型分为以下五种

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

 

redis-cli只能看到最终的执行结果, 那是因为redis-cli本身就是按照RESP进行结果解析的, 所以看不到中间结果。
例如执行set hello world, 返回结果是OK, 并不能看到加号:

127.0.0.1:6379> set hello world
OK

为了看到Redis服务端返回的“真正”结果, 可以使用nc命令、 telnet命令、 甚至写一个socket程序进行模拟。
示例:

使用nc命令连接redis:nc 127.0.0.1 6379
set hello world
+OK

sethx
-ERR unknown command 'sethx'

incr counter
:1

get hello
$5
world

mget java python
*2
$5
jedis
$8
redis-py

 


无论是字符串回复还是多条字符串回复, 如果有nil值, 那么会返回$-1

对一个不存在的键执行get操作, 返回结果为:
get not_exist_key
$-1

如果批量操作中包含一条为nil值的结果, 那么返回结果如下:
mget hello not_exist_key java
*3
$5
world
$-1
$5
jedis

 

客户端API


client list

id=5020789 addr=172.19.8.172:36962 fd=516 name= age=24621 idle=24621 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=cluster

标识:id、 addr、 fd、 name
id:客户端连接的唯一标识,这个id是随着Redis的连接自增的,重启Redis后会重置为0。
  addr:客户端连接的ip和端口。
  fd:socket的文件描述符,与lsof命令结果中的fd是同一个,如果fd=-1代表当前客户端不是外部客户端,而是Redis内部的伪装客户端。
  name:客户端的名字 后面的client setName和client getName两个命令会对其进行说明。


输入缓冲区: qbuf、 qbuf-free
  qbuf、 qbuf-free 分别代表这个缓冲区的总容量和剩余容量。


输出缓冲区: obl、 oll、 omem
obl代表固定缓冲区的长度
oll代表动态缓冲区列表的长度
omem代表使用的字节数
注:输出缓冲区由两部分组成: 固定缓冲区(16KB)和动态缓冲区,其中固定缓冲区返回比较小的执行结果,而动态缓冲区返回比较大的结果。固定缓冲区使用的是字节数组, 动态缓冲区使用的是列表。 当固定缓冲区存满后会将Redis新的返回结果存放在动态缓冲区的队列中。


  客户端的存活状态: age、idle
    age表示当前客户端已经连接的时间
    idle表示最近一次的空闲时间

 


  客户端类型: flag

    N 普通客户端
    M 当前客户端是master节点
    S 当前客户端是slave节点
    O 当前客户端正在执行monitor命令
    X 当前客户端正在执行事务
    b 当前客户端正在等待阻塞事件
    i 当前客户端正在等待VM I/O,此状态目前已弃用
    d 一个受监视的键被修改,EXEC命令将失败
    u 客户端未被阻塞
    c 回复完整输出后,关闭连接
    A 尽可能快的关闭连接

 

 

输入缓冲区说明:

Redis为每个客户端分配了输入缓冲区,它的作用是将客户端发送的命令临时保存,同时Redis从会输入缓冲区拉取命令并执行, 输入缓冲区为客户端发送命令到Redis执行命令提供了缓冲功能。
注:输入缓冲区会根据输入内容大小的不同动态调整,只是要求每个客户端缓冲区的大小不能超过1G,超过后客户端将被关闭。

 

输入缓冲使用不当会产生两个问题:

·一旦某个客户端的输入缓冲区超过1G, 客户端将会被关闭。
·输入缓冲区不受maxmemory控制, 假设一个Redis实例设置了maxmemory为4G, 已经存储了2G数据, 但是如果此时输入缓冲区使用了3G, 已经超过maxmemory限制, 可能会产生数据丢失、 键值淘汰、 OOM等情况

 

造成输入缓冲区过大的原因有哪些?

1、Redis的处理速度跟不上输入缓冲区的输入速度,并且每次进入输入缓冲区的命令包含了大量bigkey, 从而造成了输入缓冲区过大的情况。 
2、还有一种情况就是Redis发生了阻塞,短期内不能处理命令,造成客户端输入的命令积压在了输入缓冲区,造成了输入缓冲区过大。

 

监控输入缓冲区异常的方法有两种:

1、通过定期执行client list命令,收集qbuf和qbuf-free找到异常的连接记录并分析,最终找到可能出问题的客户端。
2、通过info命令的info clients模块,找到最大的输入缓冲区。

 

对比client list和info clients监控输入缓冲区的优劣势

 

 

输出缓冲区说明:

Redis为每个客户端分配了输出缓冲区,它的作用是保存命令执行的结果返回给客户端,为Redis和客户端交互返回结果提供缓冲。
注:输出缓冲区的容量可以通过参数client-outputbuffer-limit来进行设置,并且输出缓冲区做得更加细致,按照客户端的不同分为三种: 普通客户端、 发布订阅客户端、 slave客户端。

client-outputbuffer-limit配置:

client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>
·<class>: 客户端类型, 分为三种。 
    a) normal: 普通客户端; 
    b) slave: slave客户端,用于复制; 
    c) pubsub: 发布订阅客户端。
·<hard limit>: 如果客户端使用的输出缓冲区大于<hard limit>, 客户端会被立即关闭。
·<soft limit>和<soft seconds>: 如果客户端使用的输出缓冲区超过了<softlimit>并且持续了<soft limit>秒, 客户端会被立即关闭。

 

redis默认配置:

client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60

 

其他:

client setName xx               给客户端设置名字,这样比较容易标识出客户端的来源。
client getName                  查看当前客户端的name
client kill ip:port             杀掉指定IP地址和端口的客户端
client pause timeout(毫秒)       阻塞客户端timeout毫秒数,在此期间客户端连接将被阻塞(对主从复制的客户端无效)
monitor                         监控Redis正在执行的命令

 

posted on 2020-12-01 11:04  链条君  阅读(169)  评论(0编辑  收藏  举报