怎么实现优雅升级


近日同事分享了公司优雅升级的架构方案,借此学习整理了下关于优雅升级方面的知识。

1. 非优雅升级

1.1 流程

一般我们叫这种升级为停机上线。业务侧会发布一个公告,晚上xx点系统升级。然后截断请求进入,这个时候前后端就会进行系统的升级,也就是上线。这种问题会导致很多问题,有时候我们不得不为此写一些不必要的代码进行补偿。

1.2 问题

在传统项目中,如果没有采用优雅升级的方式,可能会遇到以下问题:

  1. 中断用户体验:传统项目的非优雅升级可能导致系统停机或无法正常运行,从而中断用户的正常使用体验。 具体到用户的感受就是某个app不能用了。
  2. 数据丢失或损坏:升级过程中,如果没有正确处理数据迁移或备份,可能会导致数据丢失或损坏,造成重要数据的丢失。
  3. 请求链路中断:上游仍然调用已经下线的节点导致请求报错导致业务异常
  4. 长时间停机:传统项目的非优雅升级可能需要长时间的停机来进行升级和部署,这会对业务运行和用户体验造成不利影响。
  5. 难以协调资源:需要停机升级,时间通常需要选择在深夜,发生问题时处理难以协调资源
  6. 浪费人力资源:传统项目的非优雅升级经常是半夜或者凌晨,经过一夜的消耗,人的精力很难短时间内回复,浪费大量的人力

2. 优雅升级

2.1 原理流程

优雅升级的流程很简单,也就是以下几个流程:

  1. 网络设施切走旧实例流量,不让新的请求流入到此实例。一般我们一个项目是多实例的,流量会被其他实例承接处理。
  2. 旧实例清理资源开始关闭:线程池,剩余请求处理等。没有新的请求进来了,但是服务中有可能还有未结束的线程,这个时候就要等这些线程都处理完才能关闭此实例。
  3. 新实例启动,如果实例启动失败,会回滚到老版本;如果启动成功,会通知协调节点新服务启动成功。
  4. 旧实例停止完成,新实例启动成功,流量接入,新实例接入实例集群。

2.2 基于Nginx和Jenkins的实现的实现

2.2.1 实现流程

此实现需要开发和运维分工合作:

  1. 开发:提供优雅停机需要的接口
  2. 运维:流量控制,流程执行

开发需要在应用中实现2个功能:
1.开发健康检查http接口提供给运维,用于检查项目是否正常运行;
2.开发资源释放接口,有些资源需要我们在代码中判断是否释放完成,比如未被spring托管的线程池等。

运维侧需要配置以下:

  1. 给nginx配置http_upstream_check_module用于检测应用存活状态,配置地址为健康检查接口地址。
  2. 运维-在Jenkins中创建2个任务:1.升级触发任务;2.应用升级部署任务
  3. 运维为应用注入Jenkins任务ID作为环境变量

2.2.2 实现原理

插入图片

2.2.2.1 开发编码实现
  1. 停止接受请求: Servlet Server支持,直接调用停机脚本。不支持可通过Filter或interceptor实现,Response直接返回。

  2. 处理完已接收请求。Servlet Server支持则不用处理。使用Filter或interceptor或ServletRequestListener实现请求计数,请求进入时计数器+1,请求响应后计数器-1,注册JVM shdownhook,循环检测请求数阻止JVM关闭,至到请求数为小于等于0跳出循环,关闭应用。

  3. 主要清理资源包含各种线程池,如果使用框架,各框架会对自己管理的线程池进行优雅关闭,无需处理。不使用框架,或在使用框架时未将线程池托管给框架管理,则需要手动进行清理。如果未使用框架,方法同样是使用JVM shdownhook,在回调方法中关闭线程池(shutdown方法)。

可以看到以前的优雅升级还是要开发做很多开发工作的,包括流量关闭,服务的关闭管理,资源的手动释放等。

2.2.2.2 使用Spring boot actuator实现

1.启用Spring boot actuator,Actuator 框架内提供了大量的监测服务的功能,其中就有健康检查的功能。

2.使用线程时,使用线程池,并将线程池托管给Spring。我们在关闭程序的时候,如果线程池没有交给spring管理,服务是无法监测线程池资源是否已经释放。

3.需要清理其他资源时,监听ContextClosedEvent进行处理。加入服务中还有比较重要的逻辑需要验证是否释放,我们可以ContextClosedEvent监听器自定义编写逻辑释放资源。

2.3 基于阿里云K8S实现的实现

开发侧需要在应用中实现2个功能:
1.开发健康检查http接口,我们也可以使用Spring boot actuator的健康检查接口
2.开发优雅停机功能,我们也可以自定义一些释放资源类的接口给阿里云使用,这个看具体的项目是否需要。

运维侧需要在在项目部署时,配置以下信息:
1.Readiness:一种探测容器状态的探针,探测应用是否启动完成并且处于正常服务状态。
2.Liveness: 一种探测容器状态的探针,探测应用是否处于健康状态。
3.PreStop: 一种容器钩子。该钩子在容器被删除前触发。
4.TerminationGracePeriod: 主要是配置优雅停机限制时间,超过这个限制时间有可能强制关闭。

