生产环境Redis连接超时故障排查

1,091 阅读1分钟

1. 问题现象

    生产系统出现响应缓慢,部分接口响应超时。查看日志发现Unable to connect to Redis的错误。

org.springframework.data.redis.RedisConnectionFailureException: Unable to connect to Redis; nested exception is org.springframework.data.redis.connection.PoolException: Could not get a resource from the pool; nested exception is java.util.NoSuchElementException: Timeout waiting for idle object
  at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$ExceptionTranslatingConnectionProvider.translateException(LettuceConnectionFactory.java:1534)
  at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$ExceptionTranslatingConnectionProvider.getConnection(LettuceConnectionFactory.java:1442)
  at org.springframework.data.redis.connection.lettuce.LettuceConnection.doGetAsyncDedicatedConnection(LettuceConnection.java:1026)
  at org.springframework.data.redis.connection.lettuce.LettuceConnection.getOrCreateDedicatedConnection(LettuceConnection.java:1012)
  at org.springframework.data.redis.connection.lettuce.LettuceConnection.getDedicatedConnection(LettuceConnection.java:981)
  at org.springframework.data.redis.connection.lettuce.LettuceConnection.getConnection(LettuceConnection.java:971)
  at org.springframework.data.redis.connection.lettuce.LettuceStringCommands.getConnection(LettuceStringCommands.java:795)
  at org.springframework.data.redis.connection.lettuce.LettuceStringCommands.get(LettuceStringCommands.java:66)
  at org.springframework.data.redis.connection.DefaultedRedisConnection.get(DefaultedRedisConnection.java:266)

2. 故障排查

2.1 第一次排查

    从日志描述上来看,是由于不能从连接池里拿到Redis连接,抛出的异常。起初怀疑是并发太高,连接池的容量设置过小造成的。然后调大了连接池的容量,重启后系统正常运行,但过了一段时间又出现了一样的问题。

2.2 第二次排查

    使用Jmeter对测试环境的接口做了压测,复现了问题。走读代码发现了造成事故的代码。

redisTemplate.getConnectionFactory().getConnection().setNX(realKey.getBytes(), value.getBytes()); 

    由于系统使用Redis封装了一个分布式锁,需要用到setNX()。但因为Spring Boot版本为1.5,RedisTemplate里没有该方法,所以直接通过getConnection()来执行setNX(),执行完毕后却没有主动释放连接,最终造成连接耗尽。

3. 解决问题

    找到原因后,解决就非常简单了。第一种就是在操作完成后释放连接。第二种就是扩展RedisTemplate,增加setNX()方法,RedisTemplate在执行方法后会自动释放连接。

4. 其他说明

    Redis的连接工厂使用的是LettuceConnectionFactory,同时关闭了本地连接共享(lettuceConnectionFactory.setShareNativeConnection(false))。