# 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進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。