Nacos源码学习系列服务端第3篇服务注册之客户端心跳检查

目录

心跳检查任务初始化

心跳检查任务类开胃菜(ClientBeatCheckTaskV2)

真正的心跳检查任务 InstanceBeatCheckTask 

 SPI机制加载拦截器链

拦截器链执行

 3种拦截器

 


心跳检查任务初始化

public class IpPortBasedClient extends AbstractClient {
    
   ....
    /**
     * Init client.
     */
    public void init() {
        if (ephemeral) {
            //这里开始创建心跳检查任务并加入定时任务执行
            beatCheckTask = new ClientBeatCheckTaskV2(this);
            HealthCheckReactor.scheduleCheck(beatCheckTask);
        } else {
            healthCheckTaskV2 = new HealthCheckTaskV2(this);
            HealthCheckReactor.scheduleCheck(healthCheckTaskV2);
        }
    }
    
    ....
}

心跳检查任务类开胃菜(ClientBeatCheckTaskV2)

public class ClientBeatCheckTaskV2 extends AbstractExecuteTask implements BeatCheckTask, NacosHealthCheckTask {
    
    // 客户端对象
    private final IpPortBasedClient client;
    //就是 client.getResponsibleId
    private final String taskId;
    //任务拦截器链
    private final InstanceBeatCheckTaskInterceptorChain interceptorChain;
    
    public ClientBeatCheckTaskV2(IpPortBasedClient client) {
        this.client = client;
        this.taskId = client.getResponsibleId();
        //稍后单独解读
        this.interceptorChain = InstanceBeatCheckTaskInterceptorChain.getInstance();
    }
    
    public GlobalConfig getGlobalConfig() {
        return ApplicationUtils.getBean(GlobalConfig.class);
    }
    
    
    @Override
    public String taskKey() {
        return KeyBuilder.buildServiceMetaKey(client.getClientId(), 
              String.valueOf(client.isEphemeral()));
    }
    
    ...
    
    //健康检查
    @Override
    public void doHealthCheck() {
        try {
            //轮询该客户端发布的所有服务列表
            Collection<Service> services = client.getAllPublishedService();
            for (Service each : services) {
                //获取服务下的实例信息
                HealthCheckInstancePublishInfo instance = 
                            (HealthCheckInstancePublishInfo) client
                                 .getInstancePublishInfo(each);
                //拦截器链处理完InstanceBeatCheckTask后执行
                // InstanceBeatCheckTask.passIntercept()方法
                interceptorChain.doInterceptor(new InstanceBeatCheckTask(client, each, 
                instance));
            }
        } catch (Exception e) {
            Loggers.SRV_LOG.warn("Exception while processing client beat time out.", e);
        }
    }
    
    //定时任务的执行入口
    @Override
    public void run() {
        doHealthCheck();
    }
    
    @Override
    public void passIntercept() {
        doHealthCheck();
    }
    
    @Override
    public void afterIntercept() {
    }
}

我们看到执行心跳检查任务是在ClientBeatCheckTaskV2 里面嵌入一个新的InstanceBeatCheckTask任务真正的心跳检查逻辑是在InstanceBeatCheckTask上的拦截器都执行完毕后再执行InstanceBeatCheckTask的passIntercept方法完成一次心跳检查

接下来我们先看InstanceBeatCheckTask再看看他上面的拦截器是什么怎么执行的。

真正的心跳检查任务 InstanceBeatCheckTask 


public class InstanceBeatCheckTask implements Interceptable {
    
    private static final List<InstanceBeatChecker> CHECKERS = new LinkedList<>();
    
    //客户端
    private final IpPortBasedClient client;
    //服务
    private final Service service;
    //发布的服务
    private final HealthCheckInstancePublishInfo instancePublishInfo;
    //
    // 心跳检查列表
    static {
        CHECKERS.add(new UnhealthyInstanceChecker());
        CHECKERS.add(new ExpiredInstanceChecker());
        //使用java spi 扩展机制用户可以再META-INF/services下创建
        //InstanceBeatChecker的实现类实现用户自定义扩展 
        CHECKERS.addAll(NacosServiceLoader.load(InstanceBeatChecker.class));
    }
    
    public InstanceBeatCheckTask(IpPortBasedClient client, Service service, HealthCheckInstancePublishInfo instancePublishInfo) {
        this.client = client;
        this.service = service;
        this.instancePublishInfo = instancePublishInfo;
    }
    
   //任务上的拦截器执行完毕后逐个拦截器执行
    @Override
    public void passIntercept() {
        for (InstanceBeatChecker each : CHECKERS) {
            each.doCheck(client, service, instancePublishInfo);
        }
    }
    
