Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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

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

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

kawakawaryuryu

July 30, 2020
Tweet

More Decks by kawakawaryuryu

Other Decks in Programming

Transcript

  1. Spring Boot 2.3͔ΒGraceful shutdown͕؆୯ʹ • Spring Boot 2.3͔Β؆୯ʹ࣮ݱͰ͖ΔΑ͏ʹͳͬͨ • Jetty,

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

    spring: lifecycle: timeout-per-shutdown-phase: 20s #αʔόͷ࠷େγϟοτμ΢ϯ࣌ؒ
  3. ·ͣ͸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
  4. ΞϓϦΛఀࢭ 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
  5. ΞϓϦΛఀࢭ 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ʹؔΘͬͯͦ͏
  6. 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Λߦ͏
  7. 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ͷॲཧΛͯͦ͠͏ ͔͜͜Βઌͷॲཧ͸εϥΠυͷ࠷ޙʹࡌ͚ͬͯ͋Γ·͢
  8. 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); } }
  9. WebServerGracefulShutdownLifecycle • ্هΫϥεͷstopϝιουͰݺ͹ΕΔ • SmartLifecycleͷstopϝιουΛΦʔόʔϥΠυ͍ͯ͠Δ 13 class WebServerGracefulShutdownLifecycle implements SmartLifecycle

    { @Override public void stop(Runnable callback) { this.running = false; this.webServer.shutDownGracefully((result) -> callback.run()); } }
  10. 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Ͱ͸͜ͷϝιουΛ ΦʔόʔϥΠυ͍ͯͨ͠
  11. 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) } ΞϓϦΛఀࢭͨ࣌͠ʹग़ྗ͞Εͨϩάͱಉ͡
  12. (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 } } αʔό͕࣋ͭશίωΫλΛॱ൪ʹ ఀࢭ͍ͯ͘͠ ˠ৽نίωΫγϣϯͷड෇Λఀࢭ
  13. (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ͭͭ͠ϧʔϓΛճ͢
  14. (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); } } ΞΫςΟϒͳϦΫΤετ͕ʹͳͬͨΒ γϟοτμ΢ϯ ˠॲཧதͷϦΫΤετΛ׬͔ྃͯ͠Β ҆શʹఀࢭ