Nacos - 事件的註冊、取消與監聽(EventDispatcher)

大軍發表於2021-01-04

我們在Nacos - NacosNamingService初始化提過,NacosNamingService物件建立的時候,會建立一個EventDispatcher物件。EventDispatcher的構造方法如下,建立一個執行緒池,然後放入Notifier任務。

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;
        }
    });
    this.executor.execute(new Notifier());
}

Notifier的Runnable的類,所以放入執行緒池的時候,會執行run方法。他主要是從阻塞佇列changedServices取出ServiceInfo,然後根據ServiceInfo的key取出他對應的EventListener集合,再執行EventListener的onEvent方法。

@Override
public void run() {
    while (!closed) {
        ServiceInfo serviceInfo = null;
        try {
            // changedServices是LinkedBlockingQueue,從阻塞佇列取值
            serviceInfo = changedServices.poll(5, TimeUnit.MINUTES);
        } catch (Exception ignore) {
        }
        // 沒取值,重新從阻塞佇列取
        if (serviceInfo == null) {
            continue;
        }
        
        try {
            // 從observerMap取到EventListener集合
            List<EventListener> listeners = observerMap.get(serviceInfo.getKey());
            
            if (!CollectionUtils.isEmpty(listeners)) {
                for (EventListener listener : listeners) {
                    // 執行onEvent方法
                    List<Instance> hosts = Collections.unmodifiableList(serviceInfo.getHosts());
                    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);
        }
    }
}

Nacos - 啟動中,提到NacosWatch例項化的時候,就會呼叫namingService.subscribe,他會呼叫EventDispatcher#addListener方法,在這裡會把監聽放入observerMap的map裡,然後呼叫serviceChanged方法。

public void addListener(ServiceInfo serviceInfo, String clusters, EventListener listener) {
        
    NAMING_LOGGER.info("[LISTENER] adding " + serviceInfo.getName() + " with " + clusters + " to listener map");
    List<EventListener> observers = Collections.synchronizedList(new ArrayList<EventListener>());
    observers.add(listener);
    
    observers = observerMap.putIfAbsent(ServiceInfo.getKey(serviceInfo.getName(), clusters), observers);
    if (observers != null) {
        observers.add(listener);
    }
    
    serviceChanged(serviceInfo);
}

當serviceChanged被呼叫的時候,就會往阻塞佇列存入ServiceInfo。上面已經知道了有個迴圈任務一直從阻塞佇列changedServices取值,這個值就是這麼來的。

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

總結

這個類有兩個比較重要的成員,一個是observerMap,他的key是serviceInfo.getKey(),value是EventListener集合。一個是changedServices,存放serviceInfo的LinkedBlockingQueue阻塞佇列。這兩個成員的關聯關係透過serviceInfo.getKey()維持。
當呼叫addListener的時候,就會把serviceInfo存入到changedServices,以及serviceInfo.getKey()和EventListener集合存入到observerMap。
while(true)中,由於阻塞佇列changedServices有值,就會從中拿到serviceInfo,再透過serviceInfo.getKey()拿到observerMap對應的EventListener集合,然後執行EventListener集合的EventListener.onEvent方法。
image.png

相關文章