    @Override
    public void afterIntercept() {
    }
    
   ...
}

心跳检查实际上是一个checker 列表 逐个执行

除了前面2个检查器外,第三个是使用java spi 扩展机制用户可以再META-INF/services下创建
InstanceBeatChecker的实现类实现用户自定义扩展实例的检查机UnhealthyInstanceChecker :检查心跳超时时间


public class UnhealthyInstanceChecker implements InstanceBeatChecker {
    
    @Override
    public void doCheck(Client client, Service service, HealthCheckInstancePublishInfo 
                 instance) {
        //原来是健康现在是不健康的条件判断
        if (instance.isHealthy() && isUnhealthy(service, instance)) {
            changeHealthyStatus(client, service, instance);
        }
    }
    
    //距离上次心跳时间已经超过超时时间
    private boolean isUnhealthy(Service service, HealthCheckInstancePublishInfo 
           instance) {
            long beatTimeout = getTimeout(service, instance);
            return System.currentTimeMillis() - instance.getLastHeartBeatTime() > 
                    beatTimeout;
    }
    
    //从扩展数据中去超时时间(这块不细讲,知道下就行后面学习了InstanceMetadata后就很容易 )         
    //如果没有取到则设置默认值15s
    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);
    }
    
    ....
    //实例健康状态设置为false 并发布服务和客户端变更事件
    private void changeHealthyStatus(Client client, Service service, 
             HealthCheckInstancePublishInfo instance) {
            instance.setHealthy(false);
        NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service));
        NotifyCenter.publishEvent(new ClientEvent.ClientChangedEvent(client));
    }
}
public class ExpiredInstanceChecker implements InstanceBeatChecker {
    
    @Override
    public void doCheck(Client client, Service service, HealthCheckInstancePublishInfo 
         instance) {
           // 过期实例配置 默认是true
           boolean expireInstance = 
              ApplicationUtils.getBean(GlobalConfig.class).isExpireInstance();
          if (expireInstance && isExpireInstance(service, instance)) {
              deleteIp(client, service, instance);
         }
    }
    
    //优先从元数据获取deleteiptimeout 参数否则设置默认值30s
    //超过deleteiptimeout 时间则代表过期实例
    private boolean isExpireInstance(Service service, HealthCheckInstancePublishInfo 
     instance) {
        long deleteTimeout = getTimeout(service, instance);
        return System.currentTimeMillis() - instance.getLastHeartBeatTime() > 
           deleteTimeout;
    }
    
    ...
    
    private void deleteIp(Client client, Service service, InstancePublishInfo instance) {
        //从客户端的服务列表移除该服务
        client.removeServiceInstance(service);
        NotifyCenter.publishEvent(new 
              ClientOperationEvent.ClientDeregisterServiceEvent(service, 
                client.getClientId()));
        NotifyCenter.publishEvent(new MetadataEvent.InstanceMetadataEvent(service, 
                instance.getMetadataId(), true));
    }
}

使用spi机制的除了上面的心跳检查器,接下来要解读的拦截器链也同样采用了spi机制

上面再讲到 ClientBeatCheckTaskV2 构造器里面的初始化语句

this.interceptorChain = InstanceBeatCheckTaskInterceptorChain.getInstance();

我们进入代码看一下是怎么实例化的

 SPI机制加载拦截器链

public class InstanceBeatCheckTaskInterceptorChain extends AbstractNamingInterceptorChain<InstanceBeatCheckTask> {
    
    private static final InstanceBeatCheckTaskInterceptorChain INSTANCE = new InstanceBeatCheckTaskInterceptorChain();
    
    private InstanceBeatCheckTaskInterceptorChain() {
        super(AbstractBeatCheckInterceptor.class);
    }
    
    public static InstanceBeatCheckTaskInterceptorChain getInstance() {
        return INSTANCE;
    }
}


public abstract class AbstractNamingInterceptorChain<T extends Interceptable>
        implements NacosNamingInterceptorChain<T> {
    
    private final List<NacosNamingInterceptor<T>> interceptors;
    
    protected AbstractNamingInterceptorChain(Class<? extends NacosNamingInterceptor<T>> 
          clazz) {
        this.interceptors = new LinkedList<>();
        //使用了spi机制加载用户自定义的拦截器
        interceptors.addAll(NacosServiceLoader.load(clazz));
        interceptors.sort(Comparator.comparingInt(NacosNamingInterceptor::order));
    }
}

 最终使用 interceptors.addAll(NacosServiceLoader.load(clazz));加载用户在

