How to Achieve Graceful Startup and Shutdown for Microservices with Spring Cloud, Polaris, and Docker
This article explains the principles and practical steps for implementing graceful startup and shutdown in microservices, covering service pre‑warming, registration strategies, Spring Cloud and Polaris demos, Docker container handling, and the benefits and challenges of lossless service lifecycle management.
Microservice graceful startup and shutdown aim to keep services stable and available during deployments by avoiding traffic interruption or errors caused by service changes.
Core Principles
Graceful startup: wait until the service is fully ready (or pre‑warmed) before exposing it.
Lossless shutdown: deregister from the registry, reject new requests, and let in‑flight requests finish before stopping.
Client resilience: use load balancing, retries, or blacklists to route only to healthy instances.
Graceful Startup Methods
Delayed release : postpone exposing the service until initialization (e.g., cache, DB pool) completes.
QoS commands : control service registration via CLI or HTTP, registering only after health checks pass.
Service registration & discovery : register with a registry (e.g., Polaris) after the service is ready and deregister on stop.
Canary release : gradually shift traffic to the new version after monitoring its behavior.
The common idea is to allow traffic only after the service is prepared.
Implementation with Spring Cloud & Polaris
First, create a Spring Cloud project and add Polaris dependencies. Configure application.properties:
spring:
application:
name: ${application.name}
cloud:
polaris:
address: grpc://${POLARIS_ADDRESS}:8091
namespace: defaultCreate a simple provider controller:
@RestController
public class ProviderController {
@Value("${server.port}")
private int port;
@GetMapping("/hello")
public String hello() {
return "Hello, I am provider, port: " + port;
}
}Optionally add a health indicator using Spring Actuator:
@Component
public class DatabaseHealthIndicator implements HealthIndicator {
@Override
public Health health() {
return isDatabaseConnectionOK() ? Health.up().build()
: Health.down().withDetail("Error Code", "DB-001").build();
}
private boolean isDatabaseConnectionOK() {
// check DB, cache, etc.
return true;
}
}For the consumer, enable load‑balanced RestTemplate and call the provider by service name:
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
@Bean
@LoadBalanced // enable client‑side load balancing
public RestTemplate restTemplate() {
return new RestTemplate();
}
@RestController
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/hello")
public String hello() {
return restTemplate.getForObject("http://provider/hello", String.class);
}
}
}Graceful startup steps:
When publishing a new version, start the instance but set spring.cloud.polaris.discovery.register=false or make the health check return unhealthy, so no new traffic reaches it.
After initialization, enable registration ( spring.cloud.polaris.discovery.register=true) or mark health as healthy, allowing traffic to flow.
Service Warm‑up
Warm‑up runs the service in a ready state before full traffic, loading resources and establishing connections. Cloud‑native API gateways (e.g., Tencent Cloud API Gateway) support a “slow start” option that gradually increases instance weight.
Graceful Shutdown Methods
Dubbo‑go : supports graceful shutdown via its registry and load‑balancing features.
Spring Cloud : listen to ContextClosedEvent to deregister, reject new requests, wait for in‑flight requests, then stop.
Docker : use docker stop (sends SIGTERM) so the container can clean up before termination.
Spring Boot graceful shutdown can be enabled with:
# Enable graceful shutdown (default is IMMEDIATE)
server:
shutdown: graceful
# Maximum wait time for each phase
spring:
lifecycle:
timeout-per-shutdown-phase: 30sExpose the Actuator shutdown endpoint:
# Expose shutdown endpoint
management:
endpoint:
shutdown:
enabled: true
endpoints:
web:
exposure:
include: shutdownCalling http://localhost:8080/actuator/shutdown returns:
{
"message": "Shutting down, bye..."
}Docker Graceful Shutdown Demo
Build a simple Node.js app, containerize it, and run two versions:
# Dockerfile
FROM node:14-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD [ "node", "app.js" ] // app.js
const express = require('express');
const app = express();
app.get('/hello', (req, res) => {
res.send('Hello, I am app');
});
app.listen(3000, () => {
console.log('App listening on port 3000');
});Build and run version 1, then version 2, and stop the old container with docker stop app-1. The container must handle SIGTERM, e.g.:
// SIGTERM handler in Node.js
function termHandler() {
console.log('Cleaning up...');
process.exit(0);
}
process.on('SIGTERM', termHandler);Advantages and Challenges
Benefits include minimized service interruption, data loss avoidance, improved user experience, simplified deployment, and better maintainability.
Challenges involve increased system complexity, more elaborate deployment pipelines, potential data consistency issues, and higher skill requirements for teams.
Conclusion
Graceful startup and shutdown are essential for reliable microservice operations. By leveraging service registries (Polaris, Spring Cloud), API gateways, Docker signals, and proper health‑check handling, teams can reduce downtime, protect in‑flight requests, and deliver a smoother user experience, while being aware of the added operational complexity.
Tencent Cloud Middleware
Official account of Tencent Cloud Middleware. Focuses on microservices, messaging middleware and other cloud‑native technology trends, publishing product updates, case studies, and technical insights. Regularly hosts tech salons to share effective solutions.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
