Spring Session + Redis(AWS ElastiCache)でセッション管理を行う方法【導入編】
目次
こんにちは、堀部です。
Springフレームワークを使ったWebアプリケーションで、Spring SessionとRedisを使ったセッション管理を行うことになりました。
実装方法を調べてみていろいろと試行錯誤した結果、実現はできたものの、とんでもなくドはまりしたので実装方法を書いておきたいと思います。
本番環境のRedisはAWSのElastiCacheを使っていますが、今回はひとまずローカル環境にインストールしたRedisに接続する方法をご紹介します。
ちなみにこの方法でCODEMATIC Java版にも導入可能であることを確認済みです。
Redisってそもそも何? 使うメリットは?
簡単に言うとNoSQLのインメモリDBです。
ロードバランサ―等で複数台のアプリケーションサーバを稼働させるとセッションの共有はできませんが、Redisでセッションを外部管理すると1台がフェールオーバーしてもセッションを維持することができます。
また、システム稼働中でもユーザのログアウトを待つことなく、アプリケーションのデプロイが行えたりもします。
以下のサイトに詳しい解説がありますので、読んでみてください。
Redisをインストールする
RedisはWindows環境をサポートしていないので、Windows環境にRedisをインストールする場合はMSOpenTechを利用してください。
下記サイトから、msiをダウンロードしてインストールします。
https://github.com/MicrosoftArchive/redis/releases
インストールが完了したら、コマンドプロンプトから以下のコマンドを実行します。正しく表示されればインストール完了です。
>redis-cli 127.0.0.1:6379> set test test OK 127.0.0.1:6379> get test "test" 127.0.0.1:6379>
対象バージョン
今回使用したバージョンは以下の通りです。
Java | 1.8 |
---|---|
Spring Framework | 4.2.1 |
jedis | 2.9.0 |
Redis | 3.2.6 |
Spring Sessionを導入する
Spring Sessionを導入するために設定変更を行います。RedisのJavaクライアントライブラリはJedisを使います。
pom.xml
<!-- spring session --> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>1.8.13.RELEASE</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> <version>1.3.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-commons</artifactId> <version>1.13.13.RELEASE</version> </dependency>
spring-data-commonsはバージョンによっては以下のエラーが出るため、明示的に指定しています。バージョンの組み合わせによっては追加は不要になるかもしれません。
Caused by: java.lang.NoClassDefFoundError: org/springframework/data/geo/Metric at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.<clinit>(JedisConnectionFactory.java:72) ~[spring-data-redis-1.8.13.RELEASE.jar:na] at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_171] at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source) ~[na:1.8.0_171] at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source) ~[na:1.8.0_171] at java.lang.reflect.Constructor.newInstance(Unknown Source) ~[na:1.8.0_171] at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:147) ~[spring-beans-4.2.1.RELEASE.jar:4.2.1.RELEASE] at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:89) ~[spring-beans-4.2.1.RELEASE.jar:4.2.1.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1098) ~[spring-beans-4.2.1.RELEASE.jar:4.2.1.RELEASE] ... 54 common frames omitted Caused by: java.lang.ClassNotFoundException: org.springframework.data.geo.Metric at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1275) ~[catalina.jar:na] at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1104) ~[catalina.jar:na] ... 62 common frames omitted
web.xml
<filter> <filter-name>springSessionRepositoryFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSessionRepositoryFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
applicationContext.xml
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:host-name="localhost" p:port="6379" p:password=""/> <bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate" p:connection-factory-ref="jedisConnectionFactory" /> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="jedisConnectionFactory" p:key-serializer-ref="stringKeySerializer"/> <bean id ="stringKeySerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/> <context:annotation-config/> <bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>
最後にセッションに持たせるJavaオブジェクトは、対象のクラスにSerializableを付けておきます。
これで設定は完了です。対象のアプリケーションを動かした後に、redis-cliの「keys *」を実行しセッション情報が格納されていれば成功です。
>redis-cli 127.0.0.1:6379> keys * 1) "spring:session:expirations:0000000000000" 2) "spring:session:sessions:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" 3) "spring:session:sessions:expires:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" 127.0.0.1:6379>
まとめ
いかがでしたでしょうか? 一見簡単そうに見える導入ですが、いざやってみると結構ハマりました。
基本的な設定はこれで完了しましたが、本番環境のRedisはSSL接続が必要だったり、クラスタ構成で冗長化されていたりと、何かと追加の設定が必要になります。そのような場合の設定方法についても、後日ご紹介したいと思います。