/META-INF/services/com.alibaba.nacos.naming.healthcheck.heartbeat.AbstractBeatCheckInterceptor

com.alibaba.nacos.naming.healthcheck.heartbeat.ServiceEnableBeatCheckInterceptor
com.alibaba.nacos.naming.healthcheck.heartbeat.InstanceEnableBeatCheckInterceptor
com.alibaba.nacos.naming.healthcheck.heartbeat.InstanceBeatCheckResponsibleInterceptor

拦截器链执行

public abstract class AbstractNamingInterceptorChain<T extends Interceptable>
        implements NacosNamingInterceptorChain<T> {
    
    private final List<NacosNamingInterceptor<T>> interceptors;
    
    protected AbstractNamingInterceptorChain(Class<? extends NacosNamingInterceptor<T>> clazz) {
        this.interceptors = new LinkedList<>();
        interceptors.addAll(NacosServiceLoader.load(clazz));
        interceptors.sort(Comparator.comparingInt(NacosNamingInterceptor::order));
    }
    

    protected List<NacosNamingInterceptor<T>> getInterceptors() {
        return interceptors;
    }
    
    @Override
    public void addInterceptor(NacosNamingInterceptor<T> interceptor) {
        interceptors.add(interceptor);
        interceptors.sort(Comparator.comparingInt(NacosNamingInterceptor::order));
    }
    
    @Override
    public void doInterceptor(T object) {
        for (NacosNamingInterceptor<T> each : interceptors) {
            //校验拦截器是否支持拦截该对象
            if (!each.isInterceptType(object.getClass())) {
                continue;
            }
            //成功拦截返回并执行对象的afterIntercept()
            if (each.intercept(object)) {
                object.afterIntercept();
                return;
            }
        }
        //通过了拦截
        object.passIntercept();
    }
}

 3种拦截器

 

public class ServiceEnableBeatCheckInterceptor extends AbstractBeatCheckInterceptor {
    
    //判断对该服务是否启动了客户端心跳
    @Override
    public boolean intercept(InstanceBeatCheckTask object) {
        NamingMetadataManager metadataManager = 
               ApplicationUtils.getBean(NamingMetadataManager.class);
        Optional<ServiceMetadata> metadata = 
             metadataManager.getServiceMetadata(object.getService());
        if (metadata.isPresent() && 
             metadata.get().getExtendData().containsKey(
               UtilsAndCommons.ENABLE_CLIENT_BEAT)) {
            return Boolean.parseBoolean(metadata.get().getExtendData().、 
               get(UtilsAndCommons.ENABLE_CLIENT_BEAT));
        }
        return false;
    }
    
    @Override
    public int order() {
        return Integer.MIN_VALUE;
    }
}

public class InstanceEnableBeatCheckInterceptor extends AbstractBeatCheckInterceptor {
    
    //检查实例的元数据是否启用了心跳检查  
    @Override
    public boolean intercept(InstanceBeatCheckTask object) {
        NamingMetadataManager metadataManager = 
            ApplicationUtils.getBean(NamingMetadataManager.class);
        HealthCheckInstancePublishInfo instance = object.getInstancePublishInfo();
        Optional<InstanceMetadata> metadata = 
           metadataManager.getInstanceMetadata(object.getService(), 
           instance.getMetadataId());
          if (metadata.isPresent() && 
             metadata.get().getExtendData().containsKey(、 
             UtilsAndCommons.ENABLE_CLIENT_BEAT)) {
             return ConvertUtils.toBoolean(metadata.get().getExtendData().、 
                   get(UtilsAndCommons.ENABLE_CLIENT_BEAT).toString());
        }
        if (instance.getExtendDatum().containsKey(
             UtilsAndCommons.ENABLE_CLIENT_BEAT)) {
            return ConvertUtils.toBoolean(instance.getExtendDatum().
                   get(UtilsAndCommons.ENABLE_CLIENT_BEAT).toString());
        }
        return false;
    }
    
    @Override
    public int order() {
        return Integer.MIN_VALUE + 1;
    }
}

public class InstanceBeatCheckResponsibleInterceptor extends AbstractBeatCheckInterceptor {
    
    //心跳检查任务是否由该服务器负责
    @Override
    public boolean intercept(InstanceBeatCheckTask object) {
        return !ApplicationUtils.getBean(DistroMapper.class).
               responsible(object.getClient().getResponsibleId());
    }

    @Override
    public int order() {
        //应该第一优先级运行
        return Integer.MIN_VALUE + 2;
    }

}

微信扫一扫加入讨论组备注nacos

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值