Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( その30 )( Redis のクライアントライブラリを Jedis → Lettuce に変更する )
概要
記事一覧はこちらです。
Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( その29 )( build.gradle の dependencies から不要な記述を削除する ) の続きです。
- 今回の手順で確認できるのは以下の内容です。
- Spring Boot 2.0 Migration Guide - Redis に
Lettuce is now used instead of Jedis as the Redis driver when you use spring-boot-starter-data-redis.
という記述があり、spring-boot-starter-data-redis の依存関係に入っている Redis のクライアントライブラリが Jedis から Lettuce になっていることに気づきました。 - 以前 Redis Cluster への接続処理を Jedis で実装したのですが、Lettuce に変更します。
- Spring Boot 2.0 Migration Guide - Redis に
参照したサイト・書籍
Spring Data Redis
https://docs.spring.io/spring-data/data-redis/docs/current/reference/html/Redis Spring data with Lettuce: com.lambdaworks.redis.RedisCommandExecutionException: MOVED error
https://stackoverflow.com/questions/53402326/redis-spring-data-with-lettuce-com-lambdaworks-redis-rediscommandexecutionexcepRedisCommandExecutionException: MOVED error when used cluster configuration endpoint
https://jira.spring.io/browse/DATAREDIS-898LINE LIVE のチャットが?30,000+/min のコメント投稿を捌くようになるまで
https://www.slideshare.net/linecorp/line-live-30000min-98811987Lettuce Reference Guide
https://lettuce.io/core/release/reference/index.html- 7.2.1. Cluster-specific options を参考にしました。
目次
- spring-boot-starter-data-redis の依存関係に Lettuce が入っていることを確認する
- build.gradle を変更する
- 単純に JedisConnectionFactory → LettuceConnectionFactory に変更してみる
- Lettuce で failover 発生時にサーバの切替が認識されるように実装する
- build.gradle に spring-boot-configuration-processor を追加する
手順
spring-boot-starter-data-redis の依存関係に Lettuce が入っていることを確認する
gradlew dependencies
コマンドを実行すると spring-boot-starter-data-redis の依存関係に io.lettuce:lettuce-core が入っていることが確認できます。
+--- org.springframework.boot:spring-boot-starter-data-redis -> 2.0.6.RELEASE | +--- org.springframework.boot:spring-boot-starter:2.0.6.RELEASE (*) | +--- org.springframework.data:spring-data-redis:2.0.11.RELEASE | | +--- org.springframework.data:spring-data-keyvalue:2.0.11.RELEASE | | | +--- org.springframework.data:spring-data-commons:2.0.11.RELEASE (*) | | | +--- org.springframework:spring-context:5.0.10.RELEASE (*) | | | +--- org.springframework:spring-tx:5.0.10.RELEASE (*) | | | \--- org.slf4j:slf4j-api:1.7.25 | | +--- org.springframework:spring-tx:5.0.10.RELEASE (*) | | +--- org.springframework:spring-oxm:5.0.10.RELEASE | | | +--- org.springframework:spring-beans:5.0.10.RELEASE (*) | | | \--- org.springframework:spring-core:5.0.10.RELEASE (*) | | +--- org.springframework:spring-aop:5.0.10.RELEASE (*) | | +--- org.springframework:spring-context-support:5.0.10.RELEASE (*) | | \--- org.slf4j:slf4j-api:1.7.25 | \--- io.lettuce:lettuce-core:5.0.5.RELEASE | +--- io.projectreactor:reactor-core:3.1.6.RELEASE -> 3.1.10.RELEASE | | \--- org.reactivestreams:reactive-streams:1.0.2 | +--- io.netty:netty-common:4.1.24.Final -> 4.1.29.Final | +--- io.netty:netty-transport:4.1.24.Final -> 4.1.29.Final | | +--- io.netty:netty-buffer:4.1.29.Final | | | \--- io.netty:netty-common:4.1.29.Final | | \--- io.netty:netty-resolver:4.1.29.Final | | \--- io.netty:netty-common:4.1.29.Final | \--- io.netty:netty-handler:4.1.24.Final -> 4.1.29.Final | +--- io.netty:netty-buffer:4.1.29.Final (*) | +--- io.netty:netty-transport:4.1.29.Final (*) | \--- io.netty:netty-codec:4.1.29.Final | \--- io.netty:netty-transport:4.1.29.Final (*)
build.gradle を変更する
build.gradle の dependencies block から implementation("redis.clients:jedis")
を削除します。変更後、Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
単純に JedisConnectionFactory → LettuceConnectionFactory に変更してみる
src/main/java/ksbysample/webapp/lending/config/RedisClusterConfig.java を以下のように変更します。
package ksbysample.webapp.lending.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisClusterConfiguration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import java.util.List; /** * Redis Cluster 用 Configuration クラス */ @Data @Configuration @ConfigurationProperties(prefix = "spring.redis.cluster") public class RedisClusterConfig { List<String> nodes; /** * Redis Cluster に接続するための {@link LettuceConnectionFactory} オブジェクトを生成する * * @return {@link LettuceConnectionFactory} オブジェクト */ @Bean public RedisConnectionFactory connectionFactory() { return new LettuceConnectionFactory(new RedisClusterConfiguration(nodes)); } }
- connectionFactory メソッド内で、
JedisConnectionFactory
→LettuceConnectionFactory
に変更します。
動作確認してみます。docker-compose up -d
コマンドを実行した後、Tomcat を起動します。
Spring Actuator の health check で Redis に接続できていることを確認します。
docker exec -it redis-cluster-6382 redis-cli
コマンドで Redis に接続した後、cluster nodes
コマンドを実行してクラスタ構成を確認します。
今は以下の構成になっています。
- redis-cluster-6379(master) - redis-cluster-6382(slave)
- redis-cluster-6380(master) - redis-cluster-6383(slave)
- redis-cluster-6381(master) - redis-cluster-6384(slave)
まず http://localhost:8080/ にアクセスしてログイン画面を表示した後、tanaka.taro@sample.com / taro でログインします。
IntelliJ IDEA の Docker Plugin から Redis の master 3台(redis-cluster-6379, redis-cluster-6380, redis-cluster-6381)を停止します。
redis-cli から cluster nodes
コマンドを実行して以前の master のステータスが fail になっていること、slave → master に昇格していることを確認します。
ブラウザの右上の「ログアウト」リンクをクリックしてログアウトしてみます。JedisConnectionFactory の時はログアウトできたのですが、JedisConnectionFactory
→ LettuceConnectionFactory
に変更しただけの状態だとエラーになりログアウトできません(Ctrl+F5 を押して何度かリロードしてみましたがずっとエラーのままでした)。
コンソールには
org.springframework.data.redis.RedisSystemException: Redis exception; nested exception is io.lettuce.core.RedisException: io.lettuce.core.RedisConnectionException: Unable to connect to 172.23.136.33:6381
というエラーが出ていて、どうもライブラリの方で master が変わったことが認識できていないようです。
Tomcat を停止し、docker-compose down
コマンドを実行してコンテナも停止します。
Lettuce で failover 発生時にサーバの切替が認識されるように実装する
Spring Data Redis の記述だと分からなかったのですが、以下のドキュメントを見るとオプションの設定が必要だったようです。
- LINE LIVE のチャットが?30,000+/min のコメント投稿を捌くようになるまで
- Lettuce Reference Guide - 7.2.1. Cluster-specific options
src/main/java/ksbysample/webapp/lending/config/RedisClusterConfig.java を以下のように変更します。
package ksbysample.webapp.lending.config; import io.lettuce.core.cluster.ClusterClientOptions; import io.lettuce.core.cluster.ClusterTopologyRefreshOptions; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisClusterConfiguration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import java.time.Duration; import java.util.List; /** * Redis Cluster 用 Configuration クラス */ @Data @Configuration @ConfigurationProperties(prefix = "spring.redis.cluster") public class RedisClusterConfig { List<String> nodes; /** * Redis Cluster に接続するための {@link LettuceConnectionFactory} オブジェクトを生成する * * @return {@link LettuceConnectionFactory} オブジェクト */ @Bean public RedisConnectionFactory connectionFactory() { ClusterTopologyRefreshOptions clusterTopologyRefreshOptions = ClusterTopologyRefreshOptions.builder() .enablePeriodicRefresh(Duration.ofSeconds(30)) .enableAllAdaptiveRefreshTriggers() .build(); ClusterClientOptions clusterClientOptions = ClusterClientOptions.builder() .validateClusterNodeMembership(false) .topologyRefreshOptions(clusterTopologyRefreshOptions) .build(); LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder() .commandTimeout(Duration.ofSeconds(15)) .shutdownTimeout(Duration.ZERO) .clientOptions(clusterClientOptions) .build(); RedisClusterConfiguration serverConfig = new RedisClusterConfiguration(nodes); return new LettuceConnectionFactory(serverConfig, clientConfig); } }
動作確認してみます。先程と同じ手順で、docker-compose up -d
コマンド実行 → Tomcat 起動 → ログイン画面から tanaka.taro@sample.com / taro でログインする、まで実行します。
cluster nodes
コマンドの実行結果は以下のようになっており、構成は先程と同じです。
IntelliJ IDEA の Docker Plugin から Redis の master 3台(redis-cluster-6379, redis-cluster-6380, redis-cluster-6381)を停止します。
redis-cli から cluster nodes
コマンドを実行して以前の master のステータスが fail になっていること、slave → master に昇格していることを確認します。
ブラウザの右上の「ログアウト」リンクをクリックすると、今度はログアウトすることができました。
この後ログイン/ログアウトを繰り返したり、Redis の failover を何度も行ってみましたが、エラーは出ませんでした。
build.gradle に spring-boot-configuration-processor を追加する
@ConfigurationProperties を付与すると IntelliJ IDEA では以下のような画面(エディタの上部に Spring Boot Configuration Annotation Processor not found in classpath
のメッセージが表示され、@ConfigurationProperties に赤波下線が付く )になります。
build.gradle の dependencies block に compileOnly("org.springframework.boot:spring-boot-configuration-processor")
を追記して出ないようにします。
dependencies { .......... runtimeOnly("org.springframework.boot:spring-boot-devtools") compileOnly("org.springframework.boot:spring-boot-configuration-processor") implementation("org.springframework.session:spring-session-data-redis") ..........
Recommend the use of the compileOnly or annotationProcessor configurations for spring-boot-configuration-processor の Issue を見ると compileOnly ではなく annotationProcessor でも良さそうな気がしますが、annotationProcessor だとエディタ上のメッセージが消えず、build 実行時に compileTestJava タスクで 警告:次のオプションはどのプロセッサでも認識されませんでした: '[org.springframework.boot.configurationprocessor.additionalMetadataLocations]'
という警告が出るので compileOnly にしました。
履歴
2019/01/06
初版発行。