项目架构:

  部分组件如下:

  SpringCloudAlibaba(Nacos+Gateway+OpenFeign)+SpringBoot2.x+Redis

问题背景:

  最近由于用户量增大,在高峰时期,会导致用户服务偶尔Redis出现连接超时的情况,

  例如:从Redis中获取手机验证码 ,登录成功后,将token存入Redis,以及涉及到使用Redis的场景都会出现RedisConnectionFailureException

  异常日志:

237614  2021-03-02 17:24:42.595 ERROR [d03f845825644cee8753539f24d840ad] [http-nio-7122-exec-32] c.l.c.b.e.GlobalExceptionHandler -java.net.SocketTimeoutException: Read timed out; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out
237615 org.springframework.data.redis.RedisConnectionFailureException: java.net.SocketTimeoutException: Read timed out; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Readtimed out
237616 at org.springframework.data.redis.connection.jedis.JedisExceptionConverter.convert(JedisExceptionConverter.java:65)
237617 at org.springframework.data.redis.connection.jedis.JedisExceptionConverter.convert(JedisExceptionConverter.java:42)
237618 at org.springframework.data.redis.PassThroughExceptionTranslationStrategy.translate(PassThroughExceptionTranslationStrategy.java:44)
237619 at org.springframework.data.redis.FallbackExceptionTranslationStrategy.translate(FallbackExceptionTranslationStrategy.java:42)
237620 at org.springframework.data.redis.connection.jedis.JedisConnection.convertJedisAccessException(JedisConnection.java:135)
237621 at org.springframework.data.redis.connection.jedis.JedisStringCommands.convertJedisAccessException(JedisStringCommands.java:751)
237622 at org.springframework.data.redis.connection.jedis.JedisStringCommands.get(JedisStringCommands.java:67)
237623 at org.springframework.data.redis.connection.DefaultedRedisConnection.get(DefaultedRedisConnection.java:260)
237624 at org.springframework.data.redis.connection.DefaultStringRedisConnection.get(DefaultStringRedisConnection.java:398)
237625 at org.springframework.data.redis.core.DefaultValueOperations$1.inRedis(DefaultValueOperations.java:57)
237626 at org.springframework.data.redis.core.AbstractOperations$ValueDeserializingRedisCallback.doInRedis(AbstractOperations.java:60)
237627 at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:228)
237628 at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:188)
237629 at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:96)
237630 at org.springframework.data.redis.core.DefaultValueOperations.get(DefaultValueOperations.java:53)
237631 at com.xxxx.xxx.xxx.utils.RedisUtil.get(RedisUtil.java:242)

  Maven相关的Redis依赖:

  <!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency> <dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>

  Redis配置(单节点配置,没有做分布式部署)

spring:
redis:
pool:
maxActive: 300
maxIdle: 100
maxWait: 1000
host: xxxxxxxxx
port: 6379
password:
timeout: 2000
database: 5

排查过程:

  这里分析可能的原因如下:

  原因1.代码中是否有keys *类似的查询,由于Redis是单线程的,数据量大,单个命令执行时间过长,导致Redis客户端请求超时,keys *类似的查询非常消耗Redis的性能;

  原因2.Redis配置文件配置的 timeout 超时时间过短,上一个请求还没有执行结束,下一个请求无法获执行,最终超时导致请求失败;

  原因3.Redis连接池配置的链接数太小,通过Prometheus 监控发现用户服务  高峰时间请求量最高为180,考虑是否是连接数太小导致无法获取Redis连接,从而失败;

  

  针对原因1:

    这边排查了项目中的代码,没有类似keys * 查询,因此排除了这个可能行

  针对原因2:

    这边在观察了在出现 RedisConnectionFailureException时候,确认当前服务器Redis连接数峰值为15,配置文件中配置的超时时间配置为2000ms,由于确认原因1中的没有非常耗时的查询

    所以这种可能行也被排除了;

  

  由于以上原因1和原因2都排除了,这里考虑原因3,是连接数的问题

  查看配置发现最大连接数是300,远大于峰值180,配置数据似乎没问题,

  于是,在开发环境测试该配置,由于项目中使用的是Jedis连接池,没有使用lettuce连接池(注意:SpringBoot2.x对应的Spring-Boot-Data-Redis依赖默认使用的连接池是lettuce,如果要使用Jedis连接池,需要排除默认连接池配置,引入Jedis连接池,见上面的Maven依赖)

  进一步追踪源码发现

  配置连接数相关的类为:

