# SpringBoot项目在Docker容器中该如何优雅关闭 ## 引言 在微服务架构盛行的今天,Docker已成为应用部署的标准环境之一。SpringBoot作为Java生态中最流行的微服务框架,与Docker的结合堪称完美组合。然而在实际生产环境中,我们经常会遇到这样的场景:当需要停止或重启容器时,应用突然中断导致请求丢失、数据不一致等问题。本文将深入探讨如何实现SpringBoot应用在Docker容器中的优雅关闭(Graceful Shutdown),确保服务平滑下线。 ## 一、什么是优雅关闭? ### 1.1 基本概念 优雅关闭是指在应用停止前,系统能够: - 完成正在处理的请求 - 释放占用的资源(数据库连接、线程池等) - 拒绝新的请求进入 - 执行必要的清理工作 ### 1.2 非优雅关闭的后果 ```java // 示例:未处理中断信号的线程 @RestController public class LongProcessController { @GetMapping("/long-process") public String longProcess() throws InterruptedException { // 模拟长时间处理(30秒) Thread.sleep(30000); return "Process completed"; } }
当容器突然停止时: - 客户端收到连接重置错误 - 服务端可能产生部分完成的事务 - 数据库连接等资源未正确释放
SpringBoot 2.3+版本原生支持优雅关闭:
# application.yml server: shutdown: graceful # 启用优雅关闭模式 spring: lifecycle: timeout-per-shutdown-phase: 30s # 最大等待时间
# 最佳实践:使用tini作为init进程 ENTRYPOINT ["/tini", "--", "java", "-jar", "app.jar"]
Docker停止命令的默认行为: 1. docker stop
→ 发送SIGTERM 2. 等待10秒(默认超时) 3. 发送SIGKILL强制终止
# 设置停止超时为35秒(大于SpringBoot的30秒) STOPSIGNAL SIGTERM STOP_TIMEOUT 35
@Configuration public class GracefulShutdownConfig { @Bean public GracefulShutdown gracefulShutdown() { return new GracefulShutdown(); } @Bean public ServletWebServerFactory webServerFactory() { TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); factory.addConnectorCustomizers(gracefulShutdown()); return factory; } }
FROM eclipse-temurin:17-jre # 1. 使用tini处理信号 RUN apt-get update && apt-get install -y tini ENTRYPOINT ["/usr/bin/tini", "--"] # 2. 添加健康检查 HEALTHCHECK --interval=30s --timeout=3s \ CMD curl -f http://localhost:8080/actuator/health || exit 1 # 3. 设置JVM参数 CMD ["java", "-jar", "-Dspring.lifecycle.timeout-per-shutdown-phase=30s", "-Dserver.shutdown=graceful", "app.jar"]
apiVersion: apps/v1 kind: Deployment spec: template: spec: containers: - name: app lifecycle: preStop: exec: command: - sh - -c - "sleep 10 && curl -X POST http://localhost:8080/actuator/shutdown" readinessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 20 periodSeconds: 5
// 使用Spring Cloud的服务下线通知 @PreDestroy public void deregisterService() { discoveryClient.deregister(); // 等待注册中心传播 Thread.sleep(5000); }
@Service public class OrderService { @Transactional public void processOrder(Order order) { // 1. 检查事务状态 TransactionSynchronizationManager.registerSynchronization( new TransactionSynchronization() { @Override public void beforeCompletion() { // 事务提交前的回调 } }); // 2. 使用短事务 for (Item item : order.getItems()) { processItem(item); } } }
@KafkaListener(topics = "orders") public void listen(Order order) { // 使用手动提交 Acknowledgment ack = context.getAcknowledgment(); try { process(order); ack.acknowledge(); } catch (Exception e) { log.error("Process failed", e); } }
# 1. 启动容器 docker run -d -p 8080:8080 --name myapp myimage # 2. 发起长请求 curl http://localhost:8080/long-process & # 3. 停止容器 time docker stop myapp # 4. 检查日志 docker logs myapp | grep -i shutdown
2023-01-01 12:00:00 | Received SIGTERM 2023-01-01 12:00:00 | Web server stopped accepting new requests 2023-01-01 12:00:25 | Completed ongoing requests 2023-01-01 12:00:25 | Shutdown completed
解决方案: - 分析线程转储(thread dump)找出阻塞点
jcmd <PID> Thread.print > thread.log
优化方案:
# 调整就绪探针 readinessProbe: failureThreshold: 3 periodSeconds: 10
验证方法:
# 测试信号处理 STOPSIGNAL SIGTERM CMD ["sh", "-c", "trap 'echo Received SIGTERM' SIGTERM; while true; do sleep 1; done"]
server.shutdown=graceful
配置项 | 推荐值 | 说明 |
---|---|---|
shutdown-phase-timeout | 25-30s | 兼顾用户体验和部署速度 |
STOP_TIMEOUT | shutdown-timeout + 5s | 留出缓冲时间 |
探针间隔 | 5s | 快速感知又不造成压力 |
通过本文介绍的方法,您的SpringBoot应用将能够在Docker环境中实现真正的优雅关闭,显著提升系统的可靠性和维护体验。记住,优雅关闭不仅是技术实现,更是一种系统设计哲学。 “`
注:本文实际约3200字,包含了代码示例、配置片段、表格等结构化内容,采用Markdown格式便于技术文档的传播和版本控制。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。