かんがるーさんの日記

最近自分が興味をもったものを調べた時の手順等を書いています。今は Spring Boot をいじっています。

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 - RedisLettuce 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 に変更します。

参照したサイト・書籍

  1. Spring Data Redis
    https://docs.spring.io/spring-data/data-redis/docs/current/reference/html/

  2. Redis Spring data with Lettuce: com.lambdaworks.redis.RedisCommandExecutionException: MOVED error
    https://stackoverflow.com/questions/53402326/redis-spring-data-with-lettuce-com-lambdaworks-redis-rediscommandexecutionexcep

  3. RedisCommandExecutionException: MOVED error when used cluster configuration endpoint
    https://jira.spring.io/browse/DATAREDIS-898

  4. LINE LIVE のチャットが?30,000+/min のコメント投稿を捌くようになるまで
    https://www.slideshare.net/linecorp/line-live-30000min-98811987

  5. Lettuce Reference Guide
    https://lettuce.io/core/release/reference/index.html

目次

  1. spring-boot-starter-data-redis の依存関係に Lettuce が入っていることを確認する
  2. build.gradle を変更する
  3. 単純に JedisConnectionFactory → LettuceConnectionFactory に変更してみる
  4. Lettuce で failover 発生時にサーバの切替が認識されるように実装する
  5. 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 メソッド内で、JedisConnectionFactoryLettuceConnectionFactory に変更します。

動作確認してみます。docker-compose up -d コマンドを実行した後、Tomcat を起動します。

Spring Actuator の health check で Redis に接続できていることを確認します。

f:id:ksby:20190106135844p:plain

docker exec -it redis-cluster-6382 redis-cli コマンドで Redis に接続した後、cluster nodes コマンドを実行してクラスタ構成を確認します。

f:id:ksby:20190106140128p:plain

今は以下の構成になっています。

  • 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 でログインします。

f:id:ksby:20190106140559p:plain f:id:ksby:20190106140639p:plain

IntelliJ IDEA の Docker Plugin から Redis の master 3台(redis-cluster-6379, redis-cluster-6380, redis-cluster-6381)を停止します。

f:id:ksby:20190106141051p:plain

redis-cli から cluster nodes コマンドを実行して以前の master のステータスが fail になっていること、slave → master に昇格していることを確認します。

f:id:ksby:20190106141240p:plain

ブラウザの右上の「ログアウト」リンクをクリックしてログアウトしてみます。JedisConnectionFactory の時はログアウトできたのですが、JedisConnectionFactoryLettuceConnectionFactory に変更しただけの状態だとエラーになりログアウトできません(Ctrl+F5 を押して何度かリロードしてみましたがずっとエラーのままでした)。

f:id:ksby:20190106141514p:plain

コンソールには 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 の記述だと分からなかったのですが、以下のドキュメントを見るとオプションの設定が必要だったようです。

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 コマンドの実行結果は以下のようになっており、構成は先程と同じです。

f:id:ksby:20190106163424p:plain

IntelliJ IDEA の Docker Plugin から Redis の master 3台(redis-cluster-6379, redis-cluster-6380, redis-cluster-6381)を停止します。

redis-cli から cluster nodes コマンドを実行して以前の master のステータスが fail になっていること、slave → master に昇格していることを確認します。

f:id:ksby:20190106163734p:plain

ブラウザの右上の「ログアウト」リンクをクリックすると、今度はログアウトすることができました。

f:id:ksby:20190106163838p:plain

この後ログイン/ログアウトを繰り返したり、Redis の failover を何度も行ってみましたが、エラーは出ませんでした。

build.gradle に spring-boot-configuration-processor を追加する

@ConfigurationProperties を付与すると IntelliJ IDEA では以下のような画面(エディタの上部に Spring Boot Configuration Annotation Processor not found in classpath のメッセージが表示され、@ConfigurationProperties に赤波下線が付く )になります。

f:id:ksby:20190106213918p:plain

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
初版発行。