はじめに
Memorystore にGAE/FE(Java runtime)で動くSpringbootのセッションを管理させてみましたのでまとめます。
オープンソースのRedisとMemorystoreの差異は Memorystoreリファレンスのこのへん に記載されています。
はたしてすんなり動くのか?
TL;DR
Springのリファレンス の通り下記の指定をすれば動きます。
@Bean
public static ConfigureRedisAction configureRedisAction() {
return ConfigureRedisAction.NO_OP;
}
Memorystoreでは CONFIGコマンドがブロックされている ため、この対応が必要になるようです。
Springのリファレンスによると上記の結果 SessionDeletedEvent
と SessionExpiredEvent
は取れなくなるとのこと。
上記以外の注意点として、MemorystoreとGAEは 同一プロジェクト、同一リージョン、同一ネットワークにないと接続できません。
試してみる
上記を試してみます。
事前準備
まず下記が無ければ用意します。
- JDK
- Maven
- 適当なGCPプロジェクト
- Cloud SDK
- 適当なSpringbootサンプルソース
Cloud SDKは 使用するアカウントとプロジェクトを指定してログイン しておきます。
また適当なSpringbootサンプルソースはGitHubにあげました。
Memorystoreを起動する
GCPのウェブUIのMemorystoreのところでインスタンスを作成します。
設定は初期値の通りでいいですが、使用するプロジェクトでもし過去にGAEをデプロイした
ことがある場合は、GAEとMemorystoreのリージョンを合わせておく必要があります。
GAEのリージョンはウェブUIのダッシュボードの右上か コマンドライン(describeのlocationId) で確認できます。
Memorystoreの起動が完了すると次の通りインスタンスに割り当てられたIPが確認できます。
適当なSpringbootサンプルソースについて
適当なサンプルはGitHubにあげました。
以下サンプルの補足です。
サンプル画面
次の通り訪問回数をセッションに保存して表示するだけの画面を作りました。
@RestController
public class HelloSpringbootController {
@RequestMapping({ "/" })
public String index(HttpSession session) {
Integer visits = (Integer) session.getAttribute("visits-by-session-key");
visits = visits == null ? 1 : ++visits;
session.setAttribute("visits-by-session-key", visits);
return "visits by session => " + visits;
}
}
プロファイルの切替
ローカルと本番(GAE)のプロファイルの切替は app.yaml で環境変数 SPRING_PROFILES_ACTIVE
を指定して行いました。
ローカルは指定無し、本番は'prod'を指定しています。
runtime: java
env: flex
handlers:
- url: /.*
script: this field is required, but ignored
resources:
cpu: 1
memory_gb: 2
# Specify the active profile of spring for production environment by environment variable.
env_variables:
SPRING_PROFILES_ACTIVE: 'prod'
なお本サンプルで切り替えているのは application.yml と application-prod.yml だけです。
RedisのIPを指定する
先ほどのMemorystore起動時に割り当てられたIPを application-prod.yml で指定します。
# Setting for production environment
spring:
redis:
host: 10.0.0.3
port: 6379
ConfigureRedisActionを指定する
次のような感じ。
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 60)
public class HelloSpringbootSessionConfig {
@Bean
public ConfigureRedisAction configureRedisAction() {
return ConfigureRedisAction.NO_OP;
}
// イベントが飛んでくるかどうかを確認
@EventListener
public void confirmEvent(ApplicationEvent event) {
System.out.println(LocalDateTime.now().toString() + " --> " + event.getClass().getSimpleName());
}
}
修正点まとめ
素のSpringbootアプリをGAE + Memorystoreに載せるのに必要な修正点をまとめると次の通り。
修正(または追加)対象 | 補足 |
---|---|
pom.xml | appengine-maven-pluginを追加 |
app.yaml | GAEの設定 |
HelloSpringbootSessionConfig.java | ConfigureRedisAction.NO_OPを指定 |
application-prod.yml | MemorystoreのIPを指定 |
GAEを初期化する
もし該当プロジェクトで過去にGAEを使用したことがない場合は、まず最初にGAEで使用するリージョンを指定します。
次の通りウェブUIで行うか、または コマンドライン で行います。
ここではGAEのリージョンの指定まで出来ればよいです。
GAEのリージョンはMemorystoreと合わせておく必要があります。
またGAEのリージョンは一度設定すると変更できません。
GAEにデプロイする
最後に > mvn appengine:deploy
でデプロイします。
> mvn appengine:deploy
[INFO] Scanning for projects...
[INFO]
[INFO] ----------< com.example:hello-springboot-with-session-on-gae >----------
[INFO] Building hello-springboot-with-session-on-gae 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] >>> appengine-maven-plugin:1.3.2:deploy (default-cli) > package @ hello-springboot-with-session-on-gae >>>
[INFO]
[INFO] --- maven-resources-plugin:3.0.2:resources (default-resources) @ hello-springboot-with-session-on-gae ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 2 resources
[INFO] Copying 0 resource
(以下略)
以上でGAEにSpringbootアプリがデプロイされました。
アプリにアクセスしてみる
GCPのウェブUIからリンクをたどるか、または コマンドライン でブラウザを開きます。
以上で動作が確認できます。
参考までイベントが飛んで来たらわかるようにしました。
ログを見るとリファレンス記載の通り SessionDeletedEvent
と SessionExpiredEvent
は飛んでないことが確認できます。
いやオレはイベント使うんじゃ・・という場合
MemorystoreではCONFIGコマンドはブロックされていますが、一部のconfig値はウェブUIほかで設定可能です。
これをちょっと試してみました。
Springのリファレンスを元に次の通りnotify-keyspace-eventsにEgx をセットして・・
そしてブラウザでアクセス後にログを見てみると次のような感じ。
※ 今回試した環境ではGAEインスタンスが2台あり、SessionCreatedEvent
と SessionExpiredEvent
は2台とも受け取っているようでした。
※ またSessionDeletedEvent
はまったく見当たりませんでした。
以上です。