关于Springboot2.x集成lettuce连接redis集群报超时异常Command timed out after 6 second(s)

背景:最近在对一新开发Springboot系统做压测,发现刚开始压测时,可以正常对redis集群进行数据存取,但是暂停几分钟后,接着继续用jmeter进行压测时,发现redis就开始突然疯狂爆出异常提示:Command timed out after 6 second(s)......

Caused by: io.lettuce.core.RedisCommandTimeoutException: Command timed out after 6 second(s)
 at io.lettuce.core.ExceptionFactory.createTimeoutException(ExceptionFactory.java:51)
 at io.lettuce.core.LettuceFutures.awaitOrCancel(LettuceFutures.java:114)
 at io.lettuce.core.cluster.ClusterFutureSyncInvocationHandler.handleInvocation(ClusterFutureSyncInvocationHandler.java:123)
 at io.lettuce.core.internal.AbstractInvocationHandler.invoke(AbstractInvocationHandler.java:80)
 at com.sun.proxy.$Proxy134.mget(Unknown Source)
 at org.springframework.data.redis.connection.lettuce.LettuceStringCommands.mGet(LettuceStringCommands.java:119)
 ... 15 common frames omitted

我急忙检查redis集群,发现集群里的各节点都一切正常,且cpu和内存使用率还不到百分之二十,看着这一切,我突然陷入漫长的沉思,到底是哪里出现问题......百度一番,发现不少人都出现过类似情况的,有人说把超时timeout设置更大一些就可以解决了。我按照这样的解决方法,把超时timeout的值设置到更大后,依然没有解决该超时问题。

其中,springboot操作redis的依赖包是——

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>

集群配置——

redis:
 timeout: 6000ms
 cluster:
 nodes:
  - xxx.xxx.x.xxx:6379
  - xxx.xxx.x.xxx:6379
  - xxx.xxx.x.xxx:6379
 jedis:
 pool:
  max-active: 1000
  max-idle: 10
  min-idle: 5
  max-wait: -1

点进spring-boot-starter-data-redis进去,发现里面包含了lettuce的依赖:

看到一些网友说,springboot1.x默认使用的是jedis,到了Springboot2.x就默认使用了lettuce。我们可以简单验证一下,在redis驱动加载配置类里,输出一下RedisConnectionFactory信息:

@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class Configuration {
 @Bean
 public StringRedisTemplate redisTemplate(RedisConnectionFactory factory) {
  log.info("测试打印驱动类型:"+factory);
}

打印输出——

测试打印驱动类型:org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory@74ee761e

可见,这里使用正是是lettuce驱动连接,目前我暂时的解决办法,是当把它换成以前用的比较多的jedis驱动连接时,就没有再出现这个Command timed out after 6 second(s)问题了。

<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>

那么问题来了,Springboot2.x是如何默认使用了lettuce,这得去研究下里面的部分代码。我们可以可进入到Springboot2.x自动装配模块的redis部分,其中有一个RedisAutoConfiguration类,其主要作用是对Springboot自动配置连接redis类:

@Configuration(
 proxyBeanMethods = false
)
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
 public RedisAutoConfiguration() {
 }
 ......省略
}

这里只需要关注里面的一行注解:

@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})

这就意味着使用spring-boot-starter-data-redis依赖时,可自动导入lettuce和jedis两种驱动,按理来说,不会同时存在两种驱动,这样没有太大意义,因此,这里的先后顺序就很重要了,为什么这么说呢?

分别进入到LettuceConnectionConfiguration.class与JedisConnectionConfiguration.class当中,各自展示本文需要涉及到的核心代码:

