Nacos服务注册中心-NamingProxy和BeatReactor

317 阅读1分钟

NamingProxy是NacosNamingService与NacosRestTemplate之间的一层代理,用于封装请求参数和登录。请求服务端所有接口都必须拿到accessToken,所以首先要进行登录。登录操作内部又委托给SecurityProxy。

BeatReactor是节点与服务端保持连接的心跳检测,防止节点掉线。

创建NacosRestTemplate

// Http客户端
private final NacosRestTemplate nacosRestTemplate = NamingHttpClientManager.getInstance().getNacosRestTemplate();

登录

因为accessToken有时效性,所以必须要开启一个后台任务定期进行登录


private void initRefreshTask() {
    //创建线程池,2个核心线程
    this.executorService = new ScheduledThreadPoolExecutor(2, new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                    Thread t = new Thread(r);
                    t.setName("com.alibaba.nacos.client.naming.updater");
                    t.setDaemon(true);
                    return t;
            }
    });
    //默认5s登录一次
    this.executorService.scheduleWithFixedDelay(new Runnable() {
        @Override
        public void run() {
            securityProxy.login(getServerList());
        }
    }, 0, securityInfoRefreshIntervalMills, TimeUnit.MILLISECONDS);
        //初次登录
        this.securityProxy.login(getServerList());
}

//SecurityProxy
public boolean login(List<String> servers) {
    try {
        //判断间隔事件如果小于token过期时间(减10分之1网络时间)就退出
        if ((System.currentTimeMillis() - lastRefreshTime) < TimeUnit.SECONDS
                .toMillis(tokenTtl - tokenRefreshWindow)) {
            return true;
        }
        //所有的服务端都要登录一遍
        for (String server : servers) {
            if (login(server)) {
                lastRefreshTime = System.currentTimeMillis();
                return true;
            }
        }
    } catch (Throwable ignore) {	}
    return false;
}

其他的注册/更新实例,注册/更新服务就没啥好说的,就是封装请求,发送请求。

关联必选配置项

namespace:命名空间,一般用于区分环境,如dev,test,prod
serverAddr:nacos服务端地址,多个逗号分割
username:用户名
password:密码

BeatReactor

用于心跳检测,如果一个节点是一个临时节点(会重启或者ip会变化),默认注册的节点都是临时节点(ephemeral=true),那么就需要进行心跳连接和检测,来判断节点的可用性和健康情况。

//创建BeatReactor实例
public BeatReactor(NamingProxy serverProxy, int threadCount) {
    this.serverProxy = serverProxy;
    //创建线程池,默认核心线程数是Runtime.getRuntime().availableProcessors() / 2
    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) {
    //构建缓存key
    String key = buildKey(serviceName, beatInfo.getIp(), beatInfo.getPort());
    BeatInfo existBeat = null;
    //先删除旧数据
    if ((existBeat = dom2Beat.remove(key)) != null) {
        existBeat.setStopped(true);
    }
    dom2Beat.put(key, beatInfo);
    //开启一个定时任务,默认间隔5s,可通过在metadata中preserved.heart.beat.interval配置
    executorService.schedule(new BeatTask(beatInfo), beatInfo.getPeriod(), TimeUnit.MILLISECONDS);
    //监控采集
    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 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;
            }
            int code = NamingResponseCode.OK;
            if (result.has(CommonParams.CODE)) {
                code = result.get(CommonParams.CODE).asInt();
            }
            //如果服务未注册上去,重新注册
            if (code == NamingResponseCode.RESOURCE_NOT_FOUND) {
                Instance instance = new Instance();
                instance.setPort(beatInfo.getPort());
                instance.setIp(beatInfo.getIp());
                instance.setWeight(beatInfo.getWeight());
                instance.setMetadata(beatInfo.getMetadata());
                instance.setClusterName(beatInfo.getCluster());
                instance.setServiceName(beatInfo.getServiceName());
                instance.setInstanceId(instance.getInstanceId());
                instance.setEphemeral(true);
                try {
                    serverProxy.registerService(beatInfo.getServiceName(),
                            NamingUtils.getGroupName(beatInfo.getServiceName()), instance);
                } catch (Exception ignore) {
                }
            }
        } catch (NacosException ex) {
            NAMING_LOGGER.error("[CLIENT-BEAT] failed to send beat: {}, code: {}, msg: {}",
                    JacksonUtils.toJson(beatInfo), ex.getErrCode(), ex.getErrMsg());

        }
        //再指定间隔时间执行
        executorService.schedule(new BeatTask(beatInfo), nextTime, TimeUnit.MILLISECONDS);
    }
}