Nacos源码学习系列第7篇客户端心跳任务管理类BeatReactor

338 阅读2分钟

 我们前面几篇文章已经介绍了心跳信息类BeatInfo  以及 何时创建心跳任务及心跳任务的执行时间等,有了前面的知识基础 理解BeatReactor起来会简单很多

目录

初始化方法

添加心跳任务

移除心跳任务

执行心跳任务

关于MetricsMonitor

总结


初始化方法

 public BeatReactor(NamingProxy serverProxy) {
        this(serverProxy, UtilAndComs.DEFAULT_CLIENT_BEAT_THREAD_COUNT);
    }

    public BeatReactor(NamingProxy serverProxy, int threadCount) {
        this.serverProxy = serverProxy;

        executorService = new ScheduledThreadPoolExecutor(threadCount, new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r);
                thread.setDaemon(true);
                thread.setName("com.alibaba.nacos.naming.beat.sender");
                return thread;
            }
        });
    }

创建了一个定时执行心跳任务的线程池,同时初始化了NamingProxy 对象用于后面的发送请求。

线程池核心线程数大小默认是cpu核心数 / 2

添加心跳任务

    public void addBeatInfo(String serviceName, BeatInfo beatInfo) {
        NAMING_LOGGER.info("[BEAT] adding beat: {} to beat map.", beatInfo);
        String key = buildKey(serviceName, beatInfo.getIp(), beatInfo.getPort());
        BeatInfo existBeat = null;
        //移除老的 添加新的
        if ((existBeat = dom2Beat.remove(key)) != null) {
            existBeat.setStopped(true);
        }
        dom2Beat.put(key, beatInfo);
        executorService.schedule(new BeatTask(beatInfo), beatInfo.getPeriod(), 
           TimeUnit.MILLISECONDS);
        MetricsMonitor.getDom2BeatSizeMonitor().set(dom2Beat.size());
    }


      private String buildKey(String serviceName, String ip, int port) {
           return serviceName + Constants.NAMING_INSTANCE_ID_SPLITTER
                + ip + Constants.NAMING_INSTANCE_ID_SPLITTER + port;
    }

代码容易理解删除历史旧的心跳任务(以 服务名和ip+端口 为key),添加新的心跳任务到定时器

移除心跳任务

public void removeBeatInfo(String serviceName, String ip, int port) {
        NAMING_LOGGER.info("[BEAT] removing beat: {}:{}:{} from beat map.", serviceName, 
          ip, port);
        BeatInfo beatInfo = dom2Beat.remove(buildKey(serviceName, ip, port));
        if (beatInfo == null) {
            return;
        }
        beatInfo.setStopped(true);
        MetricsMonitor.getDom2BeatSizeMonitor().set(dom2Beat.size());
    }

执行心跳任务

class BeatTask implements Runnable {

        BeatInfo beatInfo;

        public BeatTask(BeatInfo beatInfo) {
            this.beatInfo = beatInfo;
        }

        @Override
        public void run() {
            if (beatInfo.isStopped()) {
                return;
            }
            long result = serverProxy.sendBeat(beatInfo);
            long nextTime = result > 0 ? result : beatInfo.getPeriod();
            executorService.schedule(new BeatTask(beatInfo), nextTime, 
                 TimeUnit.MILLISECONDS);
        }
    }

这里首先会判断是不是旧的心跳任务,是的话就舍弃掉。心跳发送成功后如果服务端返回【clientBeatInterval】字段有值,则以服务端返回的心跳间隔时间为准,否则把beatInfo里面的心跳间隔时间作为参数 再次放入定时器。

心跳时间默认是5秒钟一次

关于MetricsMonitor

目前为止我们看到很多地方的代码有使用到类 MetricsMonitor ,从名字可以看出来是一个指标收集的统一处理类,读者可以了解下 Prometheus 的指标收集。

public class MetricsMonitor {
    private static Gauge nacosMonitor = Gauge.build()
        .name("nacos_monitor").labelNames("module", "name")
        .help("nacos_monitor").register();

    private static Histogram nacosClientRequestHistogram = 
         Histogram.build().labelNames("module", "method", "url", "code")
        .name("nacos_client_request").help("nacos_client_request")
        .register();


    public static Gauge.Child getServiceInfoMapSizeMonitor() {
        return nacosMonitor.labels("naming", "serviceInfoMapSize");
    }

    public static Gauge.Child getDom2BeatSizeMonitor() {
        return nacosMonitor.labels("naming", "dom2BeatSize");
    }

    public static Gauge.Child getListenConfigCountMonitor() {
        return nacosMonitor.labels("naming", "listenConfigCount");
    }

    public static Histogram.Timer getConfigRequestMonitor(String method, String url, 
       String code) {
        return nacosClientRequestHistogram.labels("config", method, url, 
           code).startTimer();
    }

    public static Histogram.Child getNamingRequestMonitor(String method, String url, 
       String code) {
        return nacosClientRequestHistogram.labels("naming", method, url, code);
    }
}

总结

   BeatReactor 类使用了 一个Map,保存每次新添加的心跳任务,同时删除老的心跳任务并设置老的心跳任务的stopped状态, 这样保证每个服务的示例只有一个心跳任务执行。

    public final Map<String, BeatInfo> dom2Beat = new ConcurrentHashMap<String, BeatInfo>();