//LettuceConnectionConfiguration
@ConditionalOnClass({RedisClient.class})
class LettuceConnectionConfiguration extends RedisConnectionConfiguration {
 ......省略
 @Bean
 @ConditionalOnMissingBean({RedisConnectionFactory.class})
 LettuceConnectionFactory redisConnectionFactory(ObjectProvider<LettuceClientConfigurationBuilderCustomizer> builderCustomizers, ClientResources clientResources) throws UnknownHostException {
  LettuceClientConfiguration clientConfig = this.getLettuceClientConfiguration(builderCustomizers, clientResources, this.getProperties().getLettuce().getPool());
  return this.createLettuceConnectionFactory(clientConfig);
 }
}
//JedisConnectionConfiguration
@ConditionalOnClass({GenericObjectPool.class, JedisConnection.class, Jedis.class})
class JedisConnectionConfiguration extends RedisConnectionConfiguration {
 ......省略
 @Bean
 @ConditionalOnMissingBean({RedisConnectionFactory.class})
 JedisConnectionFactory redisConnectionFactory(ObjectProvider<JedisClientConfigurationBuilderCustomizer> builderCustomizers) throws UnknownHostException {
  return this.createJedisConnectionFactory(builderCustomizers);
 }
}

可见,LettuceConnectionConfiguration.class与JedisConnectionConfiguration.class当中都有一个相同的注解 @ConditionalOnMissingBean({RedisConnectionFactory.class}),这是说,假如RedisConnectionFactory这个bean已经被注册到容器里,那么与它相似的其他Bean就不会再被加载注册,简单点说,对LettuceConnectionConfiguration与JedisConnectionConfiguration各自加上 @ConditionalOnMissingBean({RedisConnectionFactory.class})注解,两者当中只能加载注册其中一个到容器里,另外一个就不会再进行加载注册。

那么,问题就来了,谁会先被注册呢?

这就回到了上面提到的一句,@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})这一句里的先后顺序很关键,LettuceConnectionConfiguration在前面,就意味着,LettuceConnectionConfiguration将会被注册。

可见,Springboot默认是使用lettuce来连接redis的。

当我们引入spring-boot-starter-data-redis依赖包时,其实就相当于引入lettuce包,这时就会使用lettuce驱动,若不想使用该默认的lettuce驱动,直接将lettuce依赖排除即可。

<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>

然后再引入jedis依赖——

<dependency>
 <groupId>redis.clients</groupId>
 <artifactId>jedis</artifactId>
</dependency>

这样,在进行RedisAutoConfiguration的导入注解时,因为没有找到lettuce依赖,故而这注解@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})的第二个位置上的JedisConnectionConfiguration就有效了,就可以被注册到容器了,当做springboot操作redis的驱动。

lettuce与jedis两者有什么区别呢?

lettuce:底层是用netty实现,线程安全,默认只有一个实例。

jedis:可直连redis服务端,配合连接池使用,可增加物理连接。

根据异常提示找到出现错误的方法,在下列代码里的LettuceConverters.toBoolean(this.getConnection().zadd(key, score, value))——

public Boolean zAdd(byte[] key, double score, byte[] value) {
 Assert.notNull(key, "Key must not be null!");
 Assert.notNull(value, "Value must not be null!");
​
 try {
  if (this.isPipelined()) {
   this.pipeline(this.connection.newLettuceResult(this.getAsyncConnection().zadd(key, score, value), LettuceConverters.longToBoolean()));
   return null;
  } else if (this.isQueueing()) {
   this.transaction(this.connection.newLettuceResult(this.getAsyncConnection().zadd(key, score, value), LettuceConverters.longToBoolean()));
   return null;
  } else {
   return LettuceConverters.toBoolean(this.getConnection().zadd(key, score, value));
  }
 } catch (Exception var6) {
  throw this.convertLettuceAccessException(var6);
 }
}

LettuceConverters.toBoolean()是将long转为Boolean,正常情况下,this.getConnection().zadd(key, score, value)如果新增成功话,那么返回1,这样LettuceConverters.toBoolean(1)得到的是true,反之,如果新增失败,则返回0,即LettuceConverters.toBoolean(0),还有第三种情况,就是这个this.getConnection().zadd(key, score, value)方法出现异常,什么情况下会出现异常呢?

应该是,connection连接失败的时候。

这就意味着,以lettuce驱动连接redis的过程当中,会出现连接断开的情况,导致无法新增成功,超过一定时间还没有正常,就会出现连接超时的情况。

