K8s平滑更新基于Nacos注册微服务

509 阅读3分钟

一、前言

最近公司的一个新业务线集成了K8s集成部署多个项目,项目使用Nacos作为注册中心,版本是1.4.x,服务间通过Feign调用通信,最近发现在服务在重新部署的期间,会存在应用间接口调用超时的情况,此处记录下问题的排查和解决。

二、排查

通过追踪Nacos源码及查阅相关资料初步排查原因有如下几点

  • NacosAutoServiceRegistration继承了spring cloud中的AbstractAutoServiceRegistration类重写了deregister方法进行服务下线,由于hook机制,spring容器停止的时候会触发该方法调用,但此处存在调用到nacos server端及server端把下线消息通知到订阅者的延迟

image.png

  • Nacos默认心跳超时15s后才会标记为不健康
  • 被调用服务其实已经在停止过程中不再接收请求,调用方本地还缓存着该服务的实例地址,NacosDiscoveryClient实现了spring cloud中的DiscoveryClient类,跟踪其getInstances方法发现去服务端拉取提供方列表的定时任务默认10s才会执行一次

image.png

image.png

image.png

三、解决

从上面的排查中,我们最容易想到的就是缩短服务下线感知的延时,比如缩短客户端拉取提供者服务的定时任务周期,把10s改成1,2秒,这样确实有些效果,但不能完全解决问题。用户量访问较频繁的接口很容易就出现问题,要想做到平滑更新,用户无感知,需要保证以下两点

  • 在该服务停机之前,已经从注册中心下线
  • 下线的信息已全部同步到各订阅者

此时很容易想到是否能在服务停机之前将其从服务端下线。从上面的源码中可以看到NacosAutoServiceRegistration这个类正好能为我们所用。只要在服务停止前调用一下,等一段时间后再停止服务即可,与运维沟通了解到k8s中存在postStart 和 preStop 处理函数 kubernetes.io/zh-cn/docs/… 可以在停止容器前做一些操作,另一方面项目里有使用到spirngboot actuator作jvm监控及健康检查,就顺便增加了一个自定义端点来处理服务下线,代码如下

@Slf4j
@Component
@Endpoint(id = "shutdownGraceFul")
public class ServiceShutDownEndpoint {

    @Resource
    private NacosAutoServiceRegistration serviceRegistration;

    @Resource
    private  ApplicationContext context;

    /** 下线服务后关闭应用前等待的时间(秒) */
    @Value("${stopService.waitTime:120}")
    private int waitTime;



    @WriteOperation
    public Map<String, Object> shutdownGraceFul() {
        log.info("开始服务下线");
        serviceRegistration.stop();
        log.info("完成服务下线");
        log.info("等待{}s, 关闭应用", waitTime);
        try {
            TimeUnit.SECONDS.sleep(waitTime);
        } catch (InterruptedException e) {
            log.info("interrupted!", e);
        }
        log.info("Closing application...");
        SpringApplication.exit(context);
        Map<String, Object> result = new HashMap<>();

        result.put("shutdownGraceFul", true);
        return result;
    }

}

k8s中yml配置如下

k8.png

等待的时间可根据实际情况调整,不能太短,得保证下掉应用后nacos服务端能把节点下线消息完全同步到所有调用方

四、后续

看了下nacos2.x版本以后使用grpc和Rsocket替代了之前大部分的http请求和基于udp协议的请求,极大的缩短了延迟,但试了下还是存在此问题,jrm可针对自己项目使用的版本进行测试。

最后

① 谢谢光临~
② 如果对于文中所持观点有不同的看法,欢迎大佬指点,交流~