千万级微服务优雅上下线实践

随着会员业务的快速发展,会员系统架构也不断演进迭代,拆分出了多个微服务,提升了系统的稳定性和扩展能力。在敏捷的开发模式下,业务迭代更加快速,那么势必会经常发布线上服务,在服务上线的过程中,我们发现接口成功率会出现一定程度的下降,对于敏感业务直接影响了用户的体验。为了解决这个问题,我们对微服务上下线流程进行了优化,本文将详细介绍方案的设计和实现。

01

问题分析

  异常情况分析  

业务系统当前使用的是 Spring Boot 和 Spring Cloud 框架,服务发布流程如下图所示:

f6a0bbfe790de077264db845e7795358.png

通过梳理服务流程发现,引起服务可用性降低、响应时间突增的原因有以下几点:

  • 过早销毁对象:服务正在处理请求,但此时对象被销毁导致请求报错。

  • 服务未及时下线:调用方不能及时感知服务已在下线中,仍会发送请求过来,但此时对象可能已经被销毁导致请求报错。

  • 过早注册服务:服务未初始化完成就被注册到了注册中心,导致接口响应时间突增甚至超时。

  优化方向  

基于上面的分析,可以通过以下方式解决相关的问题:

  1. 在上线过程中,当服务把依赖的资源都初始化完成后,才将实例注册到注册中心。

  2. 在下线过程中,服务调用方可以排除正在下线的实例,保证在一定的时间窗口内请求不会打到这个实例上。

02

解决方案

系统以集群方式提供服务,实例的上下线状态对业务无感知,由组件封装实例的状态转换,通过优雅上线、优雅下线组合来保证服务的无损发布。

  优雅上线  

28f22796597f1b493ce17e1e663ff7f0.png

通过预热功能实现资源初始化,预热模块是可插拔的,可全使用或者仅使用其中一个模块:

  1. 自定义预热:由业务方自行扩展实现预热逻辑。

  2. 线上请求回放预热:配置预热接口,拉取线上请求对本地服务预热,当接口调用达到配置的预热次数后,再将服务注册到注册中心。

  • 预热

在原生Spring Cloud Netflix基础上,定制开发了服务注册组件 GracefulServiceRegistration 并抽象了预热组件 WarmUp。在 Spring 容器初始化过程中,会扫描所有 WarmUp 实现类并注入到容器中,启动完成后由 GracefulServiceRegistration 组件调用 WarmUp 接口所有实现类的预热方法进行服务预热,预热完成之后再进行服务注册。

a4be752b7c87bd31ef1456de675c5d77.png

图例说明:

  • VClientAutoConfiguration:服务注册配置类,负责初始化GracefulServiceRegistration

  • GracefulServiceRegistration:服务注册类,触发服务预热逻辑执行

  • WarmUp:预热组件,由业务方自行扩展实现预热逻辑。框架默认实现:延迟5s(可配置)再执行服务注册、线上请求回放预热

  优雅下线  

优雅下线通过延迟下线和可靠负载功能组合实现,在下线过程中,服务实例需要先去取消注册并将自己标记为已下线,后续的接口请求都将获取到该实例的已下线标记。服务调用方根据下线标记把该实例从可用服务列表中剔除,保证在后续一定时间窗口内的请求都不会再打到这个实例上。具体交互流程见下图:

9d688d38b5011dac9ac37b0edce09fdd.png

  • 延迟下线

上文提到在原生Spring Cloud Netflix基础上,定制开发了 GracefulServiceRegistration、WarmUp 等组件,在解决优雅下线问题时,我们又增加了调用插件 InvokePlugin。当 JVM 监听到 SIGTERM 信号时,下线钩子线程开始工作,先执行取消注册,然后通过 GracefulServiceRegistration 标记当前服务为下线中状态,并阻塞当前线程 5s(可配置)来保证当前正在处理的请求能够成功返回。如果此时收到调用方请求,InvokePlugin 会检查当前服务状态是否为下线中,如果是,直接返回下线标记。最后下线钩子线程被唤醒,再执行对象销毁逻辑。

d4a6eb071002a3948563e00223d7b4b6.png

图例说明:

  • VClientAutoConfiguration:服务注册配置类,负责初始化 InvokePlugin、GracefulServiceRegistration 等组件

  • GracefulServiceRegistration:服务注册类,负责延迟销毁对象、触发服务预热逻辑执行

  • InvokePlugin:请求调用插件类,负责执行请求时检查服务实例状态是否在下线中,如果在下线中,直接返回下线标记

  • 可靠负载

微服务框架使用Ribbon作为负载均衡策略,默认是轮询机制,BaseLoadBalancer 中维护了两个注册表集合:全量注册表 allServerList、可用注册表 upServerList,但是原生只使用了全量注册表,通过循环判断获取可用实例,这种方式可能会获取到不可用的实例,所以我们对逻辑进行了优化,新增一个路由规则,使用可用注册表保存可用服务实例,并增加任务剔除标记已下线的实例。

负载策略实现方式为,服务调用方接收到实例的下线标记时,将该实例加入失活队列,独立的任务线程处理失活队列,并维护可用注册表,且失活队列的另一个任务是在同步注册中心最新注册表的时候,不要把已排除的实例恢复到可用注册表中。通过重试 + 排除下线实例的方式,使业务得到更高的可用性。具体设计如下:

db4c0617723ff395c9e1d3364d9d9bc1.png

03

成果与总结

对接优雅上下线功能的服务在上线过程中,服务成功率可以提升到99.99%以上,有效解决了服务上线成功率的问题。对比数据见下图示例:

021de9544bafb9288196704e189c78c5.jpeg

无优雅上下线(并行1台滚动上线)

960b8b4630f95944bbfaf75e1c39d99c.png

开启优雅上下线(并行1台滚动上线)

b09348706bf7e74c334b1a69d6a2f6e9.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值