至于是什么原因导致的断开连接,暂时还没有比较好思路,暂且把这个问题留着,等慢慢研究看是否能找到问题所在,若有大神指点,也感激不尽。

到此这篇关于Springboot2.x集成lettuce连接redis集群报超时异常Command timed out after 6 second(s)的文章就介绍到这了,更多相关Springboot连接redis超时内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

时间: 2021-03-18

Springboot2.X集成redis集群(Lettuce)连接的方法

前提:搭建好redis集群环境,搭建方式请看:https://www.jb51.net/article/143749.htm 1. 新建工程,pom.xml文件中添加redis支持 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> 2

SpringBoot连接Redis2种模式解析

一.引入pom <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> 二.配置YML文件(二选一) 1.sentinel模式 server: port: 80 spring: redis: sentinel: nodes: 192.168

基于SpringBoot2.0默认使用Redis连接池的配置操作

SpringBoot2.0默认采用Lettuce客户端来连接Redis服务端的 默认是不使用连接池的,只有配置 redis.lettuce.pool下的属性的时候才可以使用到redis连接池 redis: cluster: nodes: ${redis.host.cluster} password: ${redis.password} lettuce: shutdown-timeout: 100 # 关闭超时时间 pool: max-active: 8 # 连接池最大连接数(使用负值表示没有限制

详解springboot配置多个redis连接

一.springboot nosql 简介 Spring Data提供其他项目,用来帮你使用各种各样的NoSQL技术,包括MongoDB, Neo4J, Elasticsearch, Solr, Redis,Gemfire, Couchbase和Cassandra.Spring Boot为Redis, MongoDB, Elasticsearch, Solr和Gemfire提供自动配置.你可以充分利用其他项目,但你需要自己配置它们. 1.1.Redis Redis是一个缓存,消息中间件及具有丰富

基于SpringBoot集成测试远程连接Redis服务的教程详解

前期准备 Linux虚拟机或者租用的云服务器:sudo安装redis,或者docker加载redis镜像.后者需要使用docker启用redis容器. 配置好redis.conf文件.注意:一定要注释 # bind 127.0.0.1 其他详细配置可参考我另一篇文章,不过能想到集成测试redis,配置文件应该已经配置好了. /sbin/iptables -I INPUT -p tcp --dport 6379 -j ACCEPT 开放6379端口 初始化SpringBoot项目使用Spring

SpringBoot初步连接redis详解

在初次用springboot连接redis的时候查看官方文档和一些博客会发现配置文件非常的多,这就导致了在学习的开始的时候是没有体验的,其实利用springboot连接redis的时候并不需要那么多的配置 首先开启redis服务器: 然后在springboot里面添加配置文件: # Redis数据库索引(默认为0) spring.redis.database=0 # Redis服务器地址 spring.redis.host=localhost # Redis服务器连接端口 spring.redi

springboot2整合redis使用lettuce连接池的方法(解决lettuce连接池无效问题)

lettuce客户端 Lettuce 和 Jedis 的都是连接Redis Server的客户端程序.Jedis在实现上是直连redis server,多线程环境下非线程安全(即多个线程对一个连接实例操作,是线程不安全的),除非使用连接池,为每个Jedis实例增加物理连接.Lettuce基于Netty的连接实例(StatefulRedisConnection),可以在多个线程间并发访问,且线程安全,满足多线程环境下的并发访问(即多个线程公用一个连接实例,线程安全),同时它是可伸缩的设计,一个连接

Android通过命令连接wifi的方法(解决usb不能用问题)

此方式 可以通过串口来操作 1.进入wpa_cli后,命令如下: # wpa_cli # scan # scan_results ;会显示搜索到的wifi信号列表,假设你的wifi ssid是wireless0 # add_net 会打印一个net编号,如果打印0 0 # set_network 0 ssid "wireless0" //wifi名称 # set_network 0 psk "123456" //123456是wireless0的密码. # sele

SpringBoot2整合Redis缓存三步骤代码详解

遵循SpringBoot三板斧 第一步加依赖 <!-- Redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- redis依赖commons-pool 这个依赖一定要添加 --> <

