Mastering Graceful Shutdown and Startup for Spring Boot Microservices

Learn how to implement elegant service shutdown and startup in microservice architectures using Java's shutdown hooks, Spring Boot's built-in mechanisms, Docker container signals, and external containers like Jetty, with code examples and best‑practice strategies to avoid traffic hitting unhealthy instances.

Java Interview Crash Guide
Java Interview Crash Guide
Java Interview Crash Guide
Mastering Graceful Shutdown and Startup for Spring Boot Microservices

For microservices, graceful service up‑ and down‑times are essential. A service should not be exposed before its component or container starts successfully, and it must be taken offline when the host machine stops, preventing upstream traffic from reaching unhealthy instances.

1 Graceful Shutdown

Basic shutdown (Spring/SpringBoot/built‑in container)

The JVM supports graceful termination via shutdownHook.

Runtime.getRuntime().addShutdownHook(new Thread() {
    @Override
    public void run() {
        close();
    }
});

This approach works for the following scenarios:

1) Normal program exit 2) Calling System.exit() 3) Pressing Ctrl+C in the terminal 4) Killing the process with kill pid

Forcibly killing with kill -9 cannot be handled gracefully.

Spring Boot already registers a shutdown hook that reacts to Ctrl+C or the SIGTERM (signal 15) signal.

When the application receives the signal, Spring prints a "Closing..." log in AnnotationConfigEmbeddedWebApplicationContext, whose parent class AbstractApplicationContext implements the actual shutdown logic.

public void registerShutdownHook() {
    if (this.shutdownHook == null) {
        this.shutdownHook = new Thread() {
            public void run() {
                synchronized (AbstractApplicationContext.this.startupShutdownMonitor) {
                    AbstractApplicationContext.this.doClose();
                }
            }
        };
        Runtime.getRuntime().addShutdownHook(this.shutdownHook);
    }
}

public void destroy() { this.close(); }

public void close() {
    synchronized (this.startupShutdownMonitor) {
        this.doClose();
        if (this.shutdownHook != null) {
            try {
                Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
            } catch (IllegalStateException ignored) {}
        }
    }
}

protected void doClose() {
    if (this.active.get() && this.closed.compareAndSet(false, true)) {
        if (this.logger.isInfoEnabled()) {
            this.logger.info("Closing " + this);
        }
        LiveBeansView.unregisterApplicationContext(this);
        try {
            this.publishEvent(new ContextClosedEvent(this));
        } catch (Throwable ex) {
            this.logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
        }
        if (this.lifecycleProcessor != null) {
            try {
                this.lifecycleProcessor.onClose();
            } catch (Throwable ex) {
                this.logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
            }
        }
        this.destroyBeans();
        this.closeBeanFactory();
        this.onClose();
        this.active.set(false);
    }
}

Since doClose() publishes a ContextClosedEvent, we can create a listener to perform service deregistration from the registry when the event occurs.

@Component
public class GracefulShutdownListener implements ApplicationListener<ContextClosedEvent> {
    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        // deregistration logic
        zookeeperRegistry.unregister(mCurrentServiceURL);
        ...
    }
}

After deregistering, traffic should be blocked using the framework's fault‑tolerance mechanisms.

Docker shutdown

Docker stop sends SIGTERM to PID 1 and waits 10 seconds before sending SIGKILL. The application can handle the SIGTERM to perform graceful shutdown. The timeout can be adjusted with docker stop -t.

External container shutdown (Jetty)

When using an external container, graceful shutdown can be achieved by:

Calling the RPC framework’s graceful‑down interface, which is wrapped in a preStop script executed before the container stops.

Simply adding a kill -15 command to the shutdown script.

2 Graceful Startup

Graceful startup is trickier because there is no default implementation. The principle is to expose the service only after the listening port is ready.

Spring Boot built‑in container startup

Spring Boot can perform health checks before exposing the service. For example, SOFA‑Boot runs component health checks, then middleware checks, and only after all checks pass does it register the service.

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
    healthCheckerProcessor.init();
    healthIndicatorProcessor.init();
    afterHealthCheckCallbackProcessor.init();
    publishBeforeHealthCheckEvent();
    readinessHealthCheck();
}

Because ContextRefreshedEvent may fire multiple times, a more reliable event is ApplicationReadyEvent, which is emitted after the container has fully started and the port is bound.

@Component
public class GracefulStartupListener implements ApplicationListener<ApplicationReadyEvent> {
    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        // registration logic – graceful startup
        apiRegister.register(urls);
        ...
    }
}

External container (Jetty) startup

For external containers, the application cannot directly observe the container’s state. The solution is to let the RPC framework provide a graceful‑up interface, wrap the call in a postStart script, and execute it after the container starts, following the sequence: start container → health check → register service → health‑up check.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

microservicesGraceful Shutdown
Java Interview Crash Guide
Written by

Java Interview Crash Guide

Dedicated to sharing Java interview Q&A; follow and reply "java" to receive a free premium Java interview guide.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.