Tomcat ver 9.0.13 HttpSession永続化

Tomcat2号機の作成方法
〇1号機のTomcatホームディレクトリ毎コピーして別名ディレクトリとする。
〇Tomcatホームのconfの下のserver.xmlのポートを書き換える。1号機とポートバッティングしないように。)

```
[root@localhost conf]# less server.xml
                         certificateFile="conf/localhost-rsa-cert.pem"
                         certificateChainFile="conf/localhost-rsa-chain.pem"
                         type="RSA" />
        </SSLHostConfig>
    </Connector>
    -->

    <!-- Define an AJP 1.3 Connector on port 8009 -->
    <Connector port="8010" protocol="AJP/1.3" redirectPort="8443" />


    <!-- An Engine represents the entry point (within Catalina) that processes
         every request.  The Engine implementation for Tomcat stand alone
         analyzes the HTTP headers included with the request, and passes them
         on to the appropriate Host (virtual host).
         Documentation at /docs/config/engine.html -->

    <!-- You should set jvmRoute to support load-balancing via AJP ie :
    <Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
    -->
    <Engine name="Catalina" defaultHost="localhost">
      <!--For clustering, please take a look at documentation at:
          /docs/cluster-howto.html  (simple how to)
          /docs/config/cluster.html (reference documentation) -->
      <!--
      <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
      -->

      <!-- Use the LockOutRealm to prevent attempts to guess user passwords
           via a brute-force attack -->
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <!-- This Realm uses the UserDatabase configured in the global JNDI
             resources under the key "UserDatabase".  Any edits
             that are performed against this UserDatabase are immediately
             available for use by the Realm.  -->
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
               resourceName="UserDatabase"/>
      </Realm>
      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">
        <!-- SingleSignOn valve, share authentication between web applications
             Documentation at: /docs/config/valve.html -->
        <!--
        <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
        -->

        <!-- Access log processes all example.
             Documentation at: /docs/config/valve.html
             Note: The pattern used is equivalent to using pattern="common" -->
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log" suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />

      </Host>
    </Engine>
  </Service>
</Server>
```

httpd.confの設定

```
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
<IfModule proxy_module>
ProxyRequests Off
<Proxy balancer://mycluster/>
BalancerMember http://192.168.80.139:8080
BalancerMember http://192.168.80.139:8081
</Proxy>
ProxyPass / balancer://mycluster/
ProxyPassReverse / balancer://mycluster/
</IfModule>
```

SELinux環境下で必要な追加設定

```
[root@localhost conf]# /usr/sbin/setsebool httpd_can_network_connect 1
[root@localhost conf]# /usr/sbin/setsebool -P httpd_can_network_connect 1
```

サンプルのJavaアプリ

```
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		PrintWriter out = response.getWriter();
		HttpSession session = request.getSession();
		String port = (String)session.getAttribute("port");
		if(port == null) {
			port = String.valueOf(request.getLocalPort());
			session.setAttribute("port", port);
		}	
		out.println("port:" + String.valueOf(request.getLocalPort()));
		out.println("port(session):" + port);
	}
```

ブラウザでhttp://localhost/TestWeb/SessionTestServletへアクセスすると下記の2パターンを交互にレスポンスする動きとなる。

```
port:8080
port(session):8080
```

と

```
port:8081
port(session):8081
```

の2パターン
ただし、この状態ではセッションは引き継がれない挙動。

PostgreSQLのインストールと設定

