Spring BootのGraceful shutdownって内部でどうやって実現されているの?

Spring BootのGraceful shutdownって内部でどうやって実現されているの?

JSUG勉強会 2020年その6 LT大会!の発表資料です

F54b73a93fb75a12fa6c28b26ca0bb62?s=128

kawakawaryuryu

July 30, 2020
Tweet

Transcript

  1. Spring BootͷGraceful shutdownͬͯ಺෦ͰͲ͏΍ͬ ࣮ͯݱ͞Ε͍ͯΔͷʁ 2020/07/30 JSUGษڧձ 2020 ͦͷ6 LTେձ ઒ޱɹཽଠ࿠ʢ@kawakawaryuryuʣ

  2. Who am I • ઒ޱཽଠ࿠ʢ@kawakawaryuryuʣ • Ϡϑʔגࣜձࣾɹ৘ใγεςϜຊ෦ • ࣾ಺ձܭγεςϜͷ։ൃ •

    ࠷ۙ • ϑϩϯτΤϯυͷਓʹͳΓͭͭ͋Δ • Spring৮Εͯͳ͍ 2
  3. Spring Boot 2.3͔ΒGraceful shutdown͕؆୯ʹ • Spring Boot 2.3͔Β؆୯ʹ࣮ݱͰ͖ΔΑ͏ʹͳͬͨ • Jetty,

    Reactor Netty, Tomcat, UndertowͰαϙʔτ • ڍಈ • ఀࢭࢦࣔޙɺ৽نϦΫΤετ͸ड͚෇͚ͳ͍ • ॲཧதͷϦΫΤετ͕׬͔ྃͯ͠ΒαʔόΛఀࢭ͢Δ 3
  4. Graceful shutdown࣮ݱํ๏ • application.ymlʹ2ͭͷϓϩύςΟΛ௥Ճ͢Δ͚ͩ • ඇৗʹ؆୯ʂ 4 server: shutdown: graceful

    spring: lifecycle: timeout-per-shutdown-phase: 20s #αʔόͷ࠷େγϟοτμ΢ϯ࣌ؒ
  5. ͜͜Ͱٙ໰ • ಺෦Ͱ͸Ͳ͏΍࣮ͬͯݱ͍ͯ͠Δͷʁ • ϒϥοΫϘοΫε͗͢Δɾɾ • ؾʹͳͬͯ໷΋຾Εͳ͍ 5

  6. ιʔείʔυ௥ͬͯΈ·ͨ͠ • ΰʔϧ • Graceful shutdownͷ࣮ॲཧՕॴΛ֬ೝ͢Δ • Graceful shutdownॲཧ͕ͲͷΑ͏ʹݺ͹ΕΔ͔֬ೝ͢Δ •

    લఏ • Spring Boot 2.3.1Ͱ֬ೝ • Web Server͸JettyͰ֬ೝ 6
  7. ·ͣ͸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) 7
  8. ΞϓϦΛఀࢭ 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' 8
  9. ΞϓϦΛఀࢭ 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' 9 ͜ͷΫϥε͕(SBDFGVMTIVUEPXOʹؔΘͬͯͦ͏
  10. JettyWebServerΫϥε • ͜ͷ಺෦ͰGraceful ShutdownΛߦͬͯͦ͏ 10 public class JettyWebServer implements WebServer

    { @Override public void shutDownGracefully(GracefulShutdownCallback callback) { if (this.gracefulShutdown == null) { callback.shutdownComplete(GracefulShutdownResult.IMMEDIATE); return; } this.gracefulShutdown.shutDownGracefully(callback); } } UIJTHSBDFGVM4IVUEPXOΠϯελϯε͕OVMMͰͳ͍ ৔߹ʹ(SBDFGVMTIVUEPXOΛߦ͏
  11. GracefulShutdownΫϥε 11 void shutDownGracefully(GracefulShutdownCallback callback) { logger.info("Commencing graceful shutdown. Waiting

    for active requests to complete"); for (Connector connector : this.server.getConnectors()) { shutdown(connector); // (1) } this.shuttingDown = true; new Thread(() -> awaitShutdown(callback), "jetty-shutdown").start(); // (2) } ΞϓϦΛఀࢭͨ࣌͠ʹग़ྗ͞Εͨϩάͱಉ͡ ΍͸Γ͜ͷ಺෦Ͱ(SBDFGVMTIVUEPXOͷॲཧΛͯͦ͠͏ ͔͜͜Βઌͷॲཧ͸εϥΠυͷ࠷ޙʹࡌ͚ͬͯ͋Γ·͢
  12. JettyWebServerΫϥεʢ࠶ܝʣ • ͜ͷ಺෦ͰGraceful shutdownΛߦ͍ͬͯͦ͏ͳ͜ͱ͸෼͔ͬͨ • ͍ͭࣗ͜ମ͸Ͳ͔͜Βݺ͹Ε͍ͯΔʁ 12 public class JettyWebServer

    implements WebServer { @Override public void shutDownGracefully(GracefulShutdownCallback callback) { if (this.gracefulShutdown == null) { callback.shutdownComplete(GracefulShutdownResult.IMMEDIATE); return; } this.gracefulShutdown.shutDownGracefully(callback); } }
  13. WebServerGracefulShutdownLifecycle • ্هΫϥεͷstopϝιουͰݺ͹ΕΔ • SmartLifecycleͷstopϝιουΛΦʔόʔϥΠυ͍ͯ͠Δ 13 class WebServerGracefulShutdownLifecycle implements SmartLifecycle

    { @Override public void stop(Runnable callback) { this.running = false; this.webServer.shutDownGracefully((result) -> callback.run()); } }
  14. SmartLifecycleʁ 14 https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/SmartLifecycle.html

  15. SmartLifecycle • Graceful shutdownͷॲཧΛݺͼग़͍ͯ͠Δਖ਼ମ͸͜Ε • SmartLifecycleΛ࣮૷ͨ͠Ϋϥε͸ApplicationContextͷىಈ࣌΍ ఀࢭ࣌ʹ೚ҙͷॲཧ͕ݺ͹ΕΔ 15

  16. SmartLifecycleͷ࣮૷ 16 public class SampleSmartLifecycle implements SmartLifecycle { @Override public

    void start() { // ىಈ࣌ͷ೚ҙॲཧΛ࣮૷ } @Override public void stop() { // ఀࢭ࣌ͷ೚ҙॲཧΛ࣮૷ } @Override public void stop(Runnable callback) { // ఀࢭ࣌ͷ೚ҙॲཧΛ࣮૷ // callbackΛड͚औͬͯ౉͞Εͨॲཧͷݺͼग़͕͠Մೳ } @Override public boolean isRunning() { // ίϯϙʔωϯτ͕Քಇதͷ৔߹ʹtrueΛฦ͢Α͏ʹ࣮૷ } } (SBDFGVMTIVUEPXOͰ͸͜ͷϝιουΛ ΦʔόʔϥΠυ͍ͯͨ͠
  17. 17 "QQMJDBUJPO$POUFYUΫϩʔζ࣌ʹ #FBOొ࿥͞Ε͍ͯΔ4NBSU-JGFDZDMFͷ TUPQϝιουΛॱ൪ʹݺΜͰ͍͘

  18. ·ͱΊ • Spring BootͷGraceful shutdown͸SmartLifecycleͱ͍͏࢓૊ΈΛ ࢖ͬͯApplicationContextͷఀࢭ࣌ʹݺͼग़͞ΕΔ • IDEΛۦ࢖͢Ε͹Spring Bootͷιʔείʔυ͸ׂͱಡΊΔͷͰ ෼͔Βͳ͍ͱ͖͸ಡΜͰΈΔͷ͕͓͢͢Ί

    18
  19. ઌ೔QiitaʹهࣄΛॻ͖·ͨ͠ 19 https://qiita.com/kawakawaryuryu/items/d0cfd1bb1ec0402fd6c8

  20. ͝ਗ਼ௌ͋Γ͕ͱ͏ ͍͟͝·ͨ͠

  21. ͓·͚ JettyͷGraceful shutdown ࣮ॲཧՕॴͷ֬ೝ

  22. GracefulShutdownΫϥε 22 void shutDownGracefully(GracefulShutdownCallback callback) { logger.info("Commencing graceful shutdown. Waiting

    for active requests to complete"); for (Connector connector : this.server.getConnectors()) { shutdown(connector); // (1) } this.shuttingDown = true; new Thread(() -> awaitShutdown(callback), "jetty-shutdown").start(); // (2) } ΞϓϦΛఀࢭͨ࣌͠ʹग़ྗ͞Εͨϩάͱಉ͡
  23. (1) ৽نϦΫΤετड෇Λఀࢭ͢Δॲཧ 23 void shutDownGracefully(GracefulShutdownCallback callback) { for (Connector connector

    : this.server.getConnectors()) { shutdown(connector); // (1) } } private void shutdown(Connector connector) { try { connector.shutdown().get(); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } catch (ExecutionException ex) { // Continue } } αʔό͕࣋ͭશίωΫλΛॱ൪ʹ ఀࢭ͍ͯ͘͠ ˠ৽نίωΫγϣϯͷड෇Λఀࢭ
  24. (2) ॲཧதͷϦΫΤετͷ׬ྃޙ αʔόΛఀࢭ͢Δॲཧ 24 private void awaitShutdown(GracefulShutdownCallback callback) { while

    (this.shuttingDown && this.activeRequests.get() > 0) { sleep(100); } this.shuttingDown = false; long activeRequests = this.activeRequests.get(); if (activeRequests == 0) { logger.info("Graceful shutdown complete"); callback.shutdownComplete(GracefulShutdownResult.IDLE); } else { logger.info(LogMessage.format("Graceful shutdown aborted with %d request(s) still active", activeRequests)); callback.shutdownComplete(GracefulShutdownResult.REQUESTS_ACTIVE); } } ΞΫςΟϒͳϦΫΤετ͕ͳ͘ͳΔ ·ͰTMFFQͭͭ͠ϧʔϓΛճ͢
  25. (2) ॲཧதͷϦΫΤετͷ׬ྃޙ αʔόΛఀࢭ͢Δॲཧ 25 private void awaitShutdown(GracefulShutdownCallback callback) { while

    (this.shuttingDown && this.activeRequests.get() > 0) { sleep(100); } this.shuttingDown = false; long activeRequests = this.activeRequests.get(); if (activeRequests == 0) { logger.info("Graceful shutdown complete"); callback.shutdownComplete(GracefulShutdownResult.IDLE); } else { logger.info(LogMessage.format("Graceful shutdown aborted with %d request(s) still active", activeRequests)); callback.shutdownComplete(GracefulShutdownResult.REQUESTS_ACTIVE); } } ΞΫςΟϒͳϦΫΤετ͕ʹͳͬͨΒ γϟοτμ΢ϯ ˠॲཧதͷϦΫΤετΛ׬͔ྃͯ͠Β ҆શʹఀࢭ