はじめに
先日からSpring Bootで作ったWebアプリに対してAzureの各種サービスを接続することをやっています。今回はAzure Cache for Redisに接続してみることにします。セッション情報をアプリケーションからRedisなどに保存することでスケーラブルなアプリケーションが実現できます。
前提
以下のバージョンで実装しました。
- Spring Boot 2.3.1.RELEASE
Spring Bootの設定
サンプルが公開されているので、それを参考に。
また、少しバージョンは古いですが、Qiitaに分かりやすい記事が乗っていましたのでこちらも参考にしました。
build.gradleの編集
まず、build.gradle
に以下の記述します。
implementation 'org.springframework.boot:spring-boot-starter-data-redis' implementation 'org.springframework.session:spring-session-data-redis'
セッション情報を保存するクラスを作成する
次にセッション情報を保存するクラスを作成します。こんな感じになりました。
package com.example.demo; // import省略 @SessionScope @Component public class SessionInfo implements Serializable { private static final long serialVersionUID = 1L; private long id; private String name; private Date createdAt; // getter/setter省略 }
セッションに情報を保存する
適当なControllerにセッションに情報を保存する処理を実装します。こんな感じの実装になりました。
package com.example.demo; // import省略 @RestController public class GreetingController { private static final String template = "Hello, %s!"; private final AtomicLong counter = new AtomicLong(); private final SessionInfo sessionInfo; public GreetingController(SessionInfo sessionInfo) { this.sessionInfo = sessionInfo; } @GetMapping("/") public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name) { if (sessionInfo.getName() == null) { sessionInfo.setId(counter.incrementAndGet()); sessionInfo.setName(name); sessionInfo.setCreatedAt(new Date()); } return new Greeting( sessionInfo.getId(), String.format(template, sessionInfo.getName()), sessionInfo.getCreatedAt() ); } @GetMapping("/goodbye") public String goodbye(HttpSession session) { Optional<String> name = Optional.ofNullable(sessionInfo.getName()); session.invalidate(); return "Goodbye " + name.orElse("anonymous"); } }
あわせてGreeting
クラスの内容を適宜修正しています。
application.propertiesにRedisの接続先を記述する
application.properties
にRedisの接続先を記述します。各項目をそのまま記述するのではなく、環境変数を介する感じのほうが良いかなと思います。
spring.session.store-type=redis spring.redis.host=${REDIS_HOST:cache} spring.redis.port=${REDIS_PORT:6379} spring.redis.password=${REDIS_PASSWORD:password}
docker-compose.ymlにRedisを追加する
ローカルでテストしやすいようにdocker-compose.yml
にRedisの設定を追加します。こんな感じ。
# 省略 cache: image: redis:6.0.5-alpine command: redis-server --requirepass password ports: - 6379:6379 app: image: demo:0.0.1-SNAPSHOT depends_on: - db - cache ports: - 8080:8080 environment: REDIS_HOST: cache REDIS_PORT: 6379 REDIS_PASSWORD: password # 省略
以上の記述で動くようになります。
Azure Cache for Redisを作成する
ようやく本論のAzure Cache for Redisを作成します。私は東日本リージョンで作成しましたが、作成に20分ほどかかりました。VMなどの他のサービスと異なり結構待たされました。
「非SSLポート(6379)」をどうするかは後で変更できるので作成時にあまり悩まなくても良いかなと思います。
接続してみる
作成したAzure Cache for Redisに対して接続してみます。docker-compose.yml
に追加したREDIS_HOST
・REDIS_PORT
・REDIS_PASSWORD
にそれぞれの値を設定します。REDIS_PASSWORD
はアクセスキーを表示させてコピペしてください。
...ですが、このままだとうまく接続できません。以下の例外が発生します。
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'enableRedisKeyspaceNotificationsInitializer' defined in class path resource [org/springframework/session/data/redis/config/annotation/web/http/RedisHttpSessionConfiguration.class]: Invocation of init method failed; nested exception is org.springframework.data.redis.RedisSystemException: Error in execution; nested exception is io.lettuce.core.RedisCommandExecutionException: ERR unknown command `CONFIG`, with args beginning with: `GET`, `notify-keyspace-events`,
この問題については @setoazusa さんが以下のブログにて解決方法を示されています。
バージョンが上がっていくつかのメソッドが非推奨になっていたのでそれを解決するために私は以下の実装にしました。
package com.example.demo; // import文省略 @Configuration @EnableRedisHttpSession public class SessionConfig extends AbstractHttpSessionApplicationInitializer { @Bean public RedisSerializer<Object> springSessionDefaultRedisSerializer() { return new GenericJackson2JsonRedisSerializer(); } @Bean public static ConfigureRedisAction configureRedisAction() { return ConfigureRedisAction.NO_OP; } @Bean public LettuceConnectionFactory connectionFactory() { RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(); redisStandaloneConfiguration.setHostName(System.getenv("REDIS_HOST")); redisStandaloneConfiguration.setPassword(System.getenv("REDIS_PASSWORD")); redisStandaloneConfiguration.setPort(Integer.parseInt(System.getenv("REDIS_PORT"))); LettuceClientConfiguration lettuceClientConfiguration = LettuceClientConfiguration.builder().useSsl().build(); return new LettuceConnectionFactory(redisStandaloneConfiguration, lettuceClientConfiguration); } }
この記述をすると例外が発生せずにうまく動きました。Azure PortalからAzure Cache for Redisへのコンソール機能にアクセスできるので中身を確認してみるとよいかと思います。
ソース
以下に上げました。省略したものもあるかと思うので、あわせてみてください。