SpringBoot2整合Redis多数据源步骤详解

redis是一个基于内存的高性能key-value数据库,具有极高的读写速度.本文介绍 SpringBoot 和 Redis 的整合,以及如何在项目中具体应用 配置文件属性 spring: redis: database: 1 host: 192.168.50.144 port: 6379 password: timeout: 600 #Springboot2.0 不能设置为0 lettuce: pool: max-active: 50 max-wait: -1 max-idle: 8 min-

go语言操作redis连接池的方法

本文实例讲述了go语言操作redis连接池的方法.分享给大家供大家参考.具体实现方法如下: 复制代码 代码如下: func newPool(server, password string) *redis.Pool {     return &redis.Pool{         MaxIdle: 3,         IdleTimeout: 240 * time.Second,         Dial: func () (redis.Conn, error) {             c

SpringBoot2.3整合redis缓存自定义序列化的实现

1.引言 我们使用redis作为缓存中间件时,当我们第一次查询数据的时候,是去数据库查询,然后查到的数据封装到实体类中,实体类会被序列化存入缓存中,当第二次查数据时,会直接去缓存中查找被序列化的数据,然后反序列化被我们获取.我们在缓存中看到的序列化数据不直观,如果想看到类似json的数据格式,就需要自定义序列化规则. 2.整合redis pom.xml: <!--引入redis--> <dependency> <groupId>org.springframework.d

Spring Boot整合Redis的完整步骤

前言 实际 开发 中 缓存 处理是必须的,不可能我们每次客户端去请求一次 服务器 ,服务器每次都要去 数据库 中进行查找,为什么要使用缓存?说到底是为了提高系统的运行速度.将用户频繁访问的内容存放在离用户最近,访问速度最 快的 地方,提高用户的响 应速度,今天先来讲下在 springboot 中整合 redis 的详细步骤. 一.Spring Boot对Redis的支持 Spring对Redis的支持是使用Spring Data Redis来实现的,一般使用Jedis或者lettuce(默认),

Springboot以Repository方式整合Redis的方法

1 简介 Redis是高性能的NoSQL数据库,经常作为缓存流行于各大互联网架构中.本文将介绍如何在Springboot中整合Spring Data Redis,使用Repository的方式操作. 代码结构如下: 2 整合过程 2.1 安装Redis数据库 为了节省时间,就直接通过Docker来安装了,可以参考文章:Docker安装Redis并介绍漂亮的可视化客户端进行操作,可以快速安装并使用客户端进行查看和操作. 2.2 引入相关依赖 我们引入Springboot Web的依赖,以启动RES

Spring与Mybatis基于注解整合Redis的方法

基于这段时间折腾redis遇到了各种问题,想着整理一下.本文主要介绍基于Spring+Mybatis以注解的形式整合Redis.废话少说,进入正题. 首先准备Redis,我下的是Windows版,下载后直接启动redis-server就行了,见下图: 一,先上jar包 二,创建实体类 package com.sl.user.vo; import java.io.Serializable; import com.fasterxml.jackson.databind.PropertyNamingSt

SpringBoot 整合Redis 数据库的方法

Redis简介 Redis(官网: https://redis.io )是一个基于内存的日志型可持久化的缓存数据库,保存形式为key-value格式,Redis完全免费开源,它使用ANSI C语言编写.与其他的key - value缓存产品一样,Redis具有以下三个特点. • Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用: • Redis不仅支持简单的key-value类型数据,同时还提供字符串.链表.集合.有序集合和哈希等数据结构的存储: • R

SpringBoot整合Redis、ApachSolr和SpringSession的示例

本文介绍了SpringBoot整合Redis.ApachSolr和SpringSession,分享给大家,具体如下: 一.简介 SpringBoot自从问世以来,以其方便的配置受到了广大开发者的青睐.它提供了各种starter简化很多繁琐的配置.SpringBoot整合Druid.Mybatis已经司空见惯,在这里就不详细介绍了.今天我们要介绍的是使用SpringBoot整合Redis.ApacheSolr和SpringSession. 二.SpringBoot整合Redis Redis是大家比