nacos解析-详解服务端保存服务数据以及健康检查原理

本文基于nacos-2.0.3版本

注册服务时,nacos服务端调用InstanceController.register()方法完成注册。
InstanceController.register()再调用InstanceOperatorClientImpl.registerInstance()注册服务,下面是registerInstance()方法代码:

    public void registerInstance(String namespaceId, String serviceName, Instance instance) {
    	//注册服务是否是临时服务
        boolean ephemeral = instance.isEphemeral();
        //将注册服务的IP地址与ephemeral组合起来
        String clientId = IpPortBasedClient.getClientId(instance.toInetAddr(), ephemeral);
        createIpPortClientIfAbsent(clientId);//创建定时任务,检测服务是否正常
        Service service = getService(namespaceId, serviceName, ephemeral);
        //向nacos服务端写入服务数据
        clientOperationService.registerInstance(service, instance, clientId);
    }

下面分别介绍nacos如何检测服务以及如何写入服务数据。

1、写入服务数据

根据服务是否临时服务,nacos提供了两种注册服务数据的方法:PersistentClientOperationServiceImpl和EphemeralClientOperationServiceImpl。
PersistentClientOperationServiceImpl用于注册非临时服务,使用JRaft协议,将数据写入raft集群,其原理和之前文章介绍的raft算法日志复制原理相同,本文不再介绍。
下面分析EphemeralClientOperationServiceImpl的原理。
注册临时服务调用EphemeralClientOperationServiceImpl的registerInstance()方法:

    public void registerInstance(Service service, Instance instance, String clientId) {
    	//Service记录注册服务的group,服务名,是否临时等
    	//getSingleton()方法将服务数据写入一个Map中,通过nacos提供的运维页面查询服务列表时,便是从该Map获取的数据
        Service singleton = ServiceManager.getInstance().getSingleton(service);
        //Client记录服务的地址信息
        Client client = clientManager.getClient(clientId);
        //检查服务是否是临时服务,如果是,则本方法直接返回
         if (!clientIsLegal(client, clientId)) {
            return;
        }
        InstancePublishInfo instanceInfo = getPublishInfo(instance);
        //发布ClientChangedEvent事件
        client.addServiceInstance(singleton, instanceInfo);
        client.setLastUpdatedTime();
        //发布ClientRegisterServiceEvent事件
        NotifyCenter.publishEvent(new ClientOperationEvent.ClientRegisterServiceEvent(singleton, clientId));
        //发布InstanceMetadataEvent事件
        NotifyCenter
                .publishEvent(new MetadataEvent.InstanceMetadataEvent(singleton, instanceInfo.getMetadataId(), false));
    }

registerInstance()方法的最后,发布了三个事件,对应的监听器的作用都是将注册服务写入内存的Map中,供查询使用。

2、服务健康检测

注册服务时,nacos启动定时任务HealthCheckTaskInterceptWrapper来检测服务是否健康,每5s执行一次检测。

	//task的实现类是ClientBeatCheckTaskV2
	//ClientBeatCheckTaskV2记录了注册服务的地址、是否临时服务等
    public HealthCheckTaskInterceptWrapper(NacosHealthCheckTask task) {
        this.task = task;
        this.interceptorChain = HealthCheckInterceptorChain.getInstance();
    }
    //定时任务执行run方法
    @Override
    public void run() {
        try {
            interceptorChain.doInterceptor(task);//默认有两个拦截器
        } catch (Exception e) {
            Loggers.SRV_LOG.info("Interceptor health check task {} failed", task.getTaskId(), e);
        }
    }

定时任务对需要注册的服务执行两个拦截器:HealthCheckResponsibleInterceptor和HealthCheckEnableInterceptor。这两个拦截器用于检查一些开关是否打开。
在开关都打开的情况下,接着调用task.passIntercept()方法,也就是ClientBeatCheckTaskV2.passIntercept()方法。下面看一下该方法的源码。

    public void passIntercept() {
        doHealthCheck();
    }
    public void doHealthCheck() {
        try {
        	//查询出所有的注册服务
            Collection<Service> services = client.getAllPublishedService();
            for (Service each : services) {
            	//HealthCheckInstancePublishInfo记录当前服务是否健康
                HealthCheckInstancePublishInfo instance = (HealthCheckInstancePublishInfo) client
                        .getInstancePublishInfo(each);
                //调用拦截器
                interceptorChain.doInterceptor(new InstanceBeatCheckTask(client, each, instance));
            }
        } catch (Exception e) {
            Loggers.SRV_LOG.warn("Exception while processing client beat time out.", e);
        }
    }

doHealthCheck()方法里面最后也是调用了拦截器,nacos提供了三个拦截器:ServiceEnableBeatCheckInterceptor、InstanceEnableBeatCheckInterceptor和InstanceBeatCheckResponsibleInterceptor。这些拦截器主要检查服务的元数据。拦截器检查通过后,调用InstanceBeatCheckTask.passIntercept()方法:

    public void passIntercept() {
        for (InstanceBeatChecker each : CHECKERS) {
            each.doCheck(client, service, instancePublishInfo);
        }
    }

上面代码里面调用了两个检查器:UnhealthyInstanceChecker和ExpiredInstanceChecker。
UnhealthyInstanceChecker
UnhealthyInstanceChecker检查是否在规定的时间内收到心跳,如果心跳超时,则直接将服务设置为不健康。

    public void doCheck(Client client, Service service, HealthCheckInstancePublishInfo instance) {
    	//isUnhealthy()用于检查心跳是否超时
        if (instance.isHealthy() && isUnhealthy(service, instance)) {
            changeHealthyStatus(client, service, instance);//如果超时,直接设置服务不健康
        }
    }
    
    private boolean isUnhealthy(Service service, HealthCheckInstancePublishInfo instance) {
    	//获取心跳超时时间,默认是15000ms
        long beatTimeout = getTimeout(service, instance);
        return System.currentTimeMillis() - instance.getLastHeartBeatTime() > beatTimeout;
    }
    
    private long getTimeout(Service service, InstancePublishInfo instance) {
        Optional<Object> timeout = getTimeoutFromMetadata(service, instance);
        if (!timeout.isPresent()) {
            timeout = Optional.ofNullable(instance.getExtendDatum().get(PreservedMetadataKeys.HEART_BEAT_TIMEOUT));
        }
        return timeout.map(ConvertUtils::toLong).orElse(Constants.DEFAULT_HEART_BEAT_TIMEOUT);
    }
    private void changeHealthyStatus(Client client, Service service, HealthCheckInstancePublishInfo instance) {
        instance.setHealthy(false);
        //代码删减
        NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service));
        NotifyCenter.publishEvent(new ClientEvent.ClientChangedEvent(client));
    }

ExpiredInstanceChecker
ExpiredInstanceChecker用于检查服务是否已经下线,如果下线,则将服务从服务列表中删除。
经过上面两个检测器都正常的服务,可以继续对外提供服务。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值