Spring Boot优雅停机:高并发服务的必答题
微服务滚动更新时,旧实例被直接杀掉是线上事故的高频原因。进程被SIGKILL的瞬间,正在执行的请求被截断,数据库事务处于中间状态,消息消费者提交了偏移量但业务逻辑没执行完。Spring Boot从2.3版本开始支持优雅停机(Graceful Shutdown),但默认配置不够用,需要针对不同服务类型做定制。
基础配置:
# application.yml
server:
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 30s
这个配置的工作原理:收到SIGTERM后,Spring Boot不再接收新请求,等待正在处理的请求完成,超过30秒后强制关闭。问题在于,30秒对短请求够用,对长事务(批量导入、大文件处理、复杂计算)远远不够。需要分服务类型设置不同超时:
@Configuration
public class GracefulShutdownConfig {
@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory>
tomcatCustomizer() {
return factory -> {
factory.addConnectorCustomizers(connector -> {
connector.setProperty("maxThreads", "200");
connector.setProperty("acceptCount", "0");
});
};
}
@Bean
public ApplicationListener<ContextClosedEvent> shutdownHook(
@Value("${spring.lifecycle.timeout-per-shutdown-phase}") Duration timeout) {
return event -> {
log.info("收到停机信号,开始优雅停机,超时: {}", timeout);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
cleanupResources();
}));
};
}
}
对于消息消费者,优雅停机的关键是在关闭前停止拉取新消息,等当前消息处理完再提交偏移量:
@Component
public class KafkaGracefulShutdown implements DisposableBean {
@Autowired
private KafkaListenerEndpointRegistry registry;
@Override
public void destroy() throws Exception {
// 1. 暂停所有消费者(停止拉取新消息)
registry.getListenerContainers().forEach(container -> {
container.pause();
log.info("Kafka消费者已暂停: {}", container.getListenerId());
});
// 2. 等待当前消息处理完成(最多等60秒)
int waited = 0;
while (waited < 60) {
boolean allIdle = registry.getListenerContainers().stream()
.allMatch(c -> c.isContainerPaused() &&
c.getContainersIdleTime() > 1000);
if (allIdle) break;
Thread.sleep(1000);
waited++;
}
// 3. 停止消费者
registry.stop();
log.info("Kafka消费者已停止");
}
}
高并发场景下的服务治理:限流、熔断、降级
微服务在高并发场景下的稳定性依赖三个核心治理手段:限流保护自己,熔断保护下游,降级保住核心链路。Sentinel是Spring Cloud生态中功能最全的方案,支持流量控制、熔断降级、热点参数限流、系统自适应保护。
限流配置示例——针对API接口的QPS限流:
@Configuration
public class SentinelConfig {
@PostConstruct
public void initRules() {
List<FlowRule> rules = new ArrayList<>();
// API接口限流:单机QPS不超过500
FlowRule apiRule = new FlowRule();
apiRule.setResource("order-api");
apiRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
apiRule.setCount(500);
apiRule.setLimitApp("default");
apiRule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP);
apiRule.setWarmUpPeriodSec(10);
rules.add(apiRule);
// 热点参数限流:单用户QPS不超过50
ParamFlowRule userRule = new ParamFlowRule();
userRule.setResource("order-api");
userRule.setParamIdx(0);
userRule.setCount(50);
userRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
FlowRuleManager.loadRules(rules);
ParamFlowRuleManager.loadRules(Collections.singletonList(userRule));
}
}
熔断降级配置——针对下游服务的慢调用熔断:
@Configuration
public class DegradeConfig {
@PostConstruct
public void initDegradeRules() {
List<DegradeRule> rules = new ArrayList<>();
// 慢调用比例熔断
DegradeRule slowCallRule = new DegradeRule("payment-service");
slowCallRule.setGrade(CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType());
slowCallRule.setCount(1000);
slowCallRule.setSlowRatioThreshold(0.6);
slowCallRule.setTimeWindow(30);
slowCallRule.setMinRequestAmount(10);
slowCallRule.setStatIntervalMs(5000);
rules.add(slowCallRule);
DegradeRuleManager.loadRules(rules);
}
}
分布式事务:Seata AT模式的生产级配置
微服务架构下的跨服务数据一致性是后端开发的老难题。Seata的AT模式是目前侵入性最低的方案,通过拦截SQL自动生成回滚日志,业务代码无需改动。生产级配置的关键在于事务分组和异常处理:
# seata配置
seata:
enabled: true
application-id: order-service
tx-service-group: order-tx-group
service:
vgroup-mapping:
order-tx-group: default
registry:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
namespace: seata
group: SEATA_GROUP
config:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
namespace: seata
分布式事务的使用方式——注解驱动:
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private AccountFeignClient accountClient;
@Autowired
private InventoryFeignClient inventoryClient;
@GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
public OrderResult createOrder(OrderDTO orderDTO) {
// 1. 创建订单
Order order = new Order();
order.setUserId(orderDTO.getUserId());
order.setAmount(orderDTO.getAmount());
order.setStatus("CREATED");
orderMapper.insert(order);
// 2. 扣减库存(远程调用)
inventoryClient.deduct(orderDTO.getProductId(), orderDTO.getQuantity());
// 3. 扣减账户余额(远程调用)
accountClient.debit(orderDTO.getUserId(), orderDTO.getAmount());
// 4. 更新订单状态
order.setStatus("PAID");
orderMapper.updateById(order);
return OrderResult.success(order.getId());
}
}
Seata AT模式的核心风险是全局锁。当一个分布式事务持有某行数据的全局锁时,其他事务对同一行的写操作会被阻塞。在高并发下单场景下,如果热门商品的全局锁竞争激烈,吞吐量会大幅下降。解决方案是缩小事务范围——只在核心扣减环节使用分布式事务,其他非关键操作异步化处理。
微服务的服务治理没有银弹,限流、熔断、降级、分布式事务每个环节都有取舍。选择方案的标准不是技术先进性,而是业务场景的容忍度:可以接受多少延迟、可以容忍多少数据不一致、可以承受多少服务不可用。把这几个问题回答清楚,方案选择自然就有了答案。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/springboot-wei-fu-wu-you-ya-ting-ji-shi-zhan-gao-bing-fa/