배경: 최근 새로 개발된 Springboot 시스템에서 스트레스 테스트를 하던 중 처음 스트레스 테스트를 시작했을 때 정상적으로 Redis 클러스터의 데이터에 액세스할 수 있다는 것을 발견했습니다. 계속해서 jmeter를 사용하여 스트레스 테스트를 수행했는데, redis가 실패했다는 것을 발견했습니다. 갑자기 미친 듯이 비정상적인 메시지가 나타났습니다: 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
급히 Redis 클러스터를 확인하여 발견했습니다. 클러스터에 있는 모든 노드는 정상이고, CPU와 메모리 사용량은 100% 미만이라는 것을 스물, 이것저것 보다가 문득 뭐가 문제인지 고민에 빠졌습니다... 바이두에서 검색해보니, 많은 사람들이 비슷한 상황을 경험한 것을 발견했습니다. 어떤 사람들은 시간 초과를 더 큰 값으로 설정하면 문제가 해결될 수 있다고 말했습니다. 이 해결 방법에 따라 시간 초과 값을 더 큰 값으로 설정했지만 시간 초과 문제는 여전히 해결되지 않았습니다.
그 중 springboot가 Redis를 운영하기 위한 의존성 패키지는——
1 <dependency> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-data-redis</artifactId> 4 </dependency>
Cluster 구성——
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
spring-boot-starter-data-redis를 클릭하고 lettuce의 의존성이 포함되어 있는지 확인하세요.
springboot1 .x는 기본적으로 jedis를 사용하고, Springboot2.x는 기본적으로 lettuce를 사용합니다. Redis 드라이버 로딩 구성 클래스에서 RedisConnectionFactory 정보를 출력하는 것을 간단하게 확인할 수 있습니다:
1 @Configuration 2 @AutoConfigureAfter(RedisAutoConfiguration.class) 3 public class Configuration { 4 @Bean 5 public StringRedisTemplate redisTemplate(RedisConnectionFactory factory) { 6 log.info("测试打印驱动类型:"+factory); 7 }
Print 출력 -
测试打印驱动类型:org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory@74ee761e
여기서는 lettuce 드라이버 연결이 사용되는 것을 볼 수 있으므로 이전 연결로 교체할 때 더 많은 jedis 드라이버가 연결되면 이 명령은 6초 후에 시간 초과 문제가 더 이상 발생하지 않습니다.
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>
그럼 질문은 Springboot2.x가 어떻게 기본적으로 상추를 사용하는가 하는 것입니다. 내부 코드 중 일부를 연구해야 합니다. Springboot2의 redis 부분을 입력할 수 있습니다. 이는 spring-boot-starter-data-redis 종속성을 사용할 때 논리적으로 말하면 두 개의 드라이버가 동시에 자동으로 임포트될 수 없음을 의미합니다. , 별로 의미가 없습니다. 따라서 여기의 순서는 매우 중요합니다. 왜 그렇게 말합니까?
LettuceConnectionConfiguration.class 및 JedisConnectionConfiguration.class를 각각 입력하면 각각 이 기사에 필요한 핵심 코드가 표시됩니다.
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 }
LettuceConnectionConfiguration.class와 JedisConnectionConfiguration.class 모두 동일한 주석 @ConditionalOnMissingBean({RedisConnectionFactory.class)을 갖고 있음을 알 수 있습니다. }) 이는 RedisConnectionFactory Bean이 컨테이너에 등록된 경우 이와 유사한 다른 Bean은 로드 및 등록되지 않음을 의미합니다. 간단히 말하면 @ConditionalOnMissingBean({RedisConnectionFactory.class}) 주석 중 하나만 추가하면 됩니다. 두 개는 컨테이너에 로드 및 등록될 수 있으며, 다른 하나는 다시 로드 및 등록되지 않습니다.
그렇다면 누가 먼저 등록할 것인가 하는 질문이 생깁니다.
위 문장으로 돌아가서, @Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class}) 문장의 순서가 중요합니다. 이는 LettuceConnectionConfiguration이 등록된다는 의미입니다.
Springboot는 기본적으로 Redis에 연결하기 위해 lettuce를 사용하는 것을 볼 수 있습니다.
spring-boot-starter-data-redis 종속성 패키지를 도입하면 실제로는 lettuce 패키지를 도입하는 것과 같습니다. 이때 기본 lettuce 드라이버를 사용하지 않으려면 lettuce 드라이버가 사용됩니다. , 양상추 종속성을 직접 제외할 수 있습니다.
1 2 @Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class}) 3
그런 다음 jedis 종속성을 도입합니다.——
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
상추와 제디스의 차이점은 무엇인가요? lettuce: 맨 아래 레이어는 스레드로부터 안전한 netty로 구현되며 기본적으로 인스턴스가 하나만 있습니다.
jedis: Redis 서버에 직접 연결하고 연결 풀과 함께 사용하여 물리적 연결을 늘릴 수 있습니다.
예외 프롬프트에 따라 오류 메서드를 찾으세요. 다음 코드에서 LettuceConverters.toBoolean(this.getConnection().zadd(key, Score, value))——
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>
위 내용은 Springboot2.x가 양상추를 통합하고 Redis 클러스터에 연결할 때 시간 초과 예외를 해결하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!