微服务上下线的痛点场景
Spring Boot微服务在滚动更新过程中,如果没有正确配置优雅上下线策略,会出现两类典型问题:下线时,已有请求尚未处理完毕就被强制中断,返回502或连接重置;上线时,新实例启动完成但尚未完成资源预热(数据库连接池、JIT编译、缓存填充),过早接收流量导致请求超时或慢响应。这两类问题在Kubernetes滚动更新场景下尤为常见,因为Kubernetes默认的就绪检查机制相对简单,无法准确反映服务的真实可用状态。
优雅上下线的本质是流量管控:下线时让存量请求处理完再关闭进程,上线时等服务真正就绪后再接收流量。下面从Spring Boot应用层面和基础设施层面两个维度展开方案。
优雅下线:Spring Boot应用层配置
Spring Boot 2.3+内置了优雅停机支持,配置后收到SIGTERM信号时,Web服务器会停止接收新请求,等待已有请求处理完毕再关闭。
# application.yml - 优雅停机配置
server:
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 30s
management:
endpoints:
web:
exposure:
include: health,info,readiness,liveness
endpoint:
health:
show-details: always
health:
readinessState:
enabled: true
livenessState:
enabled: true
优雅停机的实现原理:Spring Boot收到SIGTERM后,先将ApplicationContext标记为closing状态,Web服务器拒绝新连接(返回Connection: close头),已建立的连接继续处理。超过timeout-per-shutdown-phase后强制关闭。需要注意的是,如果业务线程持有分布式锁或正在执行数据库事务,超时关闭可能导致锁未释放或事务回滚不完整,需要在应用层注册ShutdownHook做清理。
// 注册停机清理逻辑
@Component
public class GracefulShutdownHandler implements DisposableBean {
@Autowired
private RedissonClient redissonClient;
@Override
public void destroy() throws Exception {
redissonClient.getLocks().forEach(lock -> {
try {
lock.forceUnlock();
} catch (Exception e) {
log.warn("Failed to unlock: {}", lock.getName(), e);
}
});
log.info("Shutdown cleanup completed");
}
}
优雅上线:Readiness Probe预热策略
Kubernetes的readiness probe决定Pod是否接收Service流量。Spring Boot Actuator的/actuator/health/readiness端点与Kubernetes readiness probe对接,但默认行为只是检查ApplicationContext是否就绪,不包含业务预热逻辑。
// 自定义Readiness指示器 - 预热完成后才标记就绪
@Component
public class ServiceReadinessIndicator implements ReadinessIndicator {
private volatile boolean warmedUp = false;
@PostConstruct
public void warmUp() {
CompletableFuture.runAsync(() -> {
try {
warmUpConnectionPool();
warmUpJit();
warmUpCache();
warmedUp = true;
log.info("Service warm-up completed");
} catch (Exception e) {
log.error("Warm-up failed", e);
warmedUp = true;
}
});
}
private void warmUpConnectionPool() {
jdbcTemplate.queryForObject("SELECT 1", Integer.class);
}
private void warmUpJit() {
orderService.calculateTotal(new OrderDto());
}
private void warmUpCache() {
cacheService.preloadHotKeys();
}
@Override
public Health health() {
if (warmedUp) {
return Health.up().build();
}
return Health.down().withDetail("reason", "warming up").build();
}
}
Kubernetes侧的优雅上下线配置
应用层配置只是方案的一部分,Kubernetes侧的Pod生命周期管理同样关键。
# Deployment配置 - 完整的优雅上下线策略
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
spec:
terminationGracePeriodSeconds: 60
containers:
- name: order-service
image: order-service:1.2.0
ports:
- containerPort: 8080
startupProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
failureThreshold: 30
periodSeconds: 2
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
periodSeconds: 5
failureThreshold: 3
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 5"]
preStop中的sleep 5看似简单,实际解决了关键问题:Kubernetes删除Pod时,先从Endpoints列表移除Pod,再发送SIGTERM。但Endpoint的传播到kube-proxy有延迟,如果立即停机,仍有流量路由到已关闭的Pod。sleep 5秒给kube-proxy足够时间更新iptables规则,确保新流量不再路由到该Pod。
网关层流量切换策略
对于有API网关的架构,可以在网关层实现更精细的流量控制。Spring Cloud Gateway配合Nacos或Consul的服务发现,可以实现权重路由和金丝雀发布。
# Spring Cloud Gateway权重路由配置
spring:
cloud:
gateway:
routes:
- id: order-service-v1
uri: lb://order-service-v1
predicates:
- Path=/api/orders/**
- Weight=group1, 90
- id: order-service-v2
uri: lb://order-service-v2
predicates:
- Path=/api/orders/**
- Weight=group1, 10
权重路由方案适用于灰度发布场景。在金丝雀发布中,v2版本先接收10%流量观察5分钟,错误率和延迟正常后逐步调高权重至100%。整个过程中,旧版本Pod保持运行,随时可以将流量切回,回滚速度远快于重新部署。
消息中间件场景的优雅上下线
使用RabbitMQ或Kafka消费消息的微服务,上下线时需要额外处理消息消费的完整性。下线前停止消费新消息,等待已拉取的消息处理完毕再关闭。
// Kafka消费者优雅停机
@Component
public class OrderConsumerGracefulShutdown {
private final KafkaMessageListenerContainer<?, ?> container;
@EventListener
public void onApplicationEvent(ContextClosedEvent event) {
container.stop(() -> {
log.info("Kafka consumer stopped, waiting for in-flight messages");
});
}
}
# Kafka消费者配置
spring:
kafka:
consumer:
auto-offset-reset: earliest
enable-auto-commit: false
listener:
ack-mode: manual_immediate
concurrency: 3
优雅上下线是微服务稳定性保障的基础能力。Spring Boot的graceful shutdown + Kubernetes的probe + preStop hook + 网关层流量控制的组合,覆盖了绝大多数生产场景。核心原则:下线时先摘流量再停进程,上线时先预热再接流量。把这个原则嵌入到CI/CD流水线和Kubernetes配置模板中,所有微服务默认继承,避免遗漏。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/springboot-wei-fu-wu-you-ya-shang-xia-xian-jian-kang-jian/