關(guān)于Springboot2.x集成lettuce連接redis集群報(bào)超時(shí)異常Command timed out after 6 second(s)
背景:最近在對一新開發(fā)Springboot系統(tǒng)做壓測,發(fā)現(xiàn)剛開始壓測時(shí),可以正常對redis集群進(jìn)行數(shù)據(jù)存取,但是暫停幾分鐘后,接著繼續(xù)用jmeter進(jìn)行壓測時(shí),發(fā)現(xiàn)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集群,發(fā)現(xiàn)集群里的各節(jié)點(diǎn)都一切正常,且cpu和內(nèi)存使用率還不到百分之二十,看著這一切,我突然陷入漫長的沉思,到底是哪里出現(xiàn)問題......百度一番,發(fā)現(xiàn)不少人都出現(xiàn)過類似情況的,有人說把超時(shí)timeout設(shè)置更大一些就可以解決了。我按照這樣的解決方法,把超時(shí)timeout的值設(shè)置到更大后,依然沒有解決該超時(shí)問題。
其中,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
點(diǎn)進(jìn)spring-boot-starter-data-redis進(jìn)去,發(fā)現(xiàn)里面包含了lettuce的依賴:

看到一些網(wǎng)友說,springboot1.x默認(rèn)使用的是jedis,到了Springboot2.x就默認(rèn)使用了lettuce。我們可以簡單驗(yàn)證一下,在redis驅(qū)動(dòng)加載配置類里,輸出一下RedisConnectionFactory信息:
@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class Configuration {
@Bean
public StringRedisTemplate redisTemplate(RedisConnectionFactory factory) {
log.info("測試打印驅(qū)動(dòng)類型:"+factory);
}
打印輸出——
測試打印驅(qū)動(dòng)類型:org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory@74ee761e
可見,這里使用正是是lettuce驅(qū)動(dòng)連接,目前我暫時(shí)的解決辦法,是當(dāng)把它換成以前用的比較多的jedis驅(qū)動(dòng)連接時(shí),就沒有再出現(xiàn)這個(gè)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是如何默認(rèn)使用了lettuce,這得去研究下里面的部分代碼。我們可以可進(jìn)入到Springboot2.x自動(dòng)裝配模塊的redis部分,其中有一個(gè)RedisAutoConfiguration類,其主要作用是對Springboot自動(dòng)配置連接redis類:
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
public RedisAutoConfiguration() {
}
......省略
}
這里只需要關(guān)注里面的一行注解:
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
這就意味著使用spring-boot-starter-data-redis依賴時(shí),可自動(dòng)導(dǎo)入lettuce和jedis兩種驅(qū)動(dòng),按理來說,不會(huì)同時(shí)存在兩種驅(qū)動(dòng),這樣沒有太大意義,因此,這里的先后順序就很重要了,為什么這么說呢?
分別進(jìn)入到LettuceConnectionConfiguration.class與JedisConnectionConfiguration.class當(dāng)中,各自展示本文需要涉及到的核心代碼:
//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當(dāng)中都有一個(gè)相同的注解 @ConditionalOnMissingBean({RedisConnectionFactory.class}),這是說,假如RedisConnectionFactory這個(gè)bean已經(jīng)被注冊到容器里,那么與它相似的其他Bean就不會(huì)再被加載注冊,簡單點(diǎn)說,對LettuceConnectionConfiguration與JedisConnectionConfiguration各自加上 @ConditionalOnMissingBean({RedisConnectionFactory.class})注解,兩者當(dāng)中只能加載注冊其中一個(gè)到容器里,另外一個(gè)就不會(huì)再進(jìn)行加載注冊。
那么,問題就來了,誰會(huì)先被注冊呢?
這就回到了上面提到的一句,@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})這一句里的先后順序很關(guān)鍵,LettuceConnectionConfiguration在前面,就意味著,LettuceConnectionConfiguration將會(huì)被注冊。
可見,Springboot默認(rèn)是使用lettuce來連接redis的。
當(dāng)我們引入spring-boot-starter-data-redis依賴包時(shí),其實(shí)就相當(dāng)于引入lettuce包,這時(shí)就會(huì)使用lettuce驅(qū)動(dòng),若不想使用該默認(rèn)的lettuce驅(qū)動(dòng),直接將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>
這樣,在進(jìn)行RedisAutoConfiguration的導(dǎo)入注解時(shí),因?yàn)闆]有找到lettuce依賴,故而這注解@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})的第二個(gè)位置上的JedisConnectionConfiguration就有效了,就可以被注冊到容器了,當(dāng)做springboot操作redis的驅(qū)動(dòng)。
lettuce與jedis兩者有什么區(qū)別呢?
lettuce:底層是用netty實(shí)現(xiàn),線程安全,默認(rèn)只有一個(gè)實(shí)例。
jedis:可直連redis服務(wù)端,配合連接池使用,可增加物理連接。
根據(jù)異常提示找到出現(xiàn)錯(cuò)誤的方法,在下列代碼里的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轉(zhuǎn)為Boolean,正常情況下,this.getConnection().zadd(key, score, value)如果新增成功話,那么返回1,這樣LettuceConverters.toBoolean(1)得到的是true,反之,如果新增失敗,則返回0,即LettuceConverters.toBoolean(0),還有第三種情況,就是這個(gè)this.getConnection().zadd(key, score, value)方法出現(xiàn)異常,什么情況下會(huì)出現(xiàn)異常呢?
應(yīng)該是,connection連接失敗的時(shí)候。
這就意味著,以lettuce驅(qū)動(dòng)連接redis的過程當(dāng)中,會(huì)出現(xiàn)連接斷開的情況,導(dǎo)致無法新增成功,超過一定時(shí)間還沒有正常,就會(huì)出現(xiàn)連接超時(shí)的情況。
至于是什么原因?qū)е碌臄嚅_連接,暫時(shí)還沒有比較好思路,暫且把這個(gè)問題留著,等慢慢研究看是否能找到問題所在,若有大神指點(diǎn),也感激不盡。
到此這篇關(guān)于Springboot2.x集成lettuce連接redis集群報(bào)超時(shí)異常Command timed out after 6 second(s)的文章就介紹到這了,更多相關(guān)Springboot連接redis超時(shí)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中綴表達(dá)式轉(zhuǎn)后綴表達(dá)式流程詳解
中綴表達(dá)式是一個(gè)通用的算術(shù)或邏輯公式表示方法。,中綴表達(dá)式不容易被計(jì)算機(jī)解析,但仍被許多程序語言使用,因?yàn)樗先藗兊钠毡橛梅?。本文介紹了實(shí)現(xiàn)中綴表達(dá)式的方法,需要的可以參考一下2022-09-09
SpringData實(shí)現(xiàn)自定義Redis緩存的序列化機(jī)制和過期策略
Spring Data Redis緩存通過提供靈活的配置選項(xiàng),使開發(fā)者能夠根據(jù)業(yè)務(wù)需求自定義序列化方式和過期策略,下面就來具體介紹一下,感興趣的可以了解一下2025-04-04
SpringCloud-Alibaba-Nacos啟動(dòng)失敗解決方案
這篇文章主要介紹了SpringCloud-Alibaba-Nacos啟動(dòng)失敗解決方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
Nacos動(dòng)態(tài)配置管理機(jī)制方式
這篇文章主要介紹了Nacos動(dòng)態(tài)配置管理機(jī)制方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07
Spring Cloud Ribbon負(fù)載均衡器處理方法
這篇文章主要介紹了Spring Cloud Ribbon負(fù)載均衡器處理方法,看看是如何獲取服務(wù)實(shí)例,獲取以后做了哪些處理,處理后又是如何選取服務(wù)實(shí)例的,需要的朋友可以參考下2018-02-02
啟用Spring事務(wù)管理@EnableTransactionManagement示例解析
這篇文章主要為大家介紹了啟用Spring事務(wù)管理@EnableTransactionManagement示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09

