背景: 最近、Yixin が開発した Springboot システムでストレス テストを行っていました。最初にストレス テストを開始したときは、Redis クラスターのデータに正常にアクセスできることがわかりました。しかし、数分間停止すると、その後、jmeter を使用してストレス テストを実行し続けたところ、Redis が突然異常なプロンプトを出し始めたことがわかりました: コマンドは 6 秒後にタイムアウトしました...
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 とメモリの使用率も 20% 未満でした。これをすべて見て、突然長い瞑想に陥ってしまいました。何が問題ですか? Baidu で検索した後、同様の状況を経験した人はたくさんいるようで、タイムアウト設定を変更する必要があると言う人もいました。設定を大きくすればうまくいきます。この解決策に従い、タイムアウト値をより大きな値に設定しましたが、タイムアウトの問題はまだ解決されていません。
このうち、springboot が redis を動作させるための依存関係パッケージは、——
1 <dependency> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-data-redis</artifactId> 4 </dependency>
クラスター構成——
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 をクリックして、レタスの依存関係が含まれています:
springboot1.x はデフォルトで jedis を使用し、springboot2.x はデフォルトでレタスを使用します。 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 }
印刷出力——
测试打印驱动类型:org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory@74ee761e
レタス ドライバー接続が使用されていることがわかります。そこで、以前よく使われていた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 部分を入力できます。##
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 }これは、spring-boot-starter-data-redis 依存関係を使用するときに、lettuce ドライバーと jedis ドライバーの両方を自動的にインポートできることを意味します。論理的に言えば、 2 人のドライバーを同時に操作することはあまり意味がありません。したがって、ここでの順序は非常に重要です。なぜこのように言えるのでしょうか? LettuceConnectionConfiguration.class と JedisConnectionConfiguration.class をそれぞれ入力します。それぞれ、この記事が必要とするコア コードを示しています。
1 2 @Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class}) 3LettuceConnectionConfiguration.class と JedisConnectionConfiguration.class が同じであることがわかります。 @ConditionalOnMissingBean({RedisConnectionFactory.class}) アノテーションを付けます。これは、RedisConnectionFactory Bean がコンテナーに登録されている場合、それに類似した他の Bean はロードおよび登録されなくなります。簡単に言うと、LettuceConnectionConfiguration と JedisConnectionConfiguration に対してそれぞれ@ConditionalOnMissingBean({RedisConnectionFactory.class}) アノテーションでは、2 つのうち 1 つだけがコンテナーにロードおよび登録でき、もう 1 つはロードおよび登録されません。 そこで問題は、誰が最初に登録されるかということです。 上記の文に戻りますが、@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class}) の順序は非常に重要で、LettuceConnectionConfiguration が前にあるということは、LettuceConnectionConfiguration が登録されることを意味します。 Springboot はデフォルトで redis への接続にレタスを使用していることがわかります。 spring-boot-starter-data-redis 依存関係パッケージを導入すると、実際には lettuce パッケージを導入するのと同じになります。このとき、lettuce ドライバーが使用されます。デフォルトのレタスドライバー。レタスを直接使用します。依存関係を除外するだけです。
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次に、jedis の依存関係を導入します——
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>
このように、RedisAutoConfiguration の import アノテーションを実行すると、lettuce の依存関係が見つからないため、このアノテーション @Import({ LettuceConnectionConfiguration.class (JedisConnectionConfiguration.class の 2 番目の位置にある JedisConnectionConfiguration)) が有効になり、SpringBoot が Redis を操作するためのドライバーとしてコンテナーに登録できるようになりました。
レタスとジェディスの違いは何ですか?
lettuce: 最下層は netty で実装されており、スレッドセーフであり、デフォルトではインスタンスが 1 つだけあります。 jedis: Redis サーバーに直接接続し、接続プールと併用して物理接続を増やすことができます。 次のコードの例外プロンプト lettuceConverters.toBoolean(this.getConnection().zadd(key,score,value)) に従ってエラー メソッドを見つけます——1 <dependency> 2 <groupId>redis.clients</groupId> 3 <artifactId>jedis</artifactId> 4 </dependency>
LettuceConverters.toBoolean() は、long を Boolean に変換します。通常の状況では、追加が成功した場合、this.getConnection().zadd(key,score,value) は 1 を返すため、LettuceConverters.toBoolean(1) は true を取得し、それ以外の場合は、新しい追加が失敗した場合、0 が返されます (LettuceConverters.toBoolean(0))。3 番目のケース、つまり this.getConnection().zaadd(key,score,value) メソッドで例外が発生する場合もあります。え? 例外が発生するとどうなりますか?
接続接続に失敗した場合のはずです。
以上がSpringboot2.x がレタスを統合し、Redis クラスターに接続するときにタイムアウト例外を解決する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。