Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Spring BootのGraceful shutdownって内部でどうやって実現されているの?
Search
kawakawaryuryu
July 30, 2020
Programming
1.2k
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Spring BootのGraceful shutdownって内部でどうやって実現されているの?
JSUG勉強会 2020年その6 LT大会!の発表資料です
kawakawaryuryu
July 30, 2020
More Decks by kawakawaryuryu
See All by kawakawaryuryu
Spring Boot 1.5→2.1バージョンアップを経験して分かったハマりどころ
kawakawaryuryu
1
3.6k
第6章ICMP 第7章Ping
kawakawaryuryu
0
110
第14章DNS
kawakawaryuryu
0
88
逆求人イベント
kawakawaryuryu
0
100
Other Decks in Programming
See All in Programming
言語を使う側から、作る側へ。 自作 Lisp で得た新たな気づき。
andpad
0
150
freee が目指す データ マネジメント戦略 AI-Ready 時代を支える 攻めのガバナンスとは
freee
PRO
0
270
「寝てても仕事が進む」Claude Codeで組む第二の脳
tomoyafujita2016
0
300
言葉の格闘技のススメ~紙とペンと言葉から始める、キャリアの描き方~
progresscicada
2
130
数百円から始めるRuby電子工作
tarosay
0
140
使用 Meilisearch 建立新聞搜尋工具
johnroyer
0
230
そこに3びきプロダクトがいるじゃろう——生成AI時代における“価値が届かない理由”の構造
kosuket
0
420
Japan Community Day at Kubecon + CloudNativeCon Japan 2026: Learning Container Privilege Control by Building My Own Low-Level Container Runtime
ternbusty
1
140
属人化した知識を、 AIが辿れる地図にする
pkshadeck
PRO
1
140
yield再入門 #phpcon
o0h
PRO
0
960
Terraform標準の組織で AWS CDKをどう使うか
mu7889yoon
1
480
使いながら育てる Claude Code — 開発フローの1コマンド化 × 繰り返し指摘の自動仕組み化
shiki_kakaku
0
1.7k
Featured
See All Featured
AI: The stuff that nobody shows you
jnunemaker
PRO
9
880
Speed Design
sergeychernyshev
33
2k
Imperfection Machines: The Place of Print at Facebook
scottboms
270
14k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
34
2.8k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
31
3.3k
Dominate Local Search Results - an insider guide to GBP, reviews, and Local SEO
greggifford
PRO
0
250
svc-hook: hooking system calls on ARM64 by binary rewriting
retrage
2
440
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
28
3.6k
The Spectacular Lies of Maps
axbom
PRO
1
890
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
31
10k
Designing Dashboards & Data Visualisations in Web Apps
destraynor
232
55k
AI Search: Where Are We & What Can We Do About It?
aleyda
0
7.8k
Transcript
Spring BootͷGraceful shutdownͬͯ෦ͰͲ͏ͬ ࣮ͯݱ͞Ε͍ͯΔͷʁ 2020/07/30 JSUGษڧձ 2020 ͦͷ6 LTେձ ޱɹཽଠʢ@kawakawaryuryuʣ
Who am I • ޱཽଠʢ@kawakawaryuryuʣ • ϠϑʔגࣜձࣾɹใγεςϜຊ෦ • ࣾձܭγεςϜͷ։ൃ •
࠷ۙ • ϑϩϯτΤϯυͷਓʹͳΓͭͭ͋Δ • Spring৮Εͯͳ͍ 2
Spring Boot 2.3͔ΒGraceful shutdown͕؆୯ʹ • Spring Boot 2.3͔Β؆୯ʹ࣮ݱͰ͖ΔΑ͏ʹͳͬͨ • Jetty,
Reactor Netty, Tomcat, UndertowͰαϙʔτ • ڍಈ • ఀࢭࢦࣔޙɺ৽نϦΫΤετड͚͚ͳ͍ • ॲཧதͷϦΫΤετ͕͔ྃͯ͠ΒαʔόΛఀࢭ͢Δ 3
Graceful shutdown࣮ݱํ๏ • application.ymlʹ2ͭͷϓϩύςΟΛՃ͢Δ͚ͩ • ඇৗʹ؆୯ʂ 4 server: shutdown: graceful
spring: lifecycle: timeout-per-shutdown-phase: 20s #αʔόͷ࠷େγϟοτμϯ࣌ؒ
͜͜Ͱٙ • ෦ͰͲ͏࣮ͬͯݱ͍ͯ͠Δͷʁ • ϒϥοΫϘοΫε͗͢Δɾɾ • ؾʹͳͬͯΕͳ͍ 5
ιʔείʔυͬͯΈ·ͨ͠ • ΰʔϧ • Graceful shutdownͷ࣮ॲཧՕॴΛ֬ೝ͢Δ • Graceful shutdownॲཧ͕ͲͷΑ͏ʹݺΕΔ͔֬ೝ͢Δ •
લఏ • Spring Boot 2.3.1Ͱ֬ೝ • Web ServerJettyͰ֬ೝ 6
·ͣ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
ΞϓϦΛఀࢭ 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
ΞϓϦΛఀࢭ 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ʹؔΘͬͯͦ͏
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Λߦ͏
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ͷॲཧΛͯͦ͠͏ ͔͜͜ΒઌͷॲཧεϥΠυͷ࠷ޙʹࡌ͚ͬͯ͋Γ·͢
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); } }
WebServerGracefulShutdownLifecycle • ্هΫϥεͷstopϝιουͰݺΕΔ • SmartLifecycleͷstopϝιουΛΦʔόʔϥΠυ͍ͯ͠Δ 13 class WebServerGracefulShutdownLifecycle implements SmartLifecycle
{ @Override public void stop(Runnable callback) { this.running = false; this.webServer.shutDownGracefully((result) -> callback.run()); } }
SmartLifecycleʁ 14 https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/SmartLifecycle.html
SmartLifecycle • Graceful shutdownͷॲཧΛݺͼग़͍ͯ͠Δਖ਼ମ͜Ε • SmartLifecycleΛ࣮ͨ͠ΫϥεApplicationContextͷىಈ࣌ ఀࢭ࣌ʹҙͷॲཧ͕ݺΕΔ 15
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 "QQMJDBUJPO$POUFYUΫϩʔζ࣌ʹ #FBOొ͞Ε͍ͯΔ4NBSU-JGFDZDMFͷ TUPQϝιουΛॱ൪ʹݺΜͰ͍͘
·ͱΊ • Spring BootͷGraceful shutdownSmartLifecycleͱ͍͏ΈΛ ͬͯApplicationContextͷఀࢭ࣌ʹݺͼग़͞ΕΔ • IDEΛۦ͢ΕSpring BootͷιʔείʔυׂͱಡΊΔͷͰ ͔Βͳ͍ͱ͖ಡΜͰΈΔͷ͕͓͢͢Ί
18
ઌQiitaʹهࣄΛॻ͖·ͨ͠ 19 https://qiita.com/kawakawaryuryu/items/d0cfd1bb1ec0402fd6c8
͝ਗ਼ௌ͋Γ͕ͱ͏ ͍͟͝·ͨ͠
͓·͚ JettyͷGraceful shutdown ࣮ॲཧՕॴͷ֬ೝ
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) } ΞϓϦΛఀࢭͨ࣌͠ʹग़ྗ͞Εͨϩάͱಉ͡
(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 } } αʔό͕࣋ͭશίωΫλΛॱ൪ʹ ఀࢭ͍ͯ͘͠ ˠ৽نίωΫγϣϯͷडΛఀࢭ
(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ͭͭ͠ϧʔϓΛճ͢
(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); } } ΞΫςΟϒͳϦΫΤετ͕ʹͳͬͨΒ γϟοτμϯ ˠॲཧதͷϦΫΤετΛ͔ྃͯ͠Β ҆શʹఀࢭ