Spring Boot パフォーマンス関連の設定

Spring Bootでパフォーマンス関連の設定をする機会があったので、メモしておきます。

私の仮説も含んでいますので、その点はご了承ください。
随時ブラッシュアップしていこうと思います。

現時点では、同時実行接続数に関する設定がメインです。

# 各種バージョン
openjdk version "11.0.6"
org.springframework.boot:spring-boot-starter-web:jar:2.1.7.RELEASE
org.springframework.boot:spring-boot-starter-jetty:jar:2.1.7.RELEASE
org.springframework.boot:spring-boot-starter-jdbc:jar:2.1.7.RELEASE
com.h2database:h2:jar:1.4.199

# Jetty（Java Servletコンテナ／Webサーバ）

Jetty はJava Servletコンテナ／Webサーバで、Spring Bootの組み込みコンテナです。
Jettyでは主にスレッド数を設定します。

### 設定項目

|設定項目|意味|デフォルト値|application.propertiesの項目名|
|---|---|---|---|
|スレッドプールの最大スレッド数|意味は左記の通りですが、application.propertiesで値を設定してもデフォルト値から変更されませんでした。QueuedThreadPoolには外部からの設定を受け入れるような箇所がないのが原因かと思いますが、調査次第、記載しようと思います。ただ、最大で200もあれば十分なのかなと思ってしまうので、設定できる必要性は薄いのかもしれません。|200|server.jetty.max-threads|
|スレッドプールの最小スレッド数|最大スレッド数と同様、設定しても有効になりませんでした。調査していこうと思います。|8|server.jetty.min-threads|
|アクセプタースレッドの数|アクセプタースレッドは、クライアントからの要求を受け付け、セレクタースレッドに処理を任せます。アクセプタースレッド自体は大したことをしないので、このスレッド数は少なくても大丈夫です。デフォルトから変更する必要はほぼ無いかと思います。なお、アクセプタースレッドもスレッドプールのスレッドです。|-1（オペレーティング環境から導出される。導出のロジックについては参考ページをご覧ください。）|server.jetty.acceptors| 
|セレクタースレッドの数|セレクターとは、ノンブロッキングIOでクライアントとの通信チャンネルを管理するコンポーネントです。従来のブロッキングIOでは、クライアントとの通信が開始してから完了するまで、スレッドがブロックされていました。Jettyで採用されているノンブロッキングIOでは、ちょっとした待ち時間ではスレッドはブロックされず、その間に別の処理を実行することができ、効率性が向上しています。このためには、通信するチャンネルを覚えておく必要があり、その役割を担うのがセレクターです。セレクターは、後続のJava処理を呼び出したりするので、セレクタースレッドの数は性能に大きな影響を与えます。なお、セレクタースレッドもスレッドプールのスレッドです。|-1（オペレーティング環境から導出される。導出のロジックについては参考ページをご覧ください。）|jetty.server.selectors| 

application.propertiesに設定すると、以下のようになります。

```properties:application.properties
server.jetty.acceptors=2
server.jetty.selectors=3
```

### 設定が効いているかどうかを確認する方法

Macで確認する方法です。（他の環境でも確認できるはずですが、未確認です。）

##### ①Spring Bootアプリケーションを起動

##### ②jconsoleを起動

ターミナルで`jconsole`と入力し、実行します。jconsoleはJDKに同梱されているGUIツールで、JMXを利用してMBeanの状態を可視化してくれます。

起動すると、以下のような画面が表示されます。Spring Bootアプリケーションを選択して、接続してみましょう。
<img width="461" alt="スクリーンショット 2020-02-24 14.26.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455418/7cc97550-d7e0-4484-9689-18fc98b1609b.png">

##### ③スレッドの状態を確認

スレッドの状態を確認すると、以下のようになっており、設定が効いていることが確認できます。

![スレッド.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455418/ef83d93f-82ff-5452-61c1-8911484c8c7a.png)

なお、ここでは先述のとおり`server.jetty.acceptors=2`、`server.jetty.selectors=3`と設定しています。

ちなみに、`qtp`で始まるスレッドが８本あるのは、スレッドプールの最小サイズ（スレッド数）のデフォルト値が8だからです。


##### 参考
https://www.eclipse.org/jetty/documentation/current/architecture.html
https://support.sonatype.com/hc/en-us/articles/360000744687-Understanding-Eclipse-Jetty-9-4-8-Thread-Allocation
https://www.techscore.com/tech/Java/JavaSE/NIO/5/
https://www.techscore.com/tech/Java/JavaSE/NIO/5-2/
https://spring.pleiades.io/spring-boot/docs/current/reference/html/appendix-application-properties.html#server-properties


# HikariCP（コネクションプール）

HikariCPは高速なコネクションプールとして、Spring Bootでもよく使われます。spring-boot-starter-jdbc -> HikariCP という依存となっていて、spring-boot-starter-jdbcを使用するとHikariCPがデフォルトで利用されます。

HikariCPではコネクションプールで維持するコネクションの数などについて、設定します。

### 設定項目

|設定項目|意味|デフォルト値|application.propertiesの項目名|
|---|---|---|---|
|connectionTimeout|クライアントがコネクションプールからコネクションを取得するときの最大待ち時間です。|30000(30秒)|spring.datasource.hikari.connection-timeout| 
|idleTimeout|コネクションプールの中のコネクションが、この時間だけ使われずに放って置かれた場合、そのコネクションはコネクションプールから解放されます。この設定は、minimumIdleがmaximumPoolSize未満に定義されている場合にのみ適用されます。アイドルなコネクションがたくさんあっても、minimumIdleの数のコネクションは解放されずに維持されます。コネクションが解放されるかどうかの判定は、この設定値そのものが閾値になるわけではなく、この設定値を起点としてバラつきがあります（設定値+平均15秒、+最大30秒のバラつき）。値0は、アイドル接続がプールから解放されないことを意味します。許可される最小値は10000ms（10秒）です。 |600000(10分）|spring.datasource.hikari.idle-timeout|
|maxLifetime|プールされてからこの時間が経過したコネクションは、コネクションプールから解放されます。使用中のコネクションは、そのコネクションがcloseされるのを待って削除されます。とはいえ、一気に大量のコネクションが解放されるのはマズイので、そうならないように制御されます。|1800000（30分）|spring.datasource.hikari.max-lifetime|
|minimumIdle|プールに最低限 維持しておこうとHikariCPが努める、アイドルなコネクションの数。つまりこの数を下回らないことは保証されません。下回った場合はHikarriCPがベストエフォルトでこの数だけのアイドルなコネクションを生成してくれます。しかし、最高のパフォーマンスと、突発的な処理要求に対応できるようにするために、そもそもHicariCPではこの設定を使わず、デフォルト値のまま（maximumPoolSize と同じ）が推奨されます。| maximumPoolSize と同じ。|spring.datasource.hikari.minimum-idle|
|maximumPoolSize|プールで維持するコネクション（使用中、アイドルの両方）の上限数です。プール中のコネクションが全て使用中だった場合、getConnection()すると、connectionTimeoutだけブロックします。|10|spring.datasource.hikari.maximum-pool-size|

application.propertiesに設定すると、以下のようになります。

```properties:application.properties
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.idle-timeout=600000
spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.minimum-idle=10
spring.datasource.hikari.maximum-pool-size=10
```

### 設定が効いているかどうかを確認する方法

Macで確認する方法です。（他の環境でも確認できるはずですが、未確認です。）

##### ①HikariCPのオブジェクトがMBeanとして登録されるように設定

以下の設定を追加します。

```properties:application.properties
spring.datasource.hikari.register-mbeans=true
```

これで、HikariCPのオブジェクトがMBeanとして登録され、JMXを利用して外部からモニタリングできるようになります。

##### ②Spring Bootアプリケーションを起動

##### ③jconsoleを起動

ターミナルで`jconsole`と入力し、実行します。jconsoleはJDKに同梱されているGUIツールで、JMXを利用してMBeanの状態を可視化してくれます。

起動すると、以下のような画面が表示されます。Spring Bootアプリケーションを選択して、接続してみましょう。
<img width="461" alt="スクリーンショット 2020-02-24 14.26.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455418/7cc97550-d7e0-4484-9689-18fc98b1609b.png">

##### ④MBeanの状態を確認
アクティブなコネクション、アイドルなコネクションの状況については、以下のように確認できます。
<img width="716" alt="スクリーンショット 2020-02-24 14.29.53.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455418/bc4b1345-5aa8-c536-5b44-e53fe4b99768.png">

適用された設定値については、以下のように確認できます。
<img width="709" alt="スクリーンショット 2020-02-24 14.32.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455418/2da8353b-8751-1d66-77a5-7928bf5b8746.png">


##### 参考
https://github.com/brettwooldridge/HikariCP
https://spring.pleiades.io/spring-boot/docs/current/reference/html/appendix-application-properties.html#data-properties
https://matsumana.info/blog/2016/02/06/spring-boot-hikaricp-metrics/