Home  >  Article  >  Database  >  How to solve the timeout exception when Springboot2.x integrates lettuce and connects to the redis cluster

How to solve the timeout exception when Springboot2.x integrates lettuce and connects to the redis cluster

PHPz
PHPzforward
2023-05-27 19:43:051920browse

Background: Recently, I was doing a stress test on the Springboot system developed by Yixin. I found that when I first started the stress test, I could access data from the redis cluster normally. However, after a few minutes of pause, and then I continued to use jmeter to perform the stress test, I found that Redis started to suddenly and crazyly burst out abnormal prompts: Command timed out after 6 second(s)...

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

I hurriedly checked the redis cluster and found that all nodes in the cluster were normal, and the CPU and memory usage were It’s less than 20%. Looking at all this, I suddenly fell into a long meditation. What is the problem? After searching on Baidu, I found that many people have experienced similar situations. Some people said that the timeout setting should be changed. A bigger one will do the trick. I followed this solution and set the timeout value to a larger value, but the timeout problem was still not solved.

Among them, the dependency package for springboot to operate redis is——

  1 <dependency>  2     <groupId>org.springframework.boot</groupId>  3     <artifactId>spring-boot-starter-data-redis</artifactId>  4 </dependency>

Cluster configuration——

  1 redis:  2   timeout: 6000ms  3   cluster:  4     nodes:  5       - xxx.xxx.x.xxx:6379  6       - xxx.xxx.x.xxx:6379  7       - xxx.xxx.x.xxx:6379  8   jedis:  9     pool: 10       max-active: 1000 11       max-idle: 10 12       min-idle: 5 13       max-wait: -1

Click on spring-boot-starter-data-redis and find it inside Contains lettuce dependencies:

How to solve the timeout exception when Springboot2.x integrates lettuce and connects to the redis cluster

springboot1.x uses jedis by default, and springboot2.x uses lettuce by default. We can simply verify that in the redis driver loading configuration class, output the RedisConnectionFactory information:

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

Print output——

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

It can be seen that the lettuce driver connection is used here , therefore, when it is replaced with the jedis driver connection that was used frequently before, the problem of Command timed out after 6 second(s) no longer occurs.

  1 <dependency>  2     <groupId>org.springframework.boot</groupId>  3     <artifactId>spring-boot-starter-data-redis</artifactId>  4     <exclusions>  5         <exclusion>  6             <groupId>io.lettuce</groupId>  7             <artifactId>lettuce-core</artifactId>  8         </exclusion>  9     </exclusions> 10 </dependency> 11 <dependency> 12     <groupId>redis.clients</groupId> 13     <artifactId>jedis</artifactId> 14 </dependency>

Then the question is, how does Springboot2.x use lettuce by default? We need to study some of the code inside. We can enter the redis part of the Springboot2. ##

  1 @Configuration(  2     proxyBeanMethods = false  3 )  4 @ConditionalOnClass({RedisOperations.class})  5 @EnableConfigurationProperties({RedisProperties.class})  6 @Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})  7 public class RedisAutoConfiguration {  8     public RedisAutoConfiguration() {  9    } 10    ......省略 11 }

This means that when using the spring-boot-starter-data-redis dependency, both lettuce and jedis drivers can be automatically imported. Logically speaking, there will not be two drivers at the same time, which does not make much sense. , therefore, the order here is very important. Why do you say this?

Enter LettuceConnectionConfiguration.class and JedisConnectionConfiguration.class respectively, each showing the core code that this article needs to involve:

  1   2 @Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})  3

It can be seen that LettuceConnectionConfiguration.class and JedisConnectionConfiguration.class have the same Annotate @ConditionalOnMissingBean({RedisConnectionFactory.class}), which means that if the RedisConnectionFactory bean has been registered in the container, then other beans similar to it will no longer be loaded and registered. To put it simply, for LettuceConnectionConfiguration and JedisConnectionConfiguration respectively With the @ConditionalOnMissingBean({RedisConnectionFactory.class}) annotation, only one of the two can be loaded and registered into the container, and the other one will not be loaded and registered.

