K8s部署SpringBoot服务Pod滚动更新时请求丢失报BeanCreationNotAllowedException
报错信息:org.springframework.beans.factory.BeanCreationNotAllowedException: Error creating bean with name 'controller': Singleton bean creation not allowed while singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!)
该异常本质是Kubernetes Pod终止流程和Spring上下文销毁流程时序不匹配导致:
- K8s触发Pod终止时,服务端点摘除是异步操作,最多需要数秒才能完成全集群路由规则更新,这段时间仍有新请求转发到待终止Pod
- Spring上下文提前启动销毁流程,正在处理/新进入的请求尝试获取Bean时,Singleton Bean工厂已进入销毁阶段,触发对应异常,最终导致请求丢失
配置K8s Pod终止时序适配
调整Pod的YAML配置,增加preStop钩子和合理的终止宽限期,确保路由规则更新完成后再启动Spring停机流程:spec: terminationGracePeriodSeconds: 45 # 需大于Spring优雅停机的等待时长 containers: - name: 你的服务容器名 lifecycle: preStop: exec: command: ["sh", "-c", "sleep 15"] # 时长可根据集群路由更新耗时调整,一般5-20秒即可 # 原有livenessProbe、readinessProbe配置保留,可适当调大livenessProbe的failureThreshold避免滚动更新时误判开启Spring优雅停机
你使用的SpringBoot 2.1.5版本无内置优雅停机配置,建议优先升级到SpringBoot 2.3.x+稳定兼容版本,添加如下配置即可:server: shutdown: graceful spring: lifecycle: timeout-per-shutdown-phase: 30s # 根据业务最长请求耗时调整,保证所有在途请求处理完成再销毁上下文若暂时无法升级SpringBoot版本,可自定义Tomcat连接器停机逻辑:实现
TomcatConnectorCustomizer接口,在停机时先暂停连接器接收新请求,等待现有请求处理完成再继续销毁流程。清理不合理的Bean销毁逻辑
检查代码中所有带@PreDestroy注解的销毁方法、自定义的Bean销毁回调,禁止在销毁逻辑中主动调用getBean()触发新的Bean实例创建;如果存在异步处理逻辑,需配置线程池的awaitTerminationSeconds参数,保证停机时等待异步任务执行完成。版本兼容性优化
Java 12属于非LTS版本,SpringBoot 2.1.5对该版本的生命周期处理存在已知兼容问题,建议降级到Java 11 LTS版本,可避免大量JDK和框架适配问题。
滚动更新发布调整后的配置,在发布期间持续发送请求统计成功率,同时检查服务日志不再出现BeanCreationNotAllowedException异常即可确认修复完成。
内容的提问来源于stack exchange,提问作者Pankaj Garg

