Redis 客户端管理 存活状态

client list中的age和idle分别代表当前客户端已经连接的时间和最近一次的空闲时间:

id=2232080 addr=10.16.xx.55:32886 fd=946 name= age=603382 idle=331060 flags=N db=0
sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=get

例如上面这条记录代表当期客户端连接Redis的时间为603382秒, 其中空闲了331060秒:

id=254487 addr=10.2.xx.234:60240 fd=1311 name= age=8888581 idle=8888581 flags=N db
sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=get

例如上面这条记录代表当期客户端连接Redis的时间为8888581秒, 其中空闲了8888581秒, 实际上这种就属于不太正常的情况, 当age等于idle时,说明连接一直处于空闲状态。
为了更加直观地描述age和idle, 下面用一个例子进行说明:

String key = "hello";
// 1) 生成jedis, 并执行get操作
Jedis jedis = new Jedis("127.0.0.1", 6379);
System.out.println(jedis.get(key));
// 2) 休息10秒
TimeUnit.SECONDS.sleep(10);
// 3) 执行新的操作ping
System.out.println(jedis.ping());
// 4) 休息5秒
TimeUnit.SECONDS.sleep(5);
// 5) 关闭jedis连接
jedis.close();

下面对代码中的每一步进行分析, 用client list命令来观察age和idle参数的相应变化。

为了与redis-cli的客户端区分, 本次测试客户端IP地址: 10.7.40.98。
1) 在执行代码之前, client list只有一个客户端, 也就是当前的rediscli, 下面为了节省篇幅忽略掉这个客户端。

127.0.0.1:6379> client list
id=45 addr=127.0.0.1:55171 fd=6 name= age=2 idle=0 flags=N db=0 sub=0 psub=0
multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client

2) 使用Jedis生成了一个新的连接, 并执行get操作, 可以看到IP地址为10.7.40.98的客户端, 最后执行的命令是get, age和idle分别是1秒和0秒:

127.0.0.1:6379> client list
id=46 addr=10.7.40.98:62908 fd=7 name= age=1 idle=0 flags=N db=0 sub=0 psub=0
multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=get

3) 休息10秒, 此时Jedis客户端并没有关闭, 所以age和idle一直在递增:

127.0.0.1:6379> client list
id=46 addr=10.7.40.98:62908 fd=7 name= age=9 idle=9 flags=N db=0 sub=0 psub=0
multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=get

4) 执行新的操作ping, 发现执行后age依然在增加, 而idle从0计算, 也就是不再闲置:

127.0.0.1:6379> client list
id=46 addr=10.7.40.98:62908 fd=7 name= age=11 idle=0 flags=N db=0 sub=0 psub=0
multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping

5) 休息5秒, 观察age和idle增加:

127.0.0.1:6379> client list
id=46 addr=10.7.40.98:62908 fd=7 name= age=15 idle=5 flags=N db=0 sub=0 psub=0
multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping

 6) 关闭Jedis, Jedis连接已经消失:

redis-cli client list | grep "10.7.40.98”为空

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

127.0.0.1:6379> info clients
# Clients
connected_clients:1414
...

 可以通过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, 连接将会被关闭, 例如设置timeout为30秒:

#Redis默认的timeout是0, 也就是不会检测客户端的空闲
127.0.0.1:6379> config set timeout 30
OK

下面继续使用Jedis进行模拟, 整个代码和上面是一样的, 只不过第2)步骤休息了31秒:

String key = "hello";
// 1) 生成jedis, 并执行get操作
Jedis jedis = new Jedis("127.0.0.1", 6379);
System.out.println(jedis.get(key));
// 2) 休息31秒
TimeUnit.SECONDS.sleep(31);
// 3) 执行get操作
System.out.println(jedis.get(key));
// 4) 休息5秒
TimeUnit.SECONDS.sleep(5);
// 5) 关闭jedis连接
jedis.close();

执行上述代码可以发现在执行完第2) 步之后, client list中已经没有了Jedis的连接, 也就是说timeout已经生效, 将超过30秒空闲的连接关闭掉:

127.0.0.1:6379> client list
id=16 addr=10.7.40.98:63892 fd=6 name= age=19 idle=19 flags=N db=0 sub=0 psub=0
multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=get
# 超过timeout后, Jedis连接被关闭
redis-cli client list | grep “10.7.40.98”为空

同时可以看到, 在Jedis代码中的第3) 步抛出了异常, 因为此时客户端已经被关闭, 所以抛出的异常是JedisConnectionException, 并且提示Unexpected end of stream:

stream:
world
Exception in thread "main" redis.clients.jedis.exceptions.JedisConnectionException:
Unexpected end of stream.

如果将Redis的loglevel设置成debug级别, 可以看到如下日志, 也就是客户端被Redis关闭的日志:
 

12885:M 26 Aug 08:46:40.085 - Closing idle client

 

总结


Redis的默认配置给出的timeout=0, 在这种情况下客户端基本不会出现上面的异常, 这是基于对客户端开发的一种保护。 例如很多开发人员在使用JedisPool时不会对连接池对象做空闲检测和验证, 如果设置了timeout>0, 可能就会出现上面的异常, 对应用业务造成一定影响, 但是如果Redis的客户端使用不当或者客户端本身的一些问题, 造成没有及时释放客户端连接, 可
能会造成大量的idle连接占据着很多连接资源, 一旦超过maxclients; 后果也是不堪设想。

所在在实际开发和运维中, 需要将timeout设置成大于0, 例如:可以设置为300秒, 同时在客户端使用上添加空闲检测和验证等等措施, 例如:JedisPool使用common-pool提供的三个属性: minEvictableIdleTimeMillis、testWhileIdle timeBetweenEvictionRunsMillis, 就不再赘述。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值