So, the question is, who will be registered first?

This goes back to the sentence mentioned above, the order in @Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class}) is very important. LettuceConnectionConfiguration is in front, which means that LettuceConnectionConfiguration will be register.

It can be seen that Springboot uses lettuce to connect to redis by default.

When we introduce the spring-boot-starter-data-redis dependency package, it is actually equivalent to introducing the lettuce package. At this time, the lettuce driver will be used. If you do not want to use the default lettuce driver, directly use lettuce Just exclude dependencies.

  1 //LettuceConnectionConfiguration  2 @ConditionalOnClass({RedisClient.class})  3 class LettuceConnectionConfiguration extends RedisConnectionConfiguration {  4    ......省略  5     @Bean  6     @ConditionalOnMissingBean({RedisConnectionFactory.class})  7     LettuceConnectionFactory redisConnectionFactory(ObjectProvider<LettuceClientConfigurationBuilderCustomizer> builderCustomizers, ClientResources clientResources) throws UnknownHostException {  8         LettuceClientConfiguration clientConfig = this.getLettuceClientConfiguration(builderCustomizers, clientResources, this.getProperties().getLettuce().getPool());  9         return this.createLettuceConnectionFactory(clientConfig); 10    } 11 } 12 //JedisConnectionConfiguration 13 @ConditionalOnClass({GenericObjectPool.class, JedisConnection.class, Jedis.class}) 14 class JedisConnectionConfiguration extends RedisConnectionConfiguration { 15    ......省略 16     @Bean 17     @ConditionalOnMissingBean({RedisConnectionFactory.class}) 18     JedisConnectionFactory redisConnectionFactory(ObjectProvider<JedisClientConfigurationBuilderCustomizer> builderCustomizers) throws UnknownHostException { 19         return this.createJedisConnectionFactory(builderCustomizers); 20    } 21 } 22

Then introduce the jedis dependency——

  1 <dependency>  2     <groupId>org.springframework.boot</groupId>  3     <artifactId>spring-boot-starter-data-redis</artifactId>  4     <exclusions>  5         <exclusion>  6             <groupId>io.lettuce</groupId>  7             <artifactId>lettuce-core</artifactId>  8         </exclusion>  9     </exclusions> 10 </dependency>

In this way, when performing the import annotation of RedisAutoConfiguration, because the lettuce dependency is not found, this annotation @Import({LettuceConnectionConfiguration.class , the JedisConnectionConfiguration in the second position of JedisConnectionConfiguration.class}) is now valid and can be registered to the container as the driver for springboot to operate redis.

What is the difference between lettuce and jedis?

lettuce: The bottom layer is implemented in netty, thread-safe, and has only one instance by default.

jedis: Can be directly connected to the redis server and used with the connection pool to increase physical connections.

Find the error method according to the exception prompt, lettuceConverters.toBoolean(this.getConnection().zadd(key, score, value)) in the following code——

  1 <dependency>  2     <groupId>redis.clients</groupId>  3     <artifactId>jedis</artifactId>  4 </dependency>

LettuceConverters.toBoolean() converts long to Boolean. Under normal circumstances, this.getConnection().zadd(key, score, value) returns 1 if the addition is successful, so LettuceConverters.toBoolean(1) gets is true, otherwise, if the new addition fails, 0 is returned, that is, LettuceConverters.toBoolean(0). There is also a third case, that is, an exception occurs in this.getConnection().zadd(key, score, value) method. What? What happens if an exception occurs?

It should be when the connection connection fails.

The above is the detailed content of How to solve the timeout exception when Springboot2.x integrates lettuce and connects to the redis cluster. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:yisu.com. If there is any infringement, please contact admin@php.cn delete