图解+源码讲解 Nacos 客户端发起心跳请求

从哪里开始分析

其实想想就知道,指定是在客户端发起注册的时候就会在本地有一个后台的线程在进行维护心跳发送请求,那么就是在注册时候指定有一个心跳任务的创建

@Override
public void registerInstance(String serviceName, String groupName, Instance instance) 
    throws NacosException {
    NamingUtils.checkInstanceIsLegal(instance);
    String groupedServiceName = NamingUtils.getGroupedName(serviceName, groupName);
    if (instance.isEphemeral()) {
        // 构建心跳信息
        BeatInfo beatInfo = beatReactor.buildBeatInfo(groupedServiceName, instance);
        // 心跳执行器
        beatReactor.addBeatInfo(groupedServiceName, beatInfo);
    }
    serverProxy.registerService(groupedServiceName, groupName, instance);
}

这么看就是在进行实例注册的时候,构建的心跳信息,之后放入了心跳执行器的里面进行任务执行

构建心跳信息

创建心跳信息,里面包含了服务名称、IP地址、端口号、集群名称、权重、元数据信息、心跳发送频率,这里面的心跳发送频率默认是5秒

public BeatInfo buildBeatInfo(String groupedServiceName, Instance instance) {
    // 创建心跳信息
    BeatInfo beatInfo = new BeatInfo();
    // 设置服务名称
    beatInfo.setServiceName(groupedServiceName);
    // 设置IP地址
    beatInfo.setIp(instance.getIp());
    // 设置端口号
    beatInfo.setPort(instance.getPort());
    // 设置集群名称
    beatInfo.setCluster(instance.getClusterName());
    // 设置权重
    beatInfo.setWeight(instance.getWeight());
    // 设置元数据
    beatInfo.setMetadata(instance.getMetadata());
    beatInfo.setScheduled(false);
    // 设置频率
    beatInfo.setPeriod(instance.getInstanceHeartBeatInterval());
    return beatInfo;
}

构建心跳执行器

创建了一个定时调度线程池执行器,里面创建的是一个后台的守护线程、并且名字是:

com.alibaba.nacos.naming.beat.sender

public BeatReactor(NamingProxy serverProxy, int threadCount) {
    this.serverProxy = serverProxy;
    this.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;
        }
    });
}

向心跳执行器中放入心跳信息

public void addBeatInfo(String serviceName, BeatInfo beatInfo) {
    NAMING_LOGGER.info("[BEAT] adding beat: {} to beat map.", beatInfo);
    // 构建key
    String key = buildKey(serviceName, beatInfo.getIp(), beatInfo.getPort());
    BeatInfo existBeat = null;
    //fix #1733
    if ((existBeat = dom2Beat.remove(key)) != null) {
        existBeat.setStopped(true);
    }
    // 将心跳信息放入 dom2Beat 中
    dom2Beat.put(key, beatInfo);
    // 进行心跳任务创建之后放入调度执行器中执行,默认是5秒
    executorService.schedule(new BeatTask(beatInfo), beatInfo.getPeriod(), 
                             TimeUnit.MILLISECONDS);
    // 指标信息监控
    MetricsMonitor.getDom2BeatSizeMonitor().set(dom2Beat.size());
}

1. 心跳key的构建

心跳key = 服务名称+#+ip地址+#+端口号

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

2. 将key和心跳信息放入到dom2Beat Map 中

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

3. 创建心跳任务并执行

class BeatTask implements Runnable {

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

    @Override
    public void run() {
        // 心跳是否停止如果是的话就返回
        if (beatInfo.isStopped()) {
            return;
        }
        // 获取心跳频率
        long nextTime = beatInfo.getPeriod();
        try {
            // 发送心跳请求并返回
            JsonNode result = serverProxy.sendBeat(beatInfo,
                    BeatReactor.this.lightBeatEnabled);
            // 从返回的结果里面获取心跳频率
            long interval = result.get("clientBeatInterval").asLong();
            boolean lightBeatEnabled = false;
            if (result.has(CommonParams.LIGHT_BEAT_ENABLED)) {
                lightBeatEnabled = result.get(CommonParams.LIGHT_BEAT_ENABLED)
                    .asBoolean();
            }
            BeatReactor.this.lightBeatEnabled = lightBeatEnabled;
            if (interval > 0) {
                nextTime = interval;
            }
            // code = 10200
            int code = NamingResponseCode.OK;
            // 返回的结果信息里面是否有 code 如果有的话则获取
            if (result.has(CommonParams.CODE)) {
                code = result.get(CommonParams.CODE).asInt();
            }
            // 如果没有找到请求资源 20404那么就创建一个实例进行注册
            if (code == NamingResponseCode.RESOURCE_NOT_FOUND) {
                // 创建一个实例
                Instance instance = new Instance();
                // 设置端口号
                instance.setPort(beatInfo.getPort());
                // 设置IP
                instance.setIp(beatInfo.getIp());
                // 设置权重
                instance.setWeight(beatInfo.getWeight());
                // 设置元数据信息
                instance.setMetadata(beatInfo.getMetadata());
                // 设置集群名称
                instance.setClusterName(beatInfo.getCluster());
                // 设置服务名称
                instance.setServiceName(beatInfo.getServiceName());
                // 设置实例ID
                instance.setInstanceId(instance.getInstanceId());
                // 设置是虚拟节点
                instance.setEphemeral(true);
                try {
                    // 发起服务注册
                    serverProxy.registerService(beatInfo.getServiceName(),
                       NamingUtils.getGroupName(beatInfo.getServiceName()), instance);
                } catch (Exception ignore) {
                }
            }
        } 
        // 心跳任务执行
        executorService.schedule(new BeatTask(beatInfo), nextTime, 
                                 TimeUnit.MILLISECONDS);
    }
}

心跳请求发起

serverProxy.sendBeat(beatInfo,
BeatReactor.this.lightBeatEnabled)
 这句代码进行了心跳发送请求,请求的路径是:
http://localhost:8848/nacos/v1/ns/instance/beat

public JsonNode sendBeat(BeatInfo beatInfo, boolean lightBeatEnabled) 
    throws NacosException {

    if (NAMING_LOGGER.isDebugEnabled()) {
        NAMING_LOGGER.debug("[BEAT] {} sending beat to server: {}",
                            namespaceId, beatInfo.toString());
    }
    // 构建参数进行请求
    Map<String, String> params = new HashMap<String, String>(8);
    Map<String, String> bodyMap = new HashMap<String, String>(2);
    if (!lightBeatEnabled) {
        bodyMap.put("beat", JacksonUtils.toJson(beatInfo));
    }
    //放入一些参数,比如名称空间等
    params.put(CommonParams.NAMESPACE_ID, namespaceId);
    params.put(CommonParams.SERVICE_NAME, beatInfo.getServiceName());
    params.put(CommonParams.CLUSTER_NAME, beatInfo.getCluster());
    params.put("ip", beatInfo.getIp());
    params.put("port", String.valueOf(beatInfo.getPort()));
    // 发起请求
    // 请求路径是 http://localhost:8848/nacos/v1/ns/instance/beat
    String result = reqApi(UtilAndComs.nacosUrlBase + "/instance/beat",
                    params, bodyMap, HttpMethod.PUT);
    return JacksonUtils.toObj(result);
}

小结

其实就是在客户端实例进行注册的时候,创建了心跳信息 BeatInfo ,之后将这个心跳信息放入了一个dom2Beat Map中,之后又创建了一个心跳任务,并将其放入了定时调度器进行周期执行心跳请求的访问


作者:沙漠中的曼陀罗
链接:
https://juejin.cn/post/7091058942125539364

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值