由于springboot自带的停止服务/actuator/shutdown,版本/集成其他插件冲突等种,种原因导致直接抛异常结束。无法满足需求。
@Slf4j
@Component
public class GracefulTomcatTest implements ApplicationListener<ContextClosedEvent> {
@Autowired
private ApplicationInfoManager applicationInfoManager;
@Override
public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
try {
//TODO 拉出服务,不再接收请求 Health.Builder().down().build()
log.info("从注册中心拉出微服务---,{}", contextClosedEvent.getTimestamp());
applicationInfoManager.getInfo().setStatus(InstanceInfo.InstanceStatus.DOWN);
}catch (Exception e){
log.info("拉出异常{}",e);
}finally {
try {
log.info("暂停30秒-等待未处理完成的线程--,{}", contextClosedEvent.getTimestamp());
TimeUnit.SECONDS.sleep(30);
log.info("暂停30秒结束---,{}", contextClosedEvent.getTimestamp());
} catch (InterruptedException e) {
log.info("异常{}",e);
}
}
}
}
kill -15 [pid] 进行验证
验证结果:
1 一个请求耗时20秒,请求进入后,执行 kill -15命令,这个请求会正常执行结束。
2 一个请求耗时40秒,请求进入后,立即执行 kill -15命令,这个请求不会执行完成。(有好的方案再补充)
Spring内置事件
1、ContextRefreshedEvent
ApplicationContext 被初始化或刷新时,该事件被发布。这也可以在 ConfigurableApplicationContext接口中使用 refresh() 方法来发生。此处的初始化是指:所有的Bean被成功装载,后处理Bean被检测并激活,所有Singleton Bean 被预实例化,ApplicationContext容器已就绪可用
2、ContextStartedEvent
当使用 ConfigurableApplicationContext (ApplicationContext子接口)接口中的 start() 方法启动 ApplicationContext 时,该事件被发布。你可以调查你的数据库,或者你可以在接受到这个事件后重启任何停止的应用程序
3、ContextStoppedEvent
当使用 ConfigurableApplicationContext 接口中的 stop() 停止 ApplicationContext 时,发布这个事件。你可以在接受到这个事件后做必要的清理的工作
4、ContextClosedEvent
当使用 ConfigurableApplicationContext 接口中的 close() 方法关闭 ApplicationContext 时,该事件被发布。一个已关闭的上下文到达生命周期末端;它不能被刷新或重启
5、RequestHandledEvent
这是一个 web-specific 事件,告诉所有 bean HTTP 请求已经被服务。只能应用于使用DispatcherServlet的Web应用。在使用Spring作为前端的MVC控制器时,当Spring处理用户请求结束后,系统会自动触发该事件
拓展阅读:springboot开源方案讨论 issues
服务治理 与 无服务无损下线/重启的实战方案
注册中心用的Eureka
重启或停止按钮,先调用eureka-service 拉出服务 ,再暂停7s,执行shudown停止应用
5秒内eureka-client的缓存会更新,DiscoveryClient会将失效的ip剔除。
eureka-service配置
## 心跳间隔,5秒
eureka.instance.leaseRenewalIntervalInSeconds = 5
## 没有心跳的淘汰时间,10秒
eureka.instance.leaseExpirationDurationInSeconds = 10
## 禁用readOnlyCacheMap 或者缩短更新时间,默认30秒
eureka.server. useReadOnlyResponseCache = false
## readOnlyCacheMap 缩短缓存更新时间
eureka.server.response-cache-update-interval-ms = 20000
client配置
# 定义从注册中心获取注册服务的信息
eureka.client.registry-fetch-interval-seconds = 5
#心跳间隔,5秒
eureka.instance.lease-renewal-interval-in-seconds = 5
#没有心跳的淘汰时间,10秒
eureka.instance.lease-expiration-duration-in-seconds = 10