Nacos服务注册中心之NacosWatch

1,848 阅读3分钟

NacosWatch用于定时与Nacos服务端数据同步当前服务的metadata数据并定时发布心跳事件。

定时事件发布

默认的taskScheduler只是定时发一个HeartbeatEvent事件,并没有定时拉取数据操作,这个定时任务只是配合服务端主动推送更新后,定时通知其他组件进行相应更新,比如网关收到这个事件后会重新初始化Route。

//创建NacosWatch实例
public NacosWatch(NacosServiceManager nacosServiceManager,
			NacosDiscoveryProperties properties,
			ObjectProvider<TaskScheduler> taskScheduler) {
        this.nacosServiceManager = nacosServiceManager;
        this.properties = properties;
        //如果taskScheduler没有可用的就创建默认的
        this.taskScheduler = taskScheduler.getIfAvailable(NacosWatch::getTaskScheduler);
}

//创建默认的taskScheduler
private static ThreadPoolTaskScheduler getTaskScheduler() {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setBeanName("Nacos-Watch-Task-Scheduler");
        taskScheduler.initialize();
        return taskScheduler;
}

//发布HeartbeatEvent事件
public void nacosServicesWatch() {
    this.publisher.publishEvent(new HeartbeatEvent(this, nacosWatchIndex.getAndIncrement()));
}

@Override
public void start() {
        //定期发布事件,默认30s
        this.watchFuture = this.taskScheduler.scheduleWithFixedDelay(this::nacosServicesWatch, this.properties.getWatchDelay());

}

@Override
public void stop() {
    if (this.watchFuture != null) {
        //停止线程池
        ((ThreadPoolTaskScheduler) this.taskScheduler).shutdown();
        //停止线程任务
        this.watchFuture.cancel(true);
    }
}

注册监听器

这里注册监听器主要是为了更新matadata

@Override
public void start() {
        //创建监听器,一般一个服务只会创建1个,但如果在线修改了service和group,就可能多个了,那之前那个难道不应该被替换?想不通为啥要用listenerMap存。
        EventListener eventListener = listenerMap.computeIfAbsent(buildKey(),
                event -> new EventListener() {
                        @Override
                        public void onEvent(Event event) {
                                //只处理服务变更事件
                                if (event instanceof NamingEvent) {
                                        List<Instance> instances = ((NamingEvent) event)
                                                        .getInstances();
                                        Optional<Instance> instanceOptional = selectCurrentInstance(
                                                        instances);
                                        instanceOptional.ifPresent(currentInstance -> {
                                                resetIfNeeded(currentInstance);
                                        });
                                }
                        }
                });
        //获取服务中心Service
        NamingService namingService = nacosServiceManager.getNamingService(properties.getNacosProperties());                         
        //订阅EventListener 
        namingService.subscribe(properties.getService(),properties.getGroup(),
                            Arrays.asList(properties.getClusterName()), eventListener);
}

//key
private String buildKey() {
        return String.join(":", properties.getService(), properties.getGroup());
}

//获取当前节点实例
private Optional<Instance> selectCurrentInstance(List<Instance> instances) {
    return instances.stream()
                    .filter(instance -> properties.getIp().equals(instance.getIp())
                                    && properties.getPort() == instance.getPort())
                    .findFirst();
}

//更新Metadata
private void resetIfNeeded(Instance instance) {
    if (!properties.getMetadata().equals(instance.getMetadata())) {
            properties.setMetadata(instance.getMetadata());
    }
}

触发监听器

监听器最终会被注册到EventDispatcher中的observerMap中,先来看EventDispatcher中这部分代码

public EventDispatcher() {
    //创建单线程池
    this.executor = Executors.newSingleThreadExecutor(new ThreadFactory() {
        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r, "com.alibaba.nacos.naming.client.listener");
            thread.setDaemon(true);
            return thread;
        }
    });
    //执行Notifier线程
    this.executor.execute(new Notifier());
}

//注册Listener
public void addListener(ServiceInfo serviceInfo, String clusters, EventListener listener) {
    List<EventListener> observers = Collections.synchronizedList(new ArrayList<EventListener>());
    observers.add(listener);
    observers = observerMap.putIfAbsent(ServiceInfo.getKey(serviceInfo.getName(), clusters), observers);
    //搞不懂为啥还要添加到前一个List中
    if (observers != null) {
        observers.add(listener);
    }
    //将serviceInfo加到changedServices中,这里只会changedServices只有一个serviceInfo
    serviceChanged(serviceInfo);
}

//添加ServiceInfo
public void serviceChanged(ServiceInfo serviceInfo) {
    if (serviceInfo == null) {
        return;
    }
    changedServices.add(serviceInfo);
}

Notifier

Notifier是EventDispatcher的一个内部类,主要用于定时扫描ServiceInfo中的List<Instance>变更情况,来通知listener

private class Notifier implements Runnable {
    @Override
    public void run() {
        //循环
        while (!closed) {
            ServiceInfo serviceInfo = null;
            try {
                //获取队列中ServiceInfo,超时5分钟
                serviceInfo = changedServices.poll(5, TimeUnit.MINUTES);
            } catch (Exception ignore) {
            }

            if (serviceInfo == null) {
                continue;
            }

            try {
                List<EventListener> listeners = observerMap.get(serviceInfo.getKey());
                if (!CollectionUtils.isEmpty(listeners)) {
                    for (EventListener listener : listeners) {
                        List<Instance> hosts = Collections.unmodifiableList(serviceInfo.getHosts());
                        //向listener发送NamingEvent事件,这里hosts就是当前服务的所有节点
                        listener.onEvent(new NamingEvent(serviceInfo.getName(), serviceInfo.getGroupName(),serviceInfo.getClusters(), hosts));
                    }
                }
            } catch (Exception e) {
                NAMING_LOGGER.error("[NA] notify error for service: " + serviceInfo.getName() + ", clusters: " + serviceInfo.getClusters(), e);
            }
        }
    }
}

看到这,有个疑问,changedServices中serviceInfo取完了就没有了,说明有个地方不断调用serviceChanged方法往队列中添加serviceInfo。查到是HostReactor中有调用,原来在NacosNamingService的init方法中创建了EventDispatcher和HostReactor实例,将EventDispatcher注入到HostReactor中,当serviceInfo有变更的时候就添加到changedServices。整体流程图如下:

image.png

总结

NacosWatch的作用就是更新当前节点的metadata和定时发送一个HearbeatEvent事件,可以监听这个事件做一些配置的动态更新操作。metadata中一般可设置一些状态标识,比如灰度发布可以设置版本号,配合网关把流量做定向分发。 HostReactor的原理后面单独写文章讲。