package org.apache.commons.pool2.impl;

public class GenericObjectPoolConfig<T> extends BaseObjectPoolConfig<T> {
public static final int DEFAULT_MAX_TOTAL = 8;
public static final int DEFAULT_MAX_IDLE = 8;
public static final int DEFAULT_MIN_IDLE = 0;
private int maxTotal = 8;
private int maxIdle = 8;
private int minIdle = 0;
... }

  加载该配置类的时机是在项目启动初始化连接池的时候

    

package org.springframework.data.redis.connection.jedis;

import java.time.Duration;
import java.util.Optional; import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocketFactory; import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.lang.Nullable; /**
* Default implementation of {@literal JedisClientConfiguration}.
*
* @author Mark Paluch
* @author Christoph Strobl
* @since 2.0
*/
class DefaultJedisClientConfiguration implements JedisClientConfiguration { private final boolean useSsl;
private final Optional<SSLSocketFactory> sslSocketFactory;
private final Optional<SSLParameters> sslParameters;
private final Optional<HostnameVerifier> hostnameVerifier;
private final boolean usePooling;
private final Optional<GenericObjectPoolConfig> poolConfig;
private final Optional<String> clientName;
private final Duration readTimeout;
private final Duration connectTimeout; DefaultJedisClientConfiguration(boolean useSsl, @Nullable SSLSocketFactory sslSocketFactory,
@Nullable SSLParameters sslParameters, @Nullable HostnameVerifier hostnameVerifier, boolean usePooling,
@Nullable GenericObjectPoolConfig poolConfig, @Nullable String clientName, Duration readTimeout,
Duration connectTimeout) { this.useSsl = useSsl;
this.sslSocketFactory = Optional.ofNullable(sslSocketFactory);
this.sslParameters = Optional.ofNullable(sslParameters);
this.hostnameVerifier = Optional.ofNullable(hostnameVerifier);
this.usePooling = usePooling;
this.poolConfig = Optional.ofNullable(poolConfig);
this.clientName = Optional.ofNullable(clientName);
this.readTimeout = readTimeout;
this.connectTimeout = connectTimeout;
}

  Debug发现加载后仍然使用的是默认的连接数

    public static final int DEFAULT_MAX_TOTAL = 8;
public static final int DEFAULT_MAX_IDLE = 8;
public static final int DEFAULT_MIN_IDLE = 0;
private int maxTotal = 8;
private int maxIdle = 8;
private int minIdle = 0;

这里可能就是问题所在,配置文件中配置的最大连接数未生效,于是发现配置中这段配置已经失效
 redis:
pool:
maxActive: 300
maxIdle: 100
maxWait: 1000
 需要改为
  redis:
jedis:
pool:
maxActive: 300
maxIdle: 100
max-wait: 1000ms

  修改后重启生效,如配置的数据一致



【Redis连接超时】记录线上RedisConnectionFailureException异常排查过程的更多相关文章

  1. 一次线上OOM故障排查经过

    转贴:http://my.oschina.net/flashsword/blog/205266 本文是一次线上OOM故障排查的经过,内容比较基础但是真实,主要是记录一下,没有OOM排查经验的同学也可以 ...

  2. Java线上应用故障排查之二:高内存占用

    搞Java开发的,经常会碰到下面两种异常: 1.java.lang.OutOfMemoryError: PermGen space 2.java.lang.OutOfMemoryError: Java ...

  3. java线上应用故障排查之二:高内存占用【转】

    前一篇介绍了线上应用故障排查之一:高CPU占用,这篇主要分析高内存占用故障的排查. 搞Java开发的,经常会碰到下面两种异常: 1.java.lang.OutOfMemoryError: PermGe ...

