redis学习笔记——第四章 客户端

第四章 客户端

4.1 客户端API

1.client list
client list命令能列出与Redis服务端相连的所有客户端连接信息

(1)标识:id、addr、fd、name
这四个属性属于客户端的标识:
·id:客户端连接的唯一标识,这个id是随着Redis的连接自增的,重启Redis后会重置为0。
·addr:客户端连接的ip和端口。

·fd:socket的文件描述符,与lsof命令结果中的fd是同一个,如果fd=-1代表当前客户端不是外部客户端,而是Redis内部的伪装客户端。
·name:客户端的名字,后面的client setName和client getName两个命令会对其进行说明。

(2)输入缓冲区:qbuf、qbuf-free
Redis为每个客户端分配了输入缓冲区,它的作用是将客户端发送的命令临时保存,同时Redis从会输入缓冲区拉取命令并执行,输入缓冲区为客户端发送命令到Redis执行命令提供了缓冲功能

​ client list中qbufqbuf-free分别代表这个缓冲区的总容量剩余容量

​ Redis没有提供相应的配置来规定每个缓冲区的大小,输入缓冲区会根据输入内容大小的不同动态调整,只是要求每个客户端缓冲区的大小不能超过1G,超过后客户端将被关闭

(3)输出缓冲区:obl、oll、omem

​ Redis为每个客户端分配了输出缓冲区,它的作用是保存命令执行的结果返回给客户端,为Redis和客户端交互返回结果提供缓冲

​ 和输入缓冲区相同的是,输出缓冲区也不会受到maxmemory的限制,如果使用不当同样会造成maxmemory用满产生的数据丢失、键值淘汰、OOM等情况。

​ 实际上输出缓冲区由两部分组成:固定缓冲区(16KB)和动态缓冲区,其中固定缓冲区返回比较小的执行结果,而动态缓冲区返回比较大的结果,例如大的字符串、hgetall、smembers命令的结果等

​ 固定缓冲区使用的是字节数组,动态缓冲区使用的是列表。当固定缓冲区存满后会将Redis新的返回结果存放在动态缓冲区的队列中,队列中的每个对象就是每个返回结果

​ client list中的obl代表固定缓冲区的长度,oll代表动态缓冲区列表的长度,omem代表使用的字节数

(4)客户端的存活状态

​ client list中的age和idle分别代表当前客户端已经连接的时间最近一次的空闲时间,当age等于idle时,说明连接一直处于空闲状态

(5)客户端的限制maxclients和timeou

​ Redis提供了maxclients参数来限制最大客户端连接数,一旦连接数超过maxclients,新的连接将被拒绝。maxclients默认值是10000,可以通过info clients来查询当前Redis的连接数

可以通过config set maxclients对最大客户端连接数进行动态设置:
127.0.0.1:6379> config get maxclients
1) "maxclients"
2) "10000"
127.0.0.1:6379> config set maxclients 50
OK
127.0.0.1:6379> config get maxclients
1) "maxclients"
2) "50"
一般来说maxclients=10000在大部分场景下已经绝对够用,但是某些情况由于业务方使用不当(例如没有主动关闭连接)可能存在大量idle连接,无论是从网络连接的成本还是超过maxclients的后果来说都不是什么好事,因此Redis提供了timeout(单位为秒)参数来限制连接的最大空闲时间,一旦客户端连接的idle时间超过了timeout,连接将会被关闭
4.2.client setName和client getName
client setName xx
client getName
1.client setName用于给客户端设置名字,这样比较容易标识出客户端的来源,lient getName和setName命令可以做为标识客户端来源的一种方式,但是通常来讲,在Redis只有一个应用方使用的情况下,IP和端口作为标识会更加清晰
4.3.client kill
client kill ip:port
此命令用于杀掉指定IP地址和端口的客户端
4.4client pause
client pause timeout(毫秒)
client pause命令用于阻塞客户端timeout毫秒数,在此期间客户端连接将被阻塞。
4.5.monitor
monitor命令用于监控Redis正在执行的命令,如图4-11所示,我们打开了两个redis-cli,一个执行set get ping命令,另一个执行monitor命令。
既然monitor能监听到所有的命令,一旦Redis的并发量过大,monitor客户端的输出缓冲会暴涨,可能瞬间会占用大量内存
4.6 客户端常见异常
4.6.1 无法从连接池获取到连接
JedisPool中的Jedis对象个数是有限的,默认是8个。这里假设使用的默认配置,如果有8个Jedis对象被占用,并且没有归还,此时调用者还要从JedisPool中借用Jedis,就需要进行等待(例如设置了maxWaitMillis>0),如
果在maxWaitMillis时间内仍然无法获取到Jedis对象就会抛出如下异常:
redis.clients.jedis.exceptions.JedisConnectionException:
Could not get a resourcefrom the pool
…
Caused by: java.util.NoSuchElementException: Timeout waiting for idle object
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.
java:449)
还有一种情况,就是设置了blockWhenExhausted=false,那么调用者发现池子中没有资源时,会立即抛出异常不进行等待,下面的异常就是blockWhenExhausted=false时的效果:
redis.clients.jedis.exceptions.JedisConnectionException: 
Could not get a resourcefrom the pool
…
Caused by: java.util.NoSuchElementException: Pool exhausted
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.
java:464)
对于这个问题,需要重点讨论的是为什么连接池没有资源了,造成没有
资源的原因非常多,可能如下:
  1. 客户端:高并发下连接池设置过小,出现供不应求,所以会出现上面的错误,但是正常情况下只要比默认的最大连接数(8个)多一些即可,因为正常情况下JedisPool以及Jedis的处理效率足够高。
  2. ·客户端:没有正确使用连接池,比如没有进行释放,例如下面代码所
    示。
  3. 客户端:存在慢查询操作,这些慢查询持有的Jedis对象归还速度会比
    较慢,造成池子满了
  4. ·服务端:客户端是正常的,但是Redis服务端由于一些原因造成了客户
    端命令执行过程的阻塞,也会使得客户端抛出这种异常