可以看到Readiness和Liveness对应的是我们开发侧提供的那两个接口;PreStop是运维配置的,一般是停机命令的shell脚本;TerminationGracePeriod是优雅停机限制时间,为了防止服务卡死,超过时间会直接停机。

2.3.1 配置实现

spring-boot-starter-actuator文档中说是支持优雅关闭,但仅仅是spring层面上的,不和tomcat等容器挂钩,直到spring boot 2.3开启自带的优雅关闭后才真正能实现,也就是说2.3之前的版本根本实现不了优雅关闭,需要自己来进一步按照使用的容器做处理才行。

2.3.1.1 引入依赖包
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2.3.1.2 配置yml文件
# 开启优雅关闭
server:
  shutdown: graceful

# 配置强制结束时间,不配置的话默认30s  
spring:
  lifecycle:
    # 优雅停机超时时间:3分钟,超过3分钟强制关闭
    timeout-per-shutdown-phase: 3M
    
# --------------------- k8s健康检查接口配置 ---------------------
management:
  endpoints:
    # 关闭所有endpoint,需要的endpoint手动开启
    enabled-by-default: false
    #以http方式暴露
    web:
      exposure:
        include: "*"
  # 需要开启的endpoint
  endpoint:
    info:
      enabled: true
     # 开启健康检查
    health:
      enabled: true
  # 健康检查接口与应用服务器端口可以不相同
  server:
    port: 52060
  info:
    git:
      # 不显示git信息
      enabled: false
2.3.1.3 编写Dockerfile镜像

项目中的Dockerfile配置,这个根据各个公司要求来。有的实在项目中,有的是运维配置。

FROM ${Docker镜像}
ADD target/${自己项目打出来的jar包名}.jar /app/app.jar
EXPOSE 8080
ENTRYPOINT ["sh","-c","java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app/app.jar"]

优雅关闭的脚本就在该基础镜像中,假如脚本是/pre-stop.sh,里边的命令大概就是找到Java项目,使用 kill -15 优雅关闭。kill-15是指在unix或linux系统中通过kill命令向一个进程发送信号SIGTERM(信号编号为15),通知该进程优雅结束其进程。

2.3.1.4 运维部署

阿里云上进行优雅升级的配置,内容可按如下设置:
PreStop配置:
command: - /bin/sh - /app/pre-stop.sh

TerminationGracePeriod配置:
默认60秒。需要通知运维调整为180秒,请注意,如果有定时调度任务,请将定时调度进行优雅关闭处理,然后合理评估该时间。

Liveness配置:
端口:52060 (健康检查接口与应用服务器端口可以不相同)
Path:/actuator/health

Readiness配置:
端口:52060 (健康检查接口与应用服务器端口可以不相同)
Path:/actuator/health

以上配置只是简单的配置,具体参数配置可以根据项目情况配置。

2.3.1.5 验证方式

1.起个健康检查分支,写一个简单的接口,只返回一段文本信息,无需登录验证。
2.在测试环境部署配置了健康检查的接口。
5.使用postman或者本地写个代码循环调用第一步的接口。
6.将步骤1中写的接口放到postman的runner中,循环次数设置1000次,间隔设置200毫秒。
7.重启服务的同时,执行postman的runner。
7.查看调用情况,根据运行情况做相应修改。

3. 总结

上边只是优雅停机的主要步骤,根据公司使用的发布工具,服务器还可以配置其他很多东西,比如短信通知,钉钉通知,邮件通知等。具体还是要开发和运维配合完成,本文只作为参考意见。

  1. 通知用户:在停机之前,通过合适的渠道(例如邮件、公告等)提前通知用户服务器将会关闭。这样用户可以提前做好准备,并避免在关闭期间遇到任何不便。

  2. 暂停新连接:在关闭服务器之前,停止接受新的连接请求。这可以通过禁用相关服务或调整网络配置来实现。这样可以确保在服务器关闭期间不会有新的用户连接进来。

  3. 等待当前任务完成:在停机之前,确保当前正在执行的任务能够正常完成。这可以通过监控系统中的任务队列或进程来实现。等待任务完成可以避免数据丢失或系统不稳定。

  4. 保存数据:在关闭服务器之前,确保重要的数据已经保存。这包括数据库的备份、文件的存档等。确保数据的完整性和安全性是服务器优雅停机的重要一步。

  5. 关闭服务:在完成以上步骤后,可以正式关闭服务器。这可以通过执行适当的关闭命令或脚本来实现。关闭服务器时,确保按照正确的顺序关闭各个服务,以避免数据损坏或其他问题。

  6. 监控关闭过程:在服务器关闭期间,密切监控系统的状态。这可以通过日志记录、监控工具等方式实现。确保服务器正常关闭,并及时处理任何异常情况。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值