```
[root@localhost conf]# yum list | grep postgre
pcp-pmda-postgresql.x86_64             4.1.0-5.el7_6               ol7_latest
postgresql.i686                        9.2.24-1.el7_5              ol7_latest
postgresql.x86_64                      9.2.24-1.el7_5              ol7_latest
・・・省略・・・
qt5-qtbase-postgresql.x86_64           5.9.2-3.el7                 ol7_latest
[root@localhost conf]# yum install postgresql.x86_64
[root@localhost conf]# yum install postgresql-server.x86_64
[root@localhost home]# passwd postgres
ユーザー postgres のパスワードを変更。
新しいパスワード:
新しいパスワードを再入力してください:
passwd: すべての認証トークンが正しく更新できました。
[root@localhost pgsql]# su - postgres
-bash-4.2$ initdb
データベースシステム内のファイルの所有者は"postgres"ユーザでした。
このユーザがサーバプロセスを所有しなければなりません。

データベースクラスタはロケール"ja_JP.UTF-8"で初期化されます。
したがってデフォルトのデータベース符号化方式はUTF8に設定されました。
initdb: ロケール"ja_JP.UTF-8"用の適切なテキスト検索設定が見つかりません
デフォルトのテキスト検索設定はsimpleに設定されました。

ディレクトリ/var/lib/pgsql/dataの権限を設定しています ... ok
サブディレクトリを作成しています ... ok
デフォルトのmax_connectionsを選択しています ... 100
デフォルトの shared_buffers を選択しています ... 32MB
設定ファイルを作成しています ... ok
/var/lib/pgsql/data/base/1にtemplate1データベースを作成しています ... ok
pg_authidを初期化しています ... ok
依存関係を初期化しています ... ok
システムビューを作成しています ... ok
システムオブジェクトの定義をロードしています ... ok
照合順序を作成しています ... ok
変換を作成しています ... ok
ディレクトリを作成しています ... ok
組み込みオブジェクトに権限を設定しています ... ok
情報スキーマを作成しています ... ok
PL/pgSQL サーバサイド言語をロードしています ... ok
template1データベースをバキュームしています ... ok
template1からtemplate0へコピーしています ... ok
template1からpostgresへコピーしています ... ok

警告: ローカル接続向けに"trust"認証が有効です。
pg_hba.confを編集する、もしくは、次回initdbを実行する時に-Aオプショ
ン、または、--auth-localおよび--auth-hostを使用することで変更するこ
とができます。

成功しました。以下を使用してデータベースサーバを起動することができます。

    postmaster -D /var/lib/pgsql/data
または
    pg_ctl -D /var/lib/pgsql/data -l logfile start
-bash-4.2$ pg_ctl start -w
サーバの起動完了を待っています....完了
サーバ起動完了
-bash-4.2$ psql
psql (9.2.24)
"help" でヘルプを表示します.
postgres=# create database tomcat;
CREATE DATABASE
postgres=# \l
                                         データベース一覧
   名前    |  所有者  | エンコーディング |  照合順序   | Ctype(変換演算子) |      アクセス権
-----------+----------+------------------+-------------+-------------------+-----------------------
 postgres  | postgres | UTF8             | ja_JP.UTF-8 | ja_JP.UTF-8       |
 template0 | postgres | UTF8             | ja_JP.UTF-8 | ja_JP.UTF-8       | =c/postgres          +
           |          |                  |             |                   | postgres=CTc/postgres
 template1 | postgres | UTF8             | ja_JP.UTF-8 | ja_JP.UTF-8       | =c/postgres          +
           |          |                  |             |                   | postgres=CTc/postgres
 tomcat    | postgres | UTF8             | ja_JP.UTF-8 | ja_JP.UTF-8       |
(4 行)

postgres=# \connect tomcat
データベース "tomcat" にユーザ"postgres"として接続しました。
tomcat=# CREATE TABLE tomcat_sessions
tomcat-# (
tomcat(#   session_id varchar(100) NOT NULL,
tomcat(#   valid_session char(1) NOT NULL,
tomcat(#   max_inactive int4 NOT NULL,
tomcat(#   last_access int8 NOT NULL,
tomcat(#   app_name varchar(255),
tomcat(#   session_data bytea,
tomcat(#   PRIMARY KEY (session_id)
tomcat(# );
NOTICE:  CREATE TABLE / PRIMARY KEYはテーブル"tomcat_sessions"に暗黙的なインデックス"tomcat_sessions_pkey"を作成します
CREATE TABLE
tomcat=# select * from tomcat_sessions;
 session_id | valid_session | max_inactive | last_access | app_name | session_data
------------+---------------+--------------+-------------+----------+--------------
(0 行)

tomcat=# CREATE INDEX idx_tomcat_sessions_app_name ON tomcat_sessions(app_name);
CREATE INDEX
```

