Redis连接与会话管理

笔记哥 / 05-24 / 11点赞 / 0评论 / 110阅读
# 序:文由 - 排查问题,发现微服务因 `ERR max number of clients reached` (**已达到客户端的最大数量**) redis异常,而导致服务在健康检测时未通过,进而导致高频宕机。 # 概述:Redis 连接与会话管理 ## Redis 连接会话的原理 - redis通过监听一个TCP端口或socket的方式接收来自客户端的连接,当与客户端**建立连接后**,redis内部会进行如下操作: > > > (1)客户端socket会被设置为非阻塞模式,因为redis在网络时间处理上采用的是非阻塞多路复用模型; > > (2)为这个socket设置TCP\_NODELAY属性,禁用Nagle算法; > > (3)创建一个可读的文件事件用于监听这个客户端socket的数据发送。 > # 常用功能与指令 ## 连接操作类 ### 查看连接总数 : `info clients` ```shell > info clients # Clients connected_clients:95 client_recent_max_input_buffer:2 client_recent_max_output_buffer:0 blocked_clients:0 rx_controlled_clients:0 total_real_rx_controlled:0 total_tx_controlled:0 total_rx_controlled:824214 proxy_header_error:0 ``` ### 查看连接详情列表 : `client list` - `client list` : 命令用于返回所有连接到服务器的客户端信息和统计数据。 > > > http://www.redis.cn/commands/client-list.html > ```shell client list ``` ![](https://cdn.res.knowhub.vip/c/2505/25/ed462cfa.png?G1YAAMTsdJyIPInoNuqQ9k5xp82ARBZBpYT1ev5%2fn4vo%2fRwJJd6zj%2bXnw1%2f6WE7MNStXQoIkQfAoLAVgbRayibKptbinAw%3d%3d) - 返回值 > > > `bulk-string-reply`: 一个独特的字符串。具体格式: > > > > > > > > > 每个已连接客户端对应一行(以 LF 分割) > > > > 每行字符串由一系列 属性=值(property=value) 形式的域组成,每个域之间以空格分开。 > > > > 各字段的含义:: > > > > ```txt id: 唯一的64位的客户端ID(Redis 2.8.12加入)。 addr: 客户端的地址和端口 fd: 套接字所使用的文件描述符 age: 以秒计算的已连接时长 idle: 以秒计算的空闲时长 flags: 客户端 flag db: 该客户端正在使用的数据库 ID sub: 已订阅频道的数量 psub: 已订阅模式的数量 multi: 在事务中被执行的命令数量 qbuf: 查询缓冲区的长度(字节为单位, 0 表示没有分配查询缓冲区) qbuf-free: 查询缓冲区剩余空间的长度(字节为单位, 0 表示没有剩余空间) obl: 输出缓冲区的长度(字节为单位, 0 表示没有分配输出缓冲区) oll: 输出列表包含的对象数量(当输出缓冲区没有剩余空间时,命令回复会以字符串对象的形式被入队到这个队列里) omem: 输出缓冲区和输出列表占用的内存总量 events: 文件描述符事件 cmd: 最近一次执行的命令 ``` ### 设置当前连接点redis的名称 : `CLIENT SETNAME` ```shell CLIENT SETNAME ``` ### 查看当前连接的名称 : `CLIENT GETNAME` ```shell CLIENT GETNAME ``` ### 杀死指定连接 : `CLIENT KILL {ip}:{port}` ```csharp CLIENT KILL {ip}:{port} ``` ## 连接配置类 ### 查看/设置连接的默认超时自动断连时间(空闲超时时间) : `config [get|set] timeout [{second}]` - 查看连接的默认超时自动断连时间(空闲超时时间) > > > 默认值 = 0 > ```shell > config get timeout 0 ``` - 设置连接的默认超时自动断连时间(空闲超时时间) > > > 单位:秒钟 > ```shell config set timeout 600 ``` ### 查询/设置允许的最大连接数 : `config get maxclients` - 查询允许的最大连接数 ```shell config get maxclients ``` - 设置允许的最大连接数 > > > 方法1:在2.6之后版本,可以修改最大连接数配置,默认`10000`,可以在`redis.conf`配置文件中修改 > ```shell ... # maxclients 10000 ... ``` > > > 方法2: `config set maxclients num` 可以设置redis允许的最大连接数 > ```shell 127.0.0.1:6379> CONFIG set maxclients 10 OK 127.0.0.1:6379> ``` > > > 方法3:启动`redis.service`服务时加参数`--maxclients 100000`来设置最大连接数限制 > ```shell redis-server --maxclients 100000 -f /etc/redis.conf ``` # FAQ for Redis 连接与会话管理 ## Q: 连接过多,乃至报`ERR max number of clients reached`(Redis客户端连接数已达到最大限制)? - 问题分析 > > - `redis maxclients` > > > > > > > > > > `redis maxclients` 是redis server的重要配置,它决定了**客户端的最大连接数量**、**最大客户端连接数量**。 > > > > 由于redis不区分连接是客户端连接、还是内部打开文件、或者和slave连接等。所以,`maxclients`最小存在32个连接数,如果超过了设置的maxclients,redis会给新的连接发送"`max number of clients reached`",并**关闭连接**。 > > > > > > - 硬编码限制 > > > > > > > > > > 在Redis 2.4中,可以同时处理的最大客户端数量存在**硬编码限制**。 > > > > 在Redis 2.6中,此限制是**动态**的:默认情况下,它设置为10000个客户端,除非`Redis.conf`中的`maxclients`指令另有说明。 > > > > > > - 原因分析 > > > > > > > > > > 绝大部分原因:由于客户端很多**空闲连接**都没有被及时释放掉,从而导致`connected_clients`非常高 > > > > 其他可能:`maxclients`设置的太少了 > > > > 还有可能:软硬件存在限制 > > > > - 解决方法 > > - 1 客户端 - 设置超时时间 > > ```java JedisPoolConfig jedisPoolConfig = initPoolConfig(); jedisPool = new JedisPool(jedisPoolConfig, "*.*.*.*", 6379); =========> 修改为: JedisPoolConfig jedisPoolConfig = initPoolConfig(); jedisPool = new JedisPool(jedisPoolConfig, "*.*.*.*", 6379, 2*1000); //timeout = 2*1000 = connectionTimeout = soTimeout ``` > > > `public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, final int connectionTimeout, final int soTimeout, final String password, final int database, final String clientName)` > > > - 2 客户端 - 回收连接资源 > > ```java Jedis jedis = JedisUtils.getJedis();try { //Todo } catch (Exception e){ throw new RuntimeException(e.getMessage(), e); JedisUtils.returnBrokenResource(jedis); //释放连接 } finally { JedisUtils.returnResource(jedis); //释放连接 } ---- /** * 回收Jedis对象资源 * * @param jedis */public synchronized void returnResource(Jedis jedis) { if (jedis != null) { jedisPool.returnResource(jedis); }} /** * Jedis对象出异常的时候,回收Jedis对象资源 * * @param jedis */public synchronized void returnBrokenResource(Jedis jedis) { if (jedis != null) { jedisPool.returnBrokenResource(jedis); } } ``` > > > 在 `Jedis 2.6.2` 以后,由于重写了`jedis.close()`,实现**自动关闭**,`2.6.3`以后正式使用,详见[Deprecates JedisPool returnResource and returnBrokenResource](https://github.com/xetorthio/jedis/pull/912) ,这样可以代码使用JDK7中新增的`try-with-resource`语法糖,这样代码会简洁很多如下: > ```java try (Jedis jedis = JedisUtils.getJedis()) { //Todo } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } ``` > > - 3 通过ip排查对应的service服务,查看是否还可以进行优化。 > - 4 查看空闲超时时间: `config get timeout` > > > > > > > > > > `0` : 默认不开启 > > > > > > - 5 设置空闲超时时间 : `config set timeout {second}` > > > > > > > > > > 建议为`0`,尤其是有用分布式锁的情况下、具体看业务场景 > > > > 如:配置默认空闲超时时间为60s : `config set timeout 60` > > > > > > - 6 `config rewrit` : 保存 > > > > > > > > > > 可在 `redis.conf` 配置文件中添加重启生效。 > > > > # X 参考文献 - [redis 连接工具_redis之timeout(默认建议为0) , maxclients(重要) - 博客园](https://www.cnblogs.com/cheyunhua/p/15903942.html) - [解决Redis 连接池报错:ERR max number of clients reached - CSDN](https://blog.csdn.net/tengxing007/article/details/88354218) 【推荐】 - [生产-已解决-Redis连接数占满 报错 (error) ERR max number of clients reached - CSDN](https://blog.csdn.net/zhanshixiang/article/details/129693694) 【推荐】 > > > ` (error) ERR max number of clients reached` > > "我仔细审查了一下前同事写的Redis工具类,发现连接用完,全都没有归还连接" > ```java public T get(String key, String modulePrefix, Class t) { checkJedisPool(); key = generateKey(key, modulePrefix); try (Jedis jedis = jedisPool.getResource()) { String valueStr = jedis.get(key); return parse(valueStr, t); } } ```