题目描述
在生产环境中利用 springcloud 框架,由于服务更新过程中,容器服务会被直接停止,部分请求仍被分发到停止的容器,导致服务出现500错误,这部分错误请求数据占用比力少,因为Pod滚动更新都是一对一。因为部分用户会产生服务器错误的环境,考虑利用优雅的停止方式,将错误请求降到最低,直至滚动更新不影响用户。这里结合nacos利用来分析。
在 K8s 的滚动升级中,好比 5 个 Pod 服务在升级过程中,会先启动一半左右(好比:3 个新的启动),然后下线一部分服务……直到所有的旧服务被新服务完全替代,简单粗暴的明确滚动升级。如果我们不涉及 Nacos 还好,因为 K8s 会包管在升级过程中,因为负载的环境很有可能在升级过程中会一部分请求打到旧服务里,但是如果在旧服务准备关闭服务时,旧情求还没返回回去的话就会出现 HTTP 连接关闭环境等一些不可预测的意外发生,导致本次请求的业务失败,这是在生产上绝不能出现的事故。针对 K8s 的优雅关闭题目,我们可以继续往下看,下面会介绍 Nacos & K8s 一个结合优雅关闭的方案。
我们来再谈谈 Nacos 在这里如果无优雅关闭的话会出现的环境,实在和 K8s 的本质很类似,如果我们已经解决了 K8s 的优雅关闭题目,那和 Nacos 之间又有什么接洽呢?
我们可以想象下,照旧举例上面的 5 个 Pod 的情形,在一个 Pod 启动时,服务的也天然会注册到 Nacos 上去,同理可得,在服务关闭时,Nacos 注册列表里服务也天然会被下线。那么类似的环境也会出现,如果说此时的情求打到旧服务上面,但是由于 Nacos 有监听时间(默认 30s)拉取一次最新环境,以及在每个 Pod 服务里当地也有一份缓存映射表(也有一个窗口时间更新),所以很有可能在这个窗口期之内,还有一些的旧的请求访问负载到旧服务里,但是这里会出现两种环境
K8s Pod 服务已下线,但是 Nacos 在窗口时间之内注册列表未更新,导致请求达到一个根本不存在的旧服务里 旧请求已经打到旧服务里,但是高峰期时,程序处置惩罚较慢,还没来及返反响应体,服务就被关闭了 以上这两种环境都会导致本次请求出现失败,生产上更是无语~ 所以我们针对 Nacos 的优雅关闭环境也会有一个解决方案,见“Nacos & K8s 优雅关闭方案”
一、思路
为相识决这个题目, 我们将利用 springboot 的 graceful shutdown 功能和 nacos 的自动下线功能来解决这个题目. 详细思路如下:
1、让新的服务先启动起来,注册到注册中央,等待客户端发现新服务。
2、把待下线的服务从注册中央下线,等待客户端革新服务列表。
3、把待下线的服务优雅停机。
二、实现方式
1.创建从 nacos 中下线副本的API
我们通过创建自定义名为 deregister 的 endpoint 来通知 nacos 下线副本
- import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
- import com.alibaba.cloud.nacos.registry.NacosRegistration;
- import com.alibaba.cloud.nacos.registry.NacosServiceRegistry;
- import lombok.extern.log4j.Log4j2;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
- import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
- import org.springframework.stereotype.Component;
- @Component
- @Endpoint(id = "deregister")
- @Log4j2
- public class LemesNacosServiceDeregisterEndpoint {
- @Autowired
- private NacosDiscoveryProperties nacosDiscoveryProperties;
- @Autowired
- private NacosRegistration nacosRegistration;
- @Autowired
- private NacosServiceRegistry nacosServiceRegistry;
- /**
- * 从 nacos 中主动下线,用于 k8s 滚动更新时,提前下线分流流量
- *
- * @param
- * @return com.lenovo.lemes.framework.core.util.ResultData<java.lang.String>
- * @author Yujie Yang
- * @date 4/6/22 2:57 PM
- */
- @ReadOperation
- public String endpoint() {
- String serviceName = nacosDiscoveryProperties.getService();
- String groupName = nacosDiscoveryProperties.getGroup();
- String clusterName = nacosDiscoveryProperties.getClusterName();
- String ip = nacosDiscoveryProperties.getIp();
- int port = nacosDiscoveryProperties.getPort();
- log.info("deregister from nacos, serviceName:{}, groupName:{}, clusterName:{}, ip:{}, port:{}", serviceName, groupName, clusterName, ip, port);
- // 设置服务下线
- nacosServiceRegistry.setStatus(nacosRegistration, "DOWN");
- return "success";
- }
- }
复制代码 2.支持 Graceful Shutdown
Spring Boot 原生支持已经支持优雅停机。
我们需要添加下面的设置,超时需要根据平常请求的耗时来定,可以轻微大一点也没关系。我们只需要在 bootstrap.yaml 中添加一下设置即可。
- server:
- # 开启优雅下线
- shutdown: graceful
- spring:
- lifecycle:
- # 优雅下线超时时间
- timeout-per-shutdown-phase: 5m
- # 暴露 shutdown 接口
- management:
- endpoint:
- shutdown:
- enabled: true
- endpoints:
- web:
- exposure:
- include: shutdown
复制代码 3.设置K8s
有了上面两个 API, 接下来就设置到 k8s 上
1.K8S容器本身有一个停当探针设置,当停当探针返回正常,则开始删除旧POD。
2.terminationGracePeriodSeconds 如果关闭应用的时间凌驾 10 分钟, 则向容器发送 kill 信号, 防止应用长时间下线不了
terminationGracePeriodSeconds要大于preStop+spring.lifecycle.timeout-per-shutdown-phase,可以设置大一点没什么关系。
3.preStop 先执行下线操作, 等待30s, 留够通知到其他应用的时间, 然后执行 graceful shutdown 关闭应用。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |