Spring BootのGraceful shutdown処理が内部でどう呼ばれているかソースコードリーディングしてみた結果

# はじめに
Spring Boot 2.3からGraceful shutdownが導入されました。アプリケーションプロパティを設定するだけで簡単にGraceful shutdownを実現することができます。しかし簡単に実現できるだけに内部でどう呼ばれてどう実現しているのかは結構ブラックボックスになっていると思います。
そこで本記事ではSpring BootのGraceful shutdownが内部でどう呼ばれているかをソースコードリーディングして調べた結果を書いていきたいと思います。

# 結論
- Spring BootのGraceful shutdownが内部でどう呼ばれているか
  - SpringのSmartLifecycleという仕組みによってApplicationContextのclose時に処理が呼ばれる

# 今回使用するバージョン・サーバ
Spring Boot 2.3.1を使用しています。また、サーバはJettyを使用します。

# 前提: Spring BootでのGraceful shutdown実現方法
ドキュメントに記載の通りですが、アプリケーションプロパティに以下のように設定するだけでGraceful shutdownを実現することができます。

```yml:application.yml
server:
  shutdown: graceful
spring:
  lifecycle:
    timeout-per-shutdown-phase: 20s #サーバの最大シャットダウン時間 この時間までにリクエストが0にならなかったら強制的にサーバを停止する
```

Graceful shutdown中の具体的な挙動としては

1. 新たに来たリクエストは受け付けない
2. 処理中のリクエストを捌き切るまでサーバを停止させない

です。この「新たに来たリクエストは受け付けない」というのは使用するWebサーバによって挙動が異なります。

|Webサーバ|挙動|
|:-:|:-:|
|Tomcat<br>Jetty<br>Reactor Netty|ネットワークレイヤーでコネクションを受け付けない|
|Undertow|503を返す|

参考: [Spring Boot Reference Documentation#boot-features-graceful-shutdown](https://docs.spring.io/spring-boot/docs/2.3.1.RELEASE/reference/htmlsingle/#boot-features-graceful-shutdown)

# Graceful shutdownが内部でどう呼ばれるか
上記からSpring BootのGraceful shutdownがどのように動くのか分かりました。では上記の処理はどこからどう呼ばれているのでしょうか。
ソースコードを読んで確認してみたいと思います。
**※注意**: 今回追うコードはSpring Boot 2.3.1を前提としています。内部の挙動は今後のバージョンアップで変わる可能性があるのでご注意ください。
また、サーバはJettyを使用します。

## Graceful shutdownの実処理部分を特定する
まずGraceful shutdownの実処理部分を特定し、そこを起点に呼び出し元を探る作戦でいきたいと思います。
何はともあれGraceful shutdownを動かしてみましょう。
まずSpring Bootを起動します。

```
% ./mvnw spring-boot:run
...
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.3.1.RELEASE)
...
2020-07-30 02:35:10.626  INFO 15060 --- [           main] o.e.jetty.server.AbstractConnector       : Started ServerConnector@552ed807{HTTP/1.1, (http/1.1)}{0.0.0.0:8080}
2020-07-30 02:35:10.628  INFO 15060 --- [           main] o.s.b.web.embedded.jetty.JettyWebServer  : Jetty started on port(s) 8080 (http/1.1) with context path '/'
2020-07-30 02:35:10.640  INFO 15060 --- [           main] c.k.s.SampleGracefulShutdownApplication  : Started SampleGracefulShutdownApplication in 1.517 seconds (JVM running for 1.795)
```

Jettyが動いているのが分かります。
これを今回は[Spring Boot Actuator](https://docs.spring.io/spring-boot/docs/2.3.1.RELEASE/reference/htmlsingle/#production-ready)のshutdownエンドポイントを用いて停止させます。

```
2020-07-30 02:35:14.253  INFO 15060 --- [     Thread-223] o.s.b.web.embedded.jetty.JettyWebServer  : Commencing graceful shutdown. Waiting for active requests to complete
2020-07-30 02:35:14.258  INFO 15060 --- [ jetty-shutdown] o.s.b.web.embedded.jetty.JettyWebServer  : Graceful shutdown complete
2020-07-30 02:35:14.264  INFO 15060 --- [     Thread-223] o.e.jetty.server.AbstractConnector       : Stopped ServerConnector@552ed807{HTTP/1.1, (http/1.1)}{0.0.0.0:8080}
2020-07-30 02:35:14.265  INFO 15060 --- [     Thread-223] org.eclipse.jetty.server.session         : node0 Stopped scavenging
2020-07-30 02:35:14.267  INFO 15060 --- [     Thread-223] o.e.j.s.h.ContextHandler.application     : Destroying Spring FrameworkServlet 'dispatcherServlet'
2020-07-30 02:35:14.268  INFO 15060 --- [     Thread-223] o.e.jetty.server.handler.ContextHandler  : Stopped o.s.b.w.e.j.JettyEmbeddedWebAppContext@38ed139b{application,/,[file:///private/var/folders/ds/ky2m710d41ldw12zs6jhg4jh0000gn/T/jetty-docbase.10343674703387388779.8080/],UNAVAILABLE}
2020-07-30 02:35:14.270  INFO 15060 --- [     Thread-223] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
```

上記1行目のログを見るとGraceful shutdownを開始しており、2行目のログを見るとGraceful shutdownを完了させていることが分かります。
このログを吐いているクラスがどちらも`o.s.b.web.embedded.jetty.JettyWebServer`クラスです。
どうやらこのクラスはGraceful shutdownに関わっていそうなので次にこのクラスを調べてみます。

JettyWebServerクラスを見てみると`shutDownGracefully`というメソッドが存在します。このメソッド内部でGraceful shutdownが行われていそうです。

```java:org.springframework.boot.web.embedded.jetty.JettyWebServer.java
@Override
public void shutDownGracefully(GracefulShutdownCallback callback) {
    if (this.gracefulShutdown == null) {
        callback.shutdownComplete(GracefulShutdownResult.IMMEDIATE);
        return;
    }
    this.gracefulShutdown.shutDownGracefully(callback);
}
```

https://github.com/spring-projects/spring-boot/blob/v2.3.1.RELEASE/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyWebServer.java#L286-L292

さらに見てみると、`this.gracefulShutdown`インスタンスがnullかどうかによってGraceful shutdownを行うかどうかここで変わってきてそうで、`this.gracefulShutdown`インスタンスがnullではない場合にGraceful shutdown処理が呼ばれていそうです。
この`this.gracefulShutdown.shutDownGracefully(callback);`のメソッドの中を確認してみると以下のようになっていました。

```java:org.springframework.boot.web.embedded.jetty.GracefulShutdown.java
void shutDownGracefully(GracefulShutdownCallback callback) {
    logger.info("Commencing graceful shutdown. Waiting for active requests to complete");
    for (Connector connector : this.server.getConnectors()) {
        shutdown(connector);
    }
    this.shuttingDown = true;
    new Thread(() -> awaitShutdown(callback), "jetty-shutdown").start();

}
```

一行目の`logger.info()`の内容は最初Spring Bootを停止させたときに書き出されていたログの内容と一致します。
したがって、このメソッドの内部でGraceful shutdownの実処理が行われていることがほぼ確実になりました。

ちなみにこれより内部のコードは[こちらの記事](https://qiita.com/kawakawaryuryu/items/d0cfd1bb1ec0402fd6c8)で紹介しているので本記事では割愛します。

## 上記処理がどこから呼ばれているか
ここまででGraceful shutdownの実処理の部分にはたどり着くことができました。
ではこの処理がどこから呼ばれているのでしょうか。

最初に示したJettyWebServerクラスのshutDownGracefullyメソッドを呼んでいる箇所を追ってみると、以下のWebServerGracefulShutdownLifecycleクラスのstopメソッドから呼ばれていることが分かりました。

```java:org.springframework.boot.web.servlet.context.WebServerGracefulShutdownLifecycle.java
class WebServerGracefulShutdownLifecycle implements SmartLifecycle {

    ...

    @Override
    public void stop(Runnable callback) {
        this.running = false;
        this.webServer.shutDownGracefully((result) -> callback.run());
    }
}
```

https://github.com/spring-projects/spring-boot/blob/v2.3.1.RELEASE/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/context/WebServerGracefulShutdownLifecycle.java#L49-L52

このクラスに注目するとSmartLifecycleインターフェースを実装していることが分かります。
実はこのSmartLifecycleが今回の肝です。
SmartLifecycleを実装したクラスはApplicationContextの起動時やシャットダウン時に任意の処理が呼ばれるようになります。具体的にはSmartLifecycleのstart, stopメソッドが呼ばれます。
今回のgraceful shutdownはこのSmartLifecycleのstopメソッドをオーバーライドして呼び出すことで実現していました。

- [SmartLifecycle (Spring Framework 5.2.7.RELEASE API)](https://docs.spring.io/spring-framework/docs/5.2.7.RELEASE/javadoc-api/org/springframework/context/SmartLifecycle.html)

ということでSpring BootのGraceful shutdownはどこから呼ばれるか、の答えはSmartLifecycleのstopメソッドから呼ばれる、ということになります。
ちなみにこのクラスのstopメソッドはJettyに限らずTomcatやReactor Nettyなどでも同様に呼ばれます。このstopメソッドの2行目では`this.webServer.shutDownGracefully((result) -> callback.run());`としており、ここでインジェクションされたwebServerインスタンスの種類、つまりWebサーバの種類によってこの後呼ばれるの処理内容が変わるような実装になっています。オブジェクト指向らしくていいですね。

また、これをさらに呼んでいるのはDefaultLifecycleProcessorクラスです。
このクラス内でSmartLifecycleのBeanのstopメソッドを順々に呼んでいきます。このクラスがApplicationContextのクローズに伴って呼ばれるため、結果的にSpring Bootクローズ時にGraceful shutdownが呼ばれるようになっています。

```java:org.springframework.context.support.DefaultLifecycleProcessor.java
private void doStop(Map<String, ? extends Lifecycle> lifecycleBeans, final String beanName,
        final CountDownLatch latch, final Set<String> countDownBeanNames) {

    Lifecycle bean = lifecycleBeans.remove(beanName);
    if (bean != null) {
        ...
        try {
            if (bean.isRunning()) {
                if (bean instanceof SmartLifecycle) {
                    ...
                    ((SmartLifecycle) bean).stop(() -> { //ここでSmartLifecycleのstopが順々に呼ばれる
                    ...
                    });
                }
                ...
            }
            ...
        }
        ...
    }
}
```

https://github.com/spring-projects/spring-framework/blob/v5.2.7.RELEASE/spring-context/src/main/java/org/springframework/context/support/DefaultLifecycleProcessor.java#L238-L244

# まとめ
今回Spring BootのGraceful shutdownがどのように実現されているか見てみました。SpringのSmartLifecycleの仕組みによって実現されているが分かりました。
ここまで追っていきましたが、実はこのことは[ドキュメント](https://docs.spring.io/spring-boot/docs/2.3.1.RELEASE/reference/htmlsingle/#boot-features-graceful-shutdown)にもちゃんと記載されています。
ただ、ドキュメントを呼んだだけではSmartLifecycleが何者なのか全く分からなかったのでそういう点ではソースコードレベルまで追ってみたことでSmartLifecycleを理解できたので良かったと思います。

# 参考
- [Spring Boot Reference Documentation](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-graceful-shutdown)
- [SmartLifecycle (Spring Framework 5.2.7.RELEASE API)](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/SmartLifecycle.html)
- [DefaultLifecycleProcessor (Spring Framework 5.2.7.RELEASE API)](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/support/DefaultLifecycleProcessor.html#stop--)