TomcatとPostregSQLでコネクションプールを試してみる（全てZip版を利用）

## 1. はじめに

以前、[ApacheとTomcat、Redisでセッション共有を１台のホスト上で試す環境](https://qiita.com/5zm/items/f127ef551ba8b64d03cc)を作成しました。一般的なWebアプリではデータベースを利用した三層Webモデルが基本になるかと思います。そこで今回はPostreSQLを追加し、Tomcatのコネクションプールを試してみたいと思います。なお、PostgreSQLは必要な時だけ実行するようにサービスではなくアプリケーションと起動するようにZip版を利用します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/195748/537e2cd2-839b-1b46-da70-dea5d6d132c1.png)

## 2. PostgreSQLのダウンロード

以下のサイトからZip版をダウンロードし、任意のディレクトリに展開します。
この記事では`C:\tools\postgresql_16.4.2`に展開したものとします。

https://www.enterprisedb.com/download-postgresql-binaries
postgresql-16.5-1-windows-x64-binaries.zip

## 3. PostgreSQLデータベースサーバの初期構築

Zip版のため`bin/initdb.exe`でデータベースサーバの各種初期設定を手動で行う必要があります。

| オプション | 説明 |
|:--------:|:------------|
| -U | ・PostregSQLサーバの管理者ユーザ<br>・今回は`postgres` |
| -E | ・文字エンコーディング<br>・今回は`utf8` |
| -D | ・各種データを格納するディレクトリパス<br>・事前に作成しておくこと<br>・今回は`C:\tools\postgresql_16.4.2\data_dir` |

```bat:PostgrSQLデータベースサーバを初期構築する
C:\tools\postgresql_16.4.2>bin\initdb.exe -U postgres -E utf8 -D C:\tools\postgresql_16.4.2\data_dir -W
データベースシステム内のファイルの所有者はユーザー"*******"となります。
このユーザーをサーバープロセスの所有者とする必要があります。

データベースクラスタはロケール"Japanese_Japan.932"で初期化されます。
initdb: ロケール"Japanese_Japan.932"用の適切なテキスト検索設定が見つかりませんでした
デフォルトのテキスト検索構成は simple に設定されます。

データベージのチェックサムは無効です。

新しいスーパーユーザーのパスワードを入力してください:

再入力してください:


ディレクトリC:/tools/postgresql_16.4.2/data_dirの権限を設定しています ... ok
サブディレクトリを作成しています ... ok
動的共有メモリの実装を選択しています ... windows
デフォルトのmax_connectionsを選択しています ... 100
デフォルトのshared_buffersを選択しています ... 128MB
デフォルトの時間帯を選択しています ... Asia/Tokyo
設定ファイルを作成しています ... ok
ブートストラップスクリプトを実行しています ... ok
ブートストラップ後の初期化を実行しています ... ok
データをディスクに同期しています ... ok

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

成功しました。以下のようにしてデータベースサーバーを起動できます:

    ^"bin^\pg^_ctl^" -D ^"C^:^\tools^\postgresql^_16^.4^.2^\data^_dir^" -l ログファイル start


C:\tools\postgresql_16.4.2>
```

## 3. PostgreSQLデータベースサーバの起動・停止・ステータス確認

前述の手順で初期構築は終わりましたがサーバの起動は別の手順になります。以降PostgreSQLを利用する場合、`bin\pg_ctl.exe`コマンドでサーバを起動して利用し、終了する際に停止させます。

| オプション | 説明 |
|:--------:|:------------|
| -w | コマンドの実行結果を受領するまで待機する |
| -o | ・コマンドに渡すオプションを指定する<br>・`"-p ポート番号"`でポート番号を変更可能 | 
| -D | ・`initdb.exe`で指定した各種データが格納するディレクトリパス<br>・今回は`C:\tools\postgresql_16.4.2\data_dir` |
| -l | ・ログファイルのファイルパス<br>・ログファイルを出力するディレクトリではなくログファイルであることに注意すること | 


```bat:PostgreSQLサーバの起動
C:\tools\postgresql_16.4.2>bin\pg_ctl.exe start -w -o "-p 5432" -D C:\tools\postgresql_16.4.2\data_dir -l C:\tools\postgresql_16.4.2\log_dir\server.log
サーバーの起動完了を待っています....完了
サーバー起動完了

C:\tools\postgresql_16.4.2>
```

```bat:PostgreSQLサーバの停止
C:\tools\postgresql_16.4.2>bin\pg_ctl.exe stop -w -o "-p 5432" -D C:\tools\postgresql_16.4.2\data_dir -l C:\tools\postgresql_16.4.2\log_dir\server.log
サーバー停止処理の完了を待っています....完了
サーバーは停止しました

C:\tools\postgresql_16.4.2>
```

```bat:PostreSQLサーバのステータス確認
C:\tools\postgresql_16.4.2>bin\pg_ctl.exe status -o "-p 5432" -D C:\tools\postgresql_16.4.2\data_dir
pg_ctl: サーバーが動作中です(PID: 148)
C:/tools/postgresql_16.4.2/bin/postgres.exe "-D" "C:/tools/postgresql_16.4.2/data_dir" "-p" "5432"

C:\tools\postgresql_16.4.2>
```

## 4. PSQLでPostreSQLサーバに接続する

PostreSQLサーバが起動していることを確認し、`bin\psql.exe`コマンドでサーバに接続します。
参考までにPSQLを終了するコマンドは`\q`になります。

```bat:psqlでサーバに接続する
C:\tools\postgresql_16.4.2>bin\psql.exe -h localhost -p 5432 -U postgres -d postgres
psql (16.4)
"help"でヘルプを表示します。

postgres=# \dt
リレーションが見つかりませんでした。
postgres=# \du
                                       ロール一覧
ロール名 |                                     属性
----------+------------------------------------------------------------------------------
postgres | スーパーユーザー, ロール作成可, DB作成可, レプリケーション可, RLS のバイパス


postgres=#
```

## 5. データベースとユーザの作成

アプリケーションから利用するにはデータベースやユーザが必要になります。
PSQLでサーバに接続後、`CREATE USER`や`CREATE DATABASE`で作成します。

```sql:ユーザの作成
postgres=# CREATE USER alice WITH PASSWORD 'demoPW123';
CREATE ROLE
postgres=# \du
                                       ロール一覧
ロール名 |                                     属性
----------+------------------------------------------------------------------------------
alice    |
postgres | スーパーユーザー, ロール作成可, DB作成可, レプリケーション可, RLS のバイパス
```

```sql:データベースの作成
postgres=# CREATE DATABASE tourreserve ENCODING 'UTF8';
CREATE DATABASE
postgres=# \l
                                                                       データベース一覧
    名前     |  所有者  | エンコーディング | ロケールプロバイダー |      照合順序      | Ctype(変換演算子)  | ICUロケール | ICUルール: |     アクセス権限
-------------+----------+------------------+----------------------+--------------------+--------------------+-------------+------------+-----------------------
postgres    | postgres | UTF8             | libc                 | Japanese_Japan.932 | Japanese_Japan.932 |             |            |
template0   | postgres | UTF8             | libc                 | Japanese_Japan.932 | Japanese_Japan.932 |             |            | =c/postgres          +
             |          |                  |                      |                    |                    |             |            | postgres=CTc/postgres
template1   | postgres | UTF8             | libc                 | Japanese_Japan.932 | Japanese_Japan.932 |             |            | =c/postgres          +
             |          |                  |                      |                    |                    |             |            | postgres=CTc/postgres
tourreserve | postgres | UTF8             | libc                 | Japanese_Japan.932 | Japanese_Japan.932 |             |            |
(4 行)


postgres=#
```

## 6. PostgreSQLのJDBCドライバのダウンロード

以下のサイトから`postgresql-42.7.4.jar`をダウンロードし、`%CATALINA_HOME%/lib`に配置します。
https://jdbc.postgresql.org/download/

## 7. Tomcatのコネクションプールの設定

Tomcatのデータソースとしてコネクションプールを構築するため、`%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" />
    -->
    <!-- ⭐️以下の内容を追加 -->
    <Resource
        type="javax.sql.DataSource"
        name="${db.jdbcname}"
        driverClassName="org.postgresql.Driver"
        url="jdbc:postgresql://${db.address}/${db.database}"
        username="${db.user}"
        password="${db.pass}"
        initialSize="5"
        minIdle="5"
        maxIdle="5"
        maxTotal="5"
        defaultAutoCommit="false"/>

    <!-- 以下はRedisのセッション共有の設定：詳細は以前の記事を参照 -->
    <Manager className="org.redisson.tomcat.RedissonSessionManager"
        configPath="${catalina.base}/conf/redisson.conf"
        readMode="REDIS" updateMode="DEFAULT" broadcastSessionEvents="false"
        keyPrefix=""/>
</Context>
```

コネクションプールの接続先に関する情報は環境(開発環境、試験環境、本番環境等)によって変わるため、[12 Factor App](https://12factor.net/ja/)に従い環境変数で変更できるようにします。
詳細については[CATALINA_BASE環境変数を利用してTomcatを複数起動する方法](https://qiita.com/5zm/items/5256ecf2fb8fa37449b8)を参照ください。

| 環境変数 | 説明 |
|:-----:|:---------|
| DB_JDBCNAME | ・データソースのJDBC名<br>・今回は`jdbc/tourreservedb` |
| DB_ADDRESS | ・PostreSQLの接続文字列のアドレス情報<br>・今回は`localhost:5432`|
| DB_DATABASE | ・PostreSQLの接続文字列の接続先データベース名<br>・今回は`tourreserve` | 
| DB_USER | ・コネククションプールの接続ユーザ名<br>・今回は`alice` |
| DB_PASS | ・コネククションプールの接続ユーザのパスワード<br>・今回は`demoPW123` |

```bat:%CATALINA_HOME%/bin/setenv.bat
@echo off
set JAVA_OPTS=-Dserver.port=%SERVER_PORT% -Dajp.port=%AJP_PORT% -Dshutdown.port=%SHUTDOWN_PORT% -Dredis.address=%REDIS_ADDRESS% -Ddb.jdbcname=%DB_JDBCNAME% -Ddb.address=%DB_ADDRESS% -Ddb.database=%DB_DATABASE% -Ddb.user=%DB_USER% -Ddb.pass=%DB_PASS%
```

```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

set REDIS_ADDRESS=redis://127.0.0.1:6379

set DB_JDBCNAME=jdbc/tourreservedb
set DB_ADDRESS=localhost:5432
set DB_DATABASE=tourreserve
set DB_USER=alice
set DB_PASS=demoPW123

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

set REDIS_ADDRESS=redis://127.0.0.1:6379

set DB_JDBCNAME=jdbc/tourreservedb
set DB_ADDRESS=localhost:5432
set DB_DATABASE=tourreserve
set DB_USER=alice
set DB_PASS=demoPW123

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

## 8. 動作確認

Tomcatを起動させた後、PSQLでPostgreSQLサーバに接続しているコネククション情報を確認してみます。
以下はTomcatを１台起動させた場合の接続コネクションの状況です。Tomcatの起動と合わせて複数のコネクションが接続済みになっているのが分かるかと思います。このコネクションをアプリケーションがTomcatから参照して利用することで、コネクションを効率的に活用することができます。

```sql:PostreSQLの接続コネククションの確認
C:\tools\postgresql_16.4.2>bin\psql.exe -h localhost -p 5432 -U postgres

postgres=# select application_name, datname, usename from pg_stat_activity;
    application_name    |   datname   | usename
------------------------+-------------+----------
                        |             |
                        |             | postgres
psql                   | postgres    | postgres
PostgreSQL JDBC Driver | tourreserve | alice
PostgreSQL JDBC Driver | tourreserve | alice
PostgreSQL JDBC Driver | tourreserve | alice
PostgreSQL JDBC Driver | tourreserve | alice
PostgreSQL JDBC Driver | tourreserve | alice
                        |             |
                        |             |
                        |             |
(11 行)


postgres=#
```

## 10. さいごに

今回はZip版のPostgreSQLの構築・使い方と、そのPostgreSQLを利用してTomcatのコネクションプールを構築する方法ついて説明しました。１台のWindowsホスト上で検証していますが、接続先情報は全て環境変数経由になっているため、環境変数を変更するだけで容易に複数ホストに分散させることが可能です。もしクラウドで動かす場合は接続先をマネージドのRedis、マネージドのPostgreSQLに接続先を変更すれば同様に動作可能です。

また、コネクションプールの方法としては今回試したTomcat等のアプリケーションサーバ側で作成する方法以外に、ライブラリを利用してアプリケーション側で作成する方法もあります。最近はコンテナ化もあり、アプリケーション側で作成することが主流になってきていますが、トラディショナルなアプリケーションのクラウドリフトの際、アプリケーションのソースコードの改修をどこまで許容するのかがポイントになります。

なお、今回検証で利用した資材は全てインストーラを利用しておらずサービス登録等は行われないため、ダウンロードした資材と利用したディレクトリ毎削除するだけ全て綺麗に削除、クリーンな環境に戻すことができます。最近はコンテナで検証することも多くなってきましたが、環境上コンテナが利用できない場合もあり、今回の記事ようにインストール不要で検証環境を構築してみました。