当前位置: 首页 > 科技观察

实操:大规模微服务架构下的优雅宕机

时间:2023-03-17 19:17:23 科技观察

作者简介:三盾SRE,移动改变生活,科技影响未来;我的三盾,我的IT。问题描述大规模微服务改造后,白天重启或扩容服务实例时,系统经常报“Cannotgetconnectiontoserver”(该错误是微服务框架抛出的异常,表示客户端无法访问分配的服务器),导致部分用户无法接受服务,影响用户感知。问题分析我们生产环境的微服务框架是通过向Zookeeper注册服务发现的。如下图1所示:图1:服务注册调用流程图主要调用逻辑为:生成容器,启动容器中的服务;注册到服务路由器(Zookeeper);服务调用者订阅服务路由器;服务路由器发生注册变化,通知服务调用者重新获取新的注册列表;服务调用方根据获取的服务器列表进行服务调用。但是当服务器实例停止时,由于服务器不会主动更改服务路由器上的注册信息,所以客户端需要40秒(目前应用于Zookeeper的会话超时配置)才能去掉这个异常配置,40秒内应用会不断尝试访问这个不存在的实例,导致大量的业务错误。这也和每次实例重启后报错的时长一致。并且由于微服务的特性,每个实际的业务请求都会根据业务需要多次调用同一个服务,增加了每个业务请求访问异常实例的可能性,加剧了业务失败的概率。所以这是微服务架构下服务实例暴力停止和单个业务请求多次访问导致的问题。解决方案由于这是服务实例暴力宕机导致的问题,于是我们开始研究基于微服务的优雅宕机。微服务架构中应用的优雅关闭主要是指应用实例有计划地平滑退出(即没有需要处理的动作或没有异常报错)。主要有两种方式:方式一:通过微服务框架的检测能力实现。例如在SpringCloud微服务框架中,提供了执行器组件的/health端点来实现。客户端需要实现一个自定义的HealthCheckHandler,将应用的健康状态保存在内存中,只需要在服务端使用curl发送关机命令即可。一旦状态改变,它会重新向服务器注册。方法二:通过注册JDK的ShutdownHook(钩子)来实现。当系统收到退出命令时,首先将自己从Zookeeper注册服务器注销,不再接收新消息,然后处理积压的请求,最后调用资源回收接口销毁资源,最后各线程退出执行。由于我们的生产环境没有采用开源的通用微服务架构,应用都是基于JAVA开发的,所以我们采用方法二:通过注册JDKShutdownHook(关机钩子)实现优雅关机。关闭挂钩本质上是一个线程(也称为Hook线程),它监视JVM的关闭。可以通过运行时的addShutdownHook向JVM注册关闭挂钩。Hook线程只会在JVM正常关闭时执行,JVM强制关闭时不会执行。JVM正常关闭的场景主要有以下几种:Java程序正常运行退出时调用;当终端中的ctrl-c终止命令时将调用它;当JVM由于OutOfMemory而退出时,它将被调用;Java程序执行时会被调用System.exit()会被调用;它会在操作系统关闭时被调用;它会在linux通过killpid(或kill-15pid)结束进程时被调用。JDK中ShutdownHook相关的源码如下图2所示:图2:如何添加和删除要调用的ShutdownHook?使用java.lang.Runtime.addShutdownHook方法注册一个JVM关闭的钩子(线程),如下图3所示。我们希望程序在JVM退出时做各种收尾工作,比如:关闭资源,注销服务路由器上的注册信息,等待中转请求处理完成等,可以加入这个线程。图3:注册JVM关闭钩子的示例当然,优雅退出需要超时控制机制。如果超时后还没有完成退出前的资源回收等操作,关机脚本会直接调用KILL-9PID或者调用强制关机流程。强制退出的代码方式,否则可能会等待很长时间,影响我们正常的启停操作。其他注意事项1、以下场景会直接停止JVM进程,JVM没有机会在shutdownhook线程中进行收尾工作,无法实现优雅关机:kill-9(SIGKILL信号);调用java.lang。runtime.halt()方法;主机直接崩溃;主机直接关机;宿主机内存(或容器内存)不足,触发操作系统OOM-KILLER。2、hook线程会延迟JVM的关闭时间,所以尽量减少执行时间,做好超时控制。效果代码优化后,通过固定场景的测试环境和实际生产演练的验证,容器销毁或正常重启时,没有出现“Cannotgetconnectiontoserver”的错误,也解决了商业认知问题。解决这个问题后,在大规模微服务架构的场景下,容器的自动自愈、伸??缩等功能又可以用上了。总结微服务优雅关闭没有最优方案,只要抓住核心思想进行设计即可。如果使用的框架中有这样的解决方案,建议直接使用,适应性一定是最高的。在微服务架构中,我们可以遵循以下建议规则来设计微服务的优雅关闭机制:所有微服务应用都应该支持优雅关闭;应优先注销在注册表中注册的服务实例;服务应用程序的访问点标记将被关闭拒绝服务;上游服务支持对因正常关闭而被拒绝的服务进行故障转移;还根据具体业务提供了合适的关闭接口。