微服务如何优雅停机

举报
yk02901 发表于 2021/05/08 15:55:07 2021/05/08
【摘要】 目的为了解决版本发布时停机造成业务处理异常,如何停机可以使用户无感知,做到7*24进行业务处理最开始的处理方法:kill -9 java进程idkill -9 属于强杀进程,首先微服务正在执行的任务被强制中断了;其次,没有通过Eureka注册中心服务下线,Eureka Client仍保存这个服务的路由信息,上游系统会继续调用服务,请求会出现503服务不可用的错误信息改进之后处理方法:kill...

目的

为了解决版本发布时停机造成业务处理异常,如何停机可以使用户无感知,做到7*24进行业务处理


最开始的处理方法:kill -9 java进程id

kill -9 属于强杀进程,首先微服务正在执行的任务被强制中断了;其次,没有通过Eureka注册中心服务下线,Eureka Client仍保存这个服务的路由信息,上游系统会继续调用服务,请求会出现503服务不可用的错误信息


改进之后处理方法:


kill java进程号后给JVM进程发送TERM终止信号时,会调用其注册的 Shutdown Hook,当SpringBoot微服务启动时也注册了 Shutdown Hook,主要关注的Spring的应用上下文抽象类AbstractApplicationContext注册了针对整个Spring容器的Shutdown Hook,在执行Shutdown Hook时的逻辑在 AbstractApplicationContext#doClose()

AbstractApplicationContext#doClose()方法


AbstractApplicationContext#doClose() 的关键点在于

  • publishEvent(new ContextClosedEvent(this)): 发送ContextClosedEvent事件,会有对应此事件的Listener处理相应的逻辑
  • getLifecycleProcessor().onClose(): 调用所有 Lifecycle bean 的 stop() 方法

而ContextClosedEvent事件的Listener有很多,实现了Lifecycle生命周期接口的bean也很多,但其中我们关心一个,即 EurekaAutoServiceRegistration ,它即监听了ContextClosedEvent事件,也实现了Lifecycle接口


EurekaAutoServiceRegistration的stop()事件

EurekaAutoServiceRegistration中对 ContextClosedEvent事件 和 Lifecycle接口 的实现都调用了stop()方法,虽然都调用了stop()方法,但由于各种对于状态的判断导致不会重复执行,如

  • Lifecycle的running标示置为false,就不会调用到此Lifecycle#stop()
  • EurekaServiceRegistry#deregister()方法包含将实例状态置为DOWN操作,其中状态置为DOWN一次后,下一次只要状态不变就不会触发状态同步请求


EurekaServiceRegistry#deregister() 注销

主要更新Instance状态为 DOWN: 更新状态会触发StatusChangeListener监听器,状态同步器InstanceInfoReplicator会向Eureka Server发送状态更新请求。实际上状态更新和Eureka Client第一次注册时都是调用的DiscoveryClient.register(),都是发送POST /eureka/apps/appID请求到Eureka Server,只不过请求Body中的Instance实例状态不同。执行完此步骤后,Eureka Server页面该服务状态会变为DOWN。


之后我们加一个ContextClosedEvent实现类将优先级设置为最低

这里duration睡的时间设置的是90s

30s(Eureka Server服务列表刷新缓存ReadOnlyMap的时间,Eureka Client默认读此缓存)
+30s(Eureka Client默认每30秒拉取一次服务列表)
+30s(loadBalance默认动态刷新其ServerList的时间间隔)
=90s


用90s的时间来处理上游系统由于缓存继续发过来的信息,这事件已经是优先级最低,因此EurekaServiceRegistry已经将该服务状态置为DOWN,running状态为false,Eureka Server以及其他服务拉取列表时也不会将此服务拉取,等90s之后再DiscoveryManager.getInstance().shutdownComponent();将服务从Eureka下线,以及this.gracefulShutdownHandler.shutdown();将Undertow容器关闭


注意:不要先将服务从Eureka下线,不然此服务虽然还能还能接收上游系统的请求,但是无法调用Eureka上其他下游系统,因为从Eureka下线后服务列表已经清空了。

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。