  4. 【JVM】线上应用故障排查

    高CPU占用 一个应用占用CPU很高,除了确实是计算密集型应用之外,通常原因都是出现了死循环. 根据top命令,发现PID为28555的Java进程占用CPU高达200%,出现故障. 通过ps aux ...

  5. 线上服务器CPU100%排查,Linux进程消耗查看

    线上服务器CPU100%排查,Linux进程消耗查看 1.排查步骤 1.1Linux下排查 1.1.1查消耗cpu最高的进程PID 1.1.2根据PID查出消耗cpu最高的线程号 1.1.3根据线程号 ...

  6. 记录线上一次线程hang住问题

    线上发现执行某特定任务在某个特定时间点后不再work.该任务由线程池中线程执行定时周期性调度,根据日志查看无任何异常.从代码研判应该无关定时任务框架,因为对提交的定时任务做了wrap,会将异常都cat ...

  7. redis连接超时问题排查

    连接池无法获取到连接或获取连接超时redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource f ...

  8. [转]线上GC故障解决过程记录

    排查了三四个小时,终于解决了这个GC问题,记录解决过程于此,希望对大家有所帮助.本文假定读者已具备基本的GC常识和JVM调优知识,关于JVM调优工具使用可以查看我在同一分类下的另一篇文章: http: ...

  9. 一次线上GC故障解决过程记录

    排查了三四个小时,终于解决了这个GC问题,记录解决过程于此,希望对大家有所帮助.本文假定读者已具备基本的GC常识和JVM调优知识,关于JVM调优工具使用可以查看我在同一分类下的另一篇文章: http: ...

随机推荐

  1. C++入门(1):计算机组成

    系列文章尽在 | 公众号:lunvey 学习C++之前,我们有必要了解一下计算机的简单组成,毕竟C++是需要操作内存的一门语言.大家或许知道内存是什么,但是内存怎么读取和操作数据以及数据的表现形式会不 ...

  2. iOS之CoreBluetooth

    思路 手机与设备间的通讯方式CoreBluetooth是比较常见且通用的.在iOS开发中需明晰以下几点 蓝牙4.0最多可联机7个设备,iPhone6以上都是蓝牙4.0 两台iPhone并不直接通过蓝牙 ...

  3. C语言之结构体内存的对齐

    C语言之结构体内存的对齐 大纲: 零.引例 一.结构体内存对齐规则 二.怎样计算结构体的大小 三.设计结构体时要注意的方面   四.为什么存在内存对齐 五.修改默认对齐数 在前面的章节中,我们谈到了C ...

  4. Shiro反序列化<=1.2.4 复现

    Apache Shiro是一个Java安全框架,执行身份验证.授权.密码和会话管理.    shiro默认使用了CookieRememberMeManager,其处理cookie的流程是:得到reme ...

  5. FreeBSD 13 显卡支持

    On FreeBSD 13, using drm-devel-kmod, support is the same as on Linux 5.4. This includes support for ...

  6. 【vscode】【python】自定义代码自动补全

    引自:https://blog.csdn.net/Kyrielong/article/details/88058884 { "python.jediEnabled": true, ...

  7. 设计模式之建造者模式(BuilderPattern)

    一.意义 将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. 说明:复杂对象的构建,比如一个对象有几十个成员属性,那么我们在创建这个对象,并给成员属性赋值时,就会很麻烦.采用 ...

  8. codefoces B - Phoenix and Beauty

    原题链接:https://codeforc.es/problemset/problem/1348/B 题意:告诉我们一个数组及其长度和k,判断是否可以构造一个新数组使得每K段长度和都相等. 思路:首先 ...

  9. Go语言中使用K8s API及一些常用API整理

    Go Client 在进入代码之前,理解k8s的go client项目是对我们又帮助的.它是k8s client中最古老的一个,因此具有很多特性. Client-go 没有使用Swagger生成器,就 ...

  10. 攻防世界 reverse android-app-100

     android-app-100  suctf-2016 jeb启动,找到点击事件: 验证流程: 输入作为参数 --> processObjectArrayFromNative 得到一返回值(r ...