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
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
82
逆求人イベント
kawakawaryuryu
0
96
Other Decks in Programming
See All in Programming
年間50登壇、単著出版、雑誌寄稿、Podcast出演、YouTube、CM、カンファレンス主催……全部やってみたので面白さ等を比較してみよう / I’ve tried them all, so let’s compare how interesting they are.
nrslib
4
720
KagglerがMixSeekを触ってみた
morim
0
370
煩雑なSkills管理をSoC(関心の分離)により解決する――関心を分離し、プロンプトを部品として育てるためのOSSを作った話 / Solving Complex Skills Management Through SoC (Separation of Concerns)
nrslib
3
670
一度始めたらやめられない開発効率向上術 / Findy あなたのdotfilesを教えて!
k0kubun
4
2.8k
ファインチューニングせずメインコンペを解く方法
pokutuna
0
270
おれのAgentic Coding 2026/03
tsukasagr
1
140
アーキテクチャモダナイゼーションとは何か
nwiizo
17
4.4k
Smarter Angular mit Transformers.js & Prompt API
christianliebel
PRO
1
120
脱 雰囲気実装!AgentCoreを良い感じにWEBアプリケーションに組み込むために
takuyay0ne
3
440
Everything Claude Code OSS詳細 — 5層構造の中身と導入方法
targe
0
160
RSAが破られる前に知っておきたい 耐量子計算機暗号(PQC)入門 / Intro to PQC: Preparing for the Post-RSA Era
mackey0225
3
120
Codex CLI でつくる、Issue から merge までの開発フロー
amata1219
0
330
Featured
See All Featured
Building a Modern Day E-commerce SEO Strategy
aleyda
45
9k
A Soul's Torment
seathinner
5
2.6k
The SEO Collaboration Effect
kristinabergwall1
0
420
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.7k
Visualization
eitanlees
150
17k
The Director’s Chair: Orchestrating AI for Truly Effective Learning
tmiket
1
150
Crafting Experiences
bethany
1
110
BBQ
matthewcrist
89
10k
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
118
110k
A Tale of Four Properties
chriscoyier
163
24k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
31
10k
[SF Ruby Conf 2025] Rails X
palkan
2
930
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); } } ΞΫςΟϒͳϦΫΤετ͕ʹͳͬͨΒ γϟοτμϯ ˠॲཧதͷϦΫΤετΛ͔ྃͯ͠Β ҆શʹఀࢭ