4.6.2客户端读写超时
Jedis在调用Redis时,如果出现了读写超时后,会出现下面的异常
redis.clients.jedis.exceptions.JedisConnectionException:
java.net.SocketTimeoutException: Read timed out

造成该异常的原因也有以下几种:

  1. 读写超时间设置得过短。
  2. ·命令本身就比较慢
  3. ·客户端与服务端网络不正常。
  4. ·Redis自身发生阻塞。
4.6.3客户端连接超时
Jedis在调用Redis时,如果出现了连接超时后,会出现下面的异常:
redis.clients.jedis.exceptions.JedisConnectionException:
java.net.SocketTimeoutException: connect timed out

造成该异常的原因也有以下几种:

  1. 连接超时设置得过短,可以通过下面代码进行设置:
    jedis.getClient().setConnectionTimeout(time);
  2. Redis发生阻塞,造成tcp-backlog已满,造成新的连接失败。
  3. 客户端与服务端网络不正常。
4.6.4.客户端缓冲区异常
Jedis在调用Redis时,如果出现客户端数据流异常,会出现下面的异常:
redis.clients.jedis.exceptions.JedisConnectionException: Unexpected end of stream.
造成这个异常的原因可能有如下几种:
1)输出缓冲区满。例如将普通客户端的输出缓冲区设置为1M1M60
config set client-output-buffer-limit "normal 1048576 1048576 60 slave 268435456
67108864 60 pubsub 33554432 8388608 60"
如果使用get命令获取一个bigkey(例如3M),就会出现这个异常。
2)长时间闲置连接被服务端主动断开,上节已经详细分析了这个问题。
3)不正常并发读写:Jedis对象同时被多个线程并发操作,可能会出现上述异常。
4.6.5 .Lua脚本正在执行
如果Redis当前正在执行Lua脚本,并且超过了lua-time-limit,此时Jedis调用Redis时,会收到下面的异常。
redis.clients.jedis.exceptions.JedisDataException: BUSY Redis is busy running a
script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.
4.6.6Redis正在加载持久化文件
Jedis调用Redis时,如果Redis正在加载持久化文件,那么会收到下面的异常:
redis.clients.jedis.exceptions.JedisDataException: LOADING Redis is loading the
dataset in memory
4.6.7Redis使用的内存超过maxmemory配置
Jedis执行写操作时,如果Redis的使用内存大于maxmemory的设置,会收到下面的异常,此时应该调整maxmemory并找到造成内存增长的原因
redis.clients.jedis.exceptions.JedisDataException: OOM command not allowed when
used memory > 'maxmemory'.
4.6.8 客户端连接数过大
如果客户端连接数超过了maxclients,新申请的连接就会出现如下异常:
redis.clients.jedis.exceptions.JedisDataException: ERR max number of clients reached
此时新的客户端连接执行任何命令,返回结果都是如下:
127.0.0.1:6379> get hello
(error) ERR max number of clients reached
这个问题可能会比较棘手,因为此时无法执行Redis命令进行问题修复,一般来说可以从两个方面进行着手解决:
·客户端:如果maxclients参数不是很小的话,应用方的客户端连接数基本不会超过maxclients,通常来看是由于应用方对于Redis客户端使用不当造成的。此时如果应用方是分布式结构的话,可以通过下线部分应用节点(例如占用连接较多的节点),使得Redis的连接数先降下来。从而让绝大部分节点可以正常运行,此时再通过查找程序bug或者调整maxclients进行问题的修复。
·服务端:如果此时客户端无法处理,而当前Redis为高可用模式(例如Redis Sentinel和Redis Cluster),可以考虑将当前Redis做故障转移。此问题不存在确定的解决方式,但是无论从哪个方面进行处理,故障的快速恢复极为重要,当然更为重要的是找到问题的所在,否则一段时间后客户端连接数依然会超过maxclients

本章重点回顾

  1. RESP(Redis Serialization Protocol Redis)保证客户端与服务端的正
    常通信,是各种编程语言开发客户端的基础。
  2. 要选择社区活跃客户端,在实际项目中使用稳定版本的客户端。
  3. 区分Jedis直连和连接池的区别,在生产环境中,应该使用连接池
  4. Jedis.close()在直连下是关闭连接,在连接池则是归还连接。
  5. Jedis客户端没有内置序列化,需要自己选用。
  6. 客户端输入缓冲区不能配置,强制限制在1G之内,但是不会受到maxmemory限制。
  7. 客户端输出缓冲区支持普通客户端、发布订阅客户端、复制客户端配置,同样会受到maxmemory限制。
  8. Redis的timeout配置可以自动关闭闲置客户端,tcp-keepalive参数可以周期性检查关闭无效TCP连接
  9. monitor命令虽然好用,但是在大并发下存在输出缓冲区暴涨的可能性。
  10. info clients帮助开发和运维人员找到客户端可能存在的问题。
  11. 理解Redis通信原理和建立完善的监控系统对快速定位解决客户端常见问题非常有帮助。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值