Tomcat側の設定
1号機、2号機ともに同じ内容で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 disable session persistence across Tomcat restarts -->
    <!--
    <Manager pathname="" />
    -->
    <Manager className="org.apache.catalina.session.PersistentManager"
        maxIdleBackup="1"
        minIdleSwap="0"
        maxIdleSwap="0"
        processExpiresFrequency="1"
        saveOnRestart='true'
        >
        <Store className="org.apache.catalina.session.JDBCStore"
                connectionURL="jdbc:postgresql://localhost:5432/tomcat?user=postgres&amp;password=postgres"
                driverName="org.postgresql.Driver"
                sessionAppCol="app_name"
                sessionDataCol="session_data"
                sessionIdCol="session_id"
                sessionLastAccessedCol="last_access"
                sessionMaxInactiveCol="max_inactive"
                sessionTable="tomcat_sessions"
                sessionValidCol="valid_session"
                />
    </Manager>
</Context>
```

https://jdbc.postgresql.org/download.htmlからJDBCドライバをダウンロードしてlibの下にファイルを配置する。（1号機、2号機共通）
サーバー起動、停止してブラウザから同じアプリへアクセスすると

```
port:8080
port(session):8080
```

と

```
port:8081
port(session):8080
```

のようにport(session)の値が固定化される。（変更前はportとport(session）が同じ値）

postgresql側でも下記のようにセッションがtableに格納されていることが確認できる。

```
tomcat=# select * from tomcat_sessions;
            session_id            | valid_session | max_inactive |  last_access  |          app_name           |

                                             session_data

----------------------------------+---------------+--------------+---------------+-----------------------------+------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------
 8DA3DC17D3BE9DD9979B217E70018D1E | 1             |         1800 | 1546751100173 | /Catalina/localhost/TestWeb | \xaced00057372000e6a6176612e6c616e672e4c6f6e673b8be49
0cc8f23df0200014a000576616c7565787200106a6176612e6c616e672e4e756d62657286ac951d0b94e08b020000787000000168218cc5047371007e000000000168218cc50d737200116a6176612e6c616e6
72e496e746567657212e2a0a4f781873802000149000576616c75657871007e000100000708737200116a6176612e6c616e672e426f6f6c65616ecd207280d59cfaee0200015a000576616c756578700073710
07e0006017371007e000000000168218cc50d74002038444133444331374433424539444439393739423231374537303031384431457371007e000400000001740004706f727474000438303831
 50EDC72B35204FF4EC923087882ECDD5 | 1             |         1800 | 1546751130386 | /Catalina/localhost/TestWeb | \xaced00057372000e6a6176612e6c616e672e4c6f6e673b8be49
0cc8f23df0200014a000576616c7565787200106a6176612e6c616e672e4e756d62657286ac951d0b94e08b020000787000000168218ccb407371007e000000000168218d3b12737200116a6176612e6c616e6
72e496e746567657212e2a0a4f781873802000149000576616c75657871007e000100000708737200116a6176612e6c616e672e426f6f6c65616ecd207280d59cfaee0200015a000576616c756578700073710
07e0006017371007e000000000168218d3b1274002035304544433732423335323034464634454339323330383738383245434444357371007e000400000001740004706f727474000438303830
(2 行)

tomcat=# select count(*) from tomcat_sessions;
 count
-------
     2
(1 行)
```

PostgreSQL障害時の挙動確認


```
-bash-4.2$ pg_ctl stop -m fast
```

でサーバー停止してブラウザからアプリケーションへアクセスするとHttpSession永続化設定前と同じ挙動になる。（500等のエラーは返らない仕様であることを確認）

```
-bash-4.2$ pg_ctl start -w
```


で再びサーバーを起動するとセッションが維持される挙動に戻る。（Tomcat側でDB復旧に気づくのでTomcat再起動は不要であることを確認）

セッションのレコードは蓄積されていくがどこかのタイミングで不要なレコードが削除されていた。メンテでの削除運用は不要。
https://tomcat.apache.org/tomcat-9.0-doc/config/manager.html
のPersistence Managerの設定項目一覧からはレコード削除機能がないことが確認できるが内部実装上（製品仕様上）はどこかにある。
本動作確認ではJDBC接続URLを直打ちしたがJNDI名指定も可能であるため本番利用時は性能面を考慮してJNDI指定（＝コネクションプール利用）が望ましい。
※Postgresqlの場合は都度アクセスしても本アプリでは0.015s程度の合計レスポンス時間であるためJDBC部分の影響は軽微。

他ホストからの接続については
http://rina.jpn.ph/~rance/linux/postgresql/connect.html
を参考に設定。Linux側はsystemctl disable firewalldを実行。