Apache、mod_jk、Tomcat、redisson、Redisでセッション共有を試してみる

## 1. はじめに

今回は１台のホスト上でTomcatのセッション共有を試してみたいと思います。セッションの保存先をTomcatのメモリから外部永続化（DB、ファイル、KVS等）するだけで良いと思っているメンバがいたため試せる環境を構築してみました。
Javaアプリでセッションを利用するにはjsessionidが重要になりますが、jsessionidはオリジン（Origin）とコンテキストパスが異なれば連携されません。`example1.com`のjsessionidが別のサイトである`example2.com`に連携されたら困ってしまいます。同じ理由で`localhost:8080`と`localhost:8081`は別のサイトと認識され、jsessionidが連携されることはありません。つまりWebブラウザが同じサイトと認識してjsessionidを連携するにはオリジンとコンテキストパスを同じにする必要があります。
Apache、mod_jk、Tomcat、redisson、Redisを利用して、セッション共有の検証環境を構築する手順について説明します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/195748/29649f77-863a-959a-b163-2ecfe80aab34.png)

## 2. Redisのダウンロードと実行

WindowsでRedisを動かすため古いバージョンになりますが以下のサイトからZip版をダウンロードし、任意のディレクトリに展開後、`redis-server.exe`を実行します。

https://github.com/MicrosoftArchive/redis/releases
Redis-x64-3.0.504.zip

## 3. Tomcatのダウンロード

以下のサイトからZip版をダウンロードし、任意のディレクトリに展開します。展開したディレクトリを`CATALINA_HOME`環境変数として定義します。

https://tomcat.apache.org/download-10.cgi
apache-tomcat-10.1.26-windows-x64.zip

## 4. redissonのダウンロードと設定

以下のサイトから`redisson-all-3.37.0.jar`と`redisson-tomcat-10-3.37.0.jar`をダウンロードし、`%CATALINA_HOME%/lib`に配置します。

https://redisson.org/articles/redis-based-tomcat-session-management.html

redisサーバにアクセスするための設定ファイル（redisson.conf）を作成し、`%CATALINA_HOME%/conf`に配置します。

```yml:%CATALINA_HOME%/conf/redisson.conf
singleServerConfig:
  address: "redis://127.0.0.1:6379"

threads: 0
nettyThreads: 0
transportMode: NIO
```

Tomcatのセッションの保存先をRedisにするため、`%CATALINA_BASE%/conf/context.xml`に設定を追加します。

```xml:%CATALINA_BASE%/conf/context.xml
<Context>

    <!-- Default set of monitored resources. If one of these changes, the    -->
    <!-- web application will be reloaded.                                   -->
    <WatchedResource>WEB-INF/web.xml</WatchedResource>
    <WatchedResource>WEB-INF/tomcat-web.xml</WatchedResource>
    <WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>

    <!-- Uncomment this to enable session persistence across Tomcat restarts -->
    <!--
    <Manager pathname="SESSIONS.ser" />
    -->
    <!-- ⭐️以下の内容を追加 -->
    <Manager className="org.redisson.tomcat.RedissonSessionManager"
        configPath="${catalina.base}/conf/redisson.conf"
        readMode="REDIS" updateMode="DEFAULT" broadcastSessionEvents="false"
        keyPrefix=""/>
</Context>
```

## 5. Tomcatの起動

セッション共有を確認するため、２つのTomcatインスタンスを起動させます。詳細については[CATALINA_BASE環境変数を利用してTomcatを複数起動する方法](https://qiita.com/5zm/items/5256ecf2fb8fa37449b8)を参照ください。

```bat:apserver1_startup.bat
@echo off
set JAVA_HOME=C:¥tools¥corretto¥jdk17.0.12_7
set CATALINA_HOME=C:¥tools¥apache-tomcat-10.1.26

set SHUTDOWN_PORT=8005
set SERVER_PORT=8081
set AJP_PORT=8009

call %CATALINA_HOME%\bin\startup.bat
```

```bat:apserver2_startup.bat
@echo off
set JAVA_HOME=C:¥tools¥corretto¥jdk17.0.12_7
set CATALINA_HOME=C:¥tools¥apache-tomcat-10.1.26
set CATALINA_BASE=C:¥tools¥apserver_2

set SHUTDOWN_PORT=8006
set SERVER_PORT=8084
set AJP_PORT=8019

call %CATALINA_HOME%¥bin¥startup.bat
```

## 6. Apache(httpd)のダウンロードと設定

以下のサイトからWindows向けのZip版をダウンロードに、任意のディレクトリに展開します。展開したディレクトリを`SRVROOT`として定義します。

https://www.apachelounge.com/download/
httpd-2.4.62-240904-win64-VS17.zip

```conf:conf/httpd.conf
Define SRVROOT "C:/tools/httpd-2.4.62/Apache24"

ServerRoot "${SRVROOT}"
```

## 7. mod_jkのダウンロードと設定

以下のサイトからmod_jkモジュールをダウンロードし、`mod_jk.so`を`SRVROOT/modules`に配置します。

https://www.apachelounge.com/download/
mod_jk-1.2.50-win64-VS17.zip

Apacheがmod_jkモジュールを読み込むように設定ファイル（httpd.conf）を変更します。
`JkMount  /examples/* lb`は`/examples/*`のリクエストはmod_jkのlbワーカーで処理（リクエストをTomcatに転送）させるという記述です。

```conf:httpd.confに以下を追記
# Load mod_jk module
LoadModule    jk_module  modules/mod_jk.so

<IfModule jk_module>
    # Where to find workers.properties
    # Update this path to match your conf directory location (put workers.properties next to httpd.conf)
    JkWorkersFile conf/workers.properties
    # Where to put jk shared memory
    # Update this path to match your local state directory or logs directory
    JkShmFile     logs/mod_jk.shm
    # Where to put jk logs
    # Update this path to match your logs directory location (put mod_jk.log next to access_log)
    JkLogFile     logs/mod_jk.log
    # Set the jk log level [debug/error/info]
    JkLogLevel    info
    # Select the timestamp log format
    JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
    JkRequestLogFormat "%w %p %s %v %V %T"
    # Send everything for context /examples to worker named lb (ajp13)
    JkMount  /examples/* lb
</IfModule>
```

ApacheからTomcatへリクエストを転送させるための設定ファイル（`SRVROOT/conf/workers.properties`）を作成します。
portはTomcatのAJP13プロトコルの待ち受けポート番号と合わせる必要があります。
lbfactorはワーカーの振り分け割合を示すもので、同じ値の場合は均等（２台の場合はそれぞれ50％）となります。また、セッション共有のため`sticky_session`は不要なので無効（false）に設定します。

```properties:conf/workers.properties
worker.list=lb

worker.lb.type=lb
worker.lb.balance_workers=worker1,worker2

worker.worker1.port=8009
worker.worker1.host=127.0.0.2
worker.worker1.type=ajp13
worker.worker1.lbfactor=1
worker.worker1.sticky_session=false

worker.worker2.port=8019
worker.worker2.host=127.0.0.3
worker.worker2.type=ajp13
worker.worker2.lbfactor=1
worker.worker2.sticky_session=false
```

## 8. Apache(httpd)の起動

準備が終わったので最後にApache(httpd)を起動します。`SRVROOT/bin`にある`httpd.exe`を実行します。

## 9. 動作確認

Webブラウザを起動し`http://localhost/examples/`にアクセスすると、Apache Tomcat Examplesの画面が表示されると思います。
「Servlet examples」の「Sessions」ー「Execute」リンクからセッションのサンプル機能でセッション共有の機能を確認できます。
セッションに値を設定しつつ、Tomcatインスタンスの一方を停止（Ctrl＋C）させてもセッションサンプルは正常に動作するはずです。また、停止させたTomcatを起動し、一定時間が経過すると再度リクストが転送されることも確認できると思います。
`git bash`をインストールしている場合、`tail`コマンドが使えるので`tail -f`でTomcatのアクセスログを表示すると２つのTomcatのどちらでリクエストを処理をしているのか判断しやすくなります。

## 10. さいごに

今回はWindowsのローカル上でRedis＆Redissonによるセッション共有を確認する方法について説明しました。redissonのaddressやmod_jk(workers.porperties)のhostを別ホストに設定すれば、複数ホスト（ノード）でのセッション共有のサーバ構築ができるかと思います。
なお、クラウド環境で実行する場合、Apacheやmod_jkを利用せずに実現することができます。例えばAWSであればALBでHTTP（S）を受け付け、バックエンドのTomcatの8080ポートにリクエストを転送できます。Cookies(jsessionid)のドメイン・パスを確認するとWebブラウザから見えるホスト（ApacheやALB等）とセッションの関係が分かるかと思います。