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
85
逆求人イベント
kawakawaryuryu
0
100
Other Decks in Programming
See All in Programming
Observability in Practice:Grafana 與 Edge Device SRE 的那些事
blueswen
0
160
Mujeres en SEO Summit 2026 - Greatest Disaster Hits en Web Performance
guaca
0
180
技術記事、AIに書かせるか、自分で書くか? 〜それでも私が自分の手で書く理由〜 / #QiitaConference
jnchito
2
1.4k
New "Type" system on PicoRuby
pocke
1
930
軽量Java基盤の設計 DIコンテナに頼らない、長期保守と1秒起動の実現 JJUG CCC 2026 Spring
macha64
0
530
CSC307 Lecture 17
javiergs
PRO
0
320
「エンジニアインターン、どうやって取った?」準備のリアルを語るLT会 Progate BAR
akiomatic
0
130
LLMによるContent Moderationの本番運用の裏側と品質担保への挑戦
suikabar
3
680
Even G2とAWSで推しのエージェントを召喚しよう!
har1101
1
120
ローカルLLMを使ってB2Bサービスを作っていての学び
yaotti
0
170
3Dシーンの圧縮
fadis
1
770
タクシーアプリ『GO』の バックエンド開発のおける AI利活用と若者のすべて
pyama86
3
2k
Featured
See All Featured
Optimising Largest Contentful Paint
csswizardry
37
3.7k
Context Engineering - Making Every Token Count
addyosmani
9
970
Statistics for Hackers
jakevdp
799
230k
What Being in a Rock Band Can Teach Us About Real World SEO
427marketing
0
250
Unlocking the hidden potential of vector embeddings in international SEO
frankvandijk
0
840
Rails Girls Zürich Keynote
gr2m
96
14k
Writing Fast Ruby
sferik
630
63k
Lessons Learnt from Crawling 1000+ Websites
charlesmeaden
PRO
1
1.3k
How to build an LLM SEO readiness audit: a practical framework
nmsamuel
1
780
4 Signs Your Business is Dying
shpigford
187
22k
Principles of Awesome APIs and How to Build Them.
keavy
128
18k
How to Align SEO within the Product Triangle To Get Buy-In & Support - #RIMC
aleyda
2
1.5k
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); } } ΞΫςΟϒͳϦΫΤετ͕ʹͳͬͨΒ γϟοτμϯ ˠॲཧதͷϦΫΤετΛ͔ྃͯ͠Β ҆શʹఀࢭ