SIer だけど技術やりたいブログ

簡単 Spring SessionでKVSを用いたセッションストア

Java SpringBoot Spring

そもそもセッションってなに?

セッションはAPサーバ固有のデータ保存領域。
アプリ内でユーザのログイン情報などを保存するために使われることが多い。
4. クッキーとセッション (2) | TECHSCORE(テックスコア)

セッションはAPサーバ固有のデータ保存領域のため、APサーバが複数台あると、ロードバランサのふりわけによってはセッション情報が参照できなくなる可能性がある。

この問題を防ぐための方法はいくつかある。

  1. スティッキーセッション

    • クライアントとAPサーバが1:1になるように常に同じAPサーバにふりわける方法。
    • APサーバとユーザが紐づくため、負荷が高くなったときにAPサーバを動的に追加したとしても、うまく負荷が分散されない
  2. セッションレプリケーション

    • セッションを各APサーバ間で同期する方法。
    • APサーバが増えるたびにセッションレプリケーションのコストが指数的に増えていくため、うまくスケールしない
  3. データの保存先を外部ストレージに切り出す

    • RDBMSやKVSなどを利用してセッション情報を外部に持たせる方法。
    • HttpSessionのAPIはkeyとvalueの組み合わせを保存するAPIなので、KVSのほうが適している感じはする。
    • APサーバ・外部ストレージのそれぞれを別々にスケールさせていくことができる

Spring Sessionってなに?

セッション管理をKVSの機能を利用して実現することができるプロジェクト。

前回エントリで書いたHttpSessionの機能拡張を利用するだけなので、既存アプリがSpringを使っている必要はなく、導入すれば既存のHttpSessionのAPIを利用しているところが裏でKVSの実装に置き換わる。わざわざインタフェースでプログラミングしていた恩恵を受けてる感がある。

どんな動き?

HttpSessionの拡張の中で、Spring Data Redisを利用してRedisサーバに接続して、Javaオブジェクトをシリアライズした文字列をつっこんでるだけなんじゃないのー?…という感じがしたけど、そこまで確認してないし確認するスキルがない。知ってる人がいたら教えてほしい。

実装方法

今回はKVSにRedisを利用する。

Redisの準備

インストールして起動する。
redisドキュメント日本語訳 — redis 2.0.3 documentation

アプリケーションコード

pom.xmlに依存性を追加する。

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-redis</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.session</groupId>
  <artifactId>spring-session</artifactId>
</dependency>

接続先のRedisサーバの設定を記述する。
@EnableRedisHttpSessionを設定すると、HttpSessionの拡張を登録するspringSessionRepositoryFilterまで設定してくれる。
JedisはRedisのクライアント実装。(http://d.hatena.ne.jp/hrendoh/20110901/1314887550)

@EnableRedisHttpSession
public class SessionConfig {
    @Bean
    public JedisConnectionFactory connectionFactory(){
        JedisConnectionFactory factory =  new JedisConnectionFactory();
        factory.setPort(6379);
        factory.setHostName("localhost");
        return factory;
    }
}

springSessionRepositoryFilterを設定してくれるとはいうものの、そのためにはAbstractHttpSessionApplicationInitializerにConfigクラスを渡す必要がある。Spring使ってない人はweb.xmlに自分でFilterを記述することになるはず。

public class Initializer
        extends AbstractHttpSessionApplicationInitializer {
  public Initializer() {
    super(SessionConfig.class);
  }
}

HttpSessionを利用する側

HttpSessionを利用するだけ。ということは、既存アプリのセッションをKVSにしたいときはアプリ側の改修は必要ないはず。
@RestControllerなのに、stateを持ってるのはご容赦いただきたい。

@RestController
public class StatefulRestController {
  private static final String KEY = "name";

  @RequestMapping("change")
  public String start(@RequestParam("name") String name, HttpSession session){
     session.setAttribute(KEY , name);
     return "set " + name;
  }

  @RequestMapping("confirm")
  public String start(HttpSession session){
    String name = (String) session.getAttribute(KEY);
    if(name == null)
        return "unset";
    return name;
  }
}

動作確認する

redisがport:6379で起動してるのが前提。
APを2つ起動する。

java -jar target/session-client-0.0.1-SNAPSHOT.jar --server.port=8081
java -jar target/session-client-0.0.1-SNAPSHOT.jar --server.port=8082

ブラウザ経由で確認すると、別APなのにセッションが共有されているのがわかる。

localhost:8080/change?name=kimura\
set kimura

localhost:8081/confirm\
kimura

サンプル

サンプル書いた。
https://github.com/kimullaa/session-client

まとめ

  • Spring Session便利そう。
  • Redisでレプリケーションの設定すれば耐障害性が上がりそう。

参考にしたサイト