Nacos源码阅读前置导读-01-通知中心

97 阅读4分钟

nacos的发布者订阅者的实现

nacos2.0采用了订阅者/发布者模式对各类事件的处理进行解耦,本小节将对该实现进行解析。

上图为Nacos事件机制的实现相关类,如果是订阅者/发布者模式的同学一定注意到了Publisher和Subscriber,分别对应了发布者和订阅者。

Publisher & Subscriber

  1. Publisher 发布者

顶级接口为EventPublisher

public interface EventPublisher extends Closeable {
    
	// 初始化方法,用于初始化发布者关心事件类型和缓存队列大小
    void init(Class<? extends Event> type, int bufferSize);
    
	// 当前事件队列大小
    long currentEventSize();
    
    // 添加订阅者
    void addSubscriber(Subscriber subscriber);
    
    // 移除订阅者
    void removeSubscriber(Subscriber subscriber);
    
    // 发布事件
    boolean publish(Event event);
    
    // 通知订阅者
    void notifySubscriber(Subscriber subscriber, Event event);
    
}

DefaultPublisher为默认实现,也是我们在使用NotifyCenter时候采用的默认发布者

public class DefaultPublisher extends Thread implements EventPublisher

DefaultPublisher继承了Thread,表明DefaultPublisher为一个线程,其run方法如下

    @Override
    public void run() {
        openEventHandler();
    }
    
    void openEventHandler() {
        try {
            
            // This variable is defined to resolve the problem which message overstock in the queue.
            int waitTimes = 60;
            // 当没有停机时,并且没有订阅者时候进行等待,最多等待60秒
            while (!shutdown && !hasSubscriber() && waitTimes > 0) {
                ThreadUtils.sleep(1000L);
                waitTimes--;
            }

            while (!shutdown) {
                // 其中queue为阻塞队列,默认采用ArrayBlockingQueue
                final Event event = queue.take();
                receiveEvent(event);
                // 更新最后一次处理时间
                UPDATER.compareAndSet(this, lastEventSequence, Math.max(lastEventSequence, event.sequence()));
            }
        } catch (Throwable ex) {
            LOGGER.error("Event listener exception : ", ex);
        }
    }
    
  1. Subscriber 订阅者

顶级接口为Subscriber

public abstract class Subscriber<T extends Event> {
    // 事件回调    
    public abstract void onEvent(T event);
    
    // 订阅的事件类型
    public abstract Class<? extends Event> subscribeType();
    
    // 通过该方法决定订阅者在消费事件时是否在线程池中执行
    public Executor executor() {
        return null;
    }
    
    // 是否无视超时的事件
    public boolean ignoreExpireEvent() {
        return false;
    }
    
    // 判断当前订阅者的作用域是否符合当前事件
    public boolean scopeMatches(T event) {
        return true;
    }
}

除了以上的发布者和订阅者外,我们还需要关注两个类EventPublisherFactory和NotifyCenter。

EventPublishFactory

首先,介绍一下EventPublisherFactory,该类采用了抽象工厂模式用于生产发布者,继承了BiFunction接口,后续的工厂通过实现apply方法完成发布者的建造。

public interface EventPublisherFactory extends BiFunction<Class<? extends Event>, Integer, EventPublisher> {
    @Override
    EventPublisher apply(Class<? extends Event> eventType, Integer maxQueueSize);
}

NotifyCenter

然后便是关键的NotiyCenter,顾名思义便是事件通知中心,其起到的作用是纳管所有的发布者和进行事件发布。

核心的成员变量如下

public class NotifyCenter {
    // 对于非共享的订阅者的事件缓存队列大小
    public static int ringBufferSize;
    // 对于共享的订阅者的事件缓存队列大小
    public static int shareBufferSize;
    // notifyCenter是否关闭的标识
    private static final AtomicBoolean CLOSED = new AtomicBoolean(false);
    // 事件发布者工厂
    private static final EventPublisherFactory DEFAULT_PUBLISHER_FACTORY;
    // 单例模式
    private static final NotifyCenter INSTANCE = new NotifyCenter();
    // 共享事件发布者
    private DefaultSharePublisher sharePublisher;
    // 事件发布者工厂的默认事件发布者类型
    private static Class<? extends EventPublisher> clazz;
    // key为Event类的小写名称,key为对应事件发布者
    private final Map<String, EventPublisher> publisherMap = new ConcurrentHashMap<>(16);
}

其核心方法为

addSubscriber:添加消费者

    private static void addSubscriber(final Subscriber consumer, Class<? extends Event> subscribeType,
            EventPublisherFactory factory) {
        
        final String topic = ClassUtils.getCanonicalName(subscribeType);
        synchronized (NotifyCenter.class) {
            // MapUtils.computeIfAbsent is a unsafe method.
            MapUtil.computeIfAbsent(INSTANCE.publisherMap, topic, factory, subscribeType, ringBufferSize);
        }
        EventPublisher publisher = INSTANCE.publisherMap.get(topic);
        if (publisher instanceof ShardedEventPublisher) {
            ((ShardedEventPublisher) publisher).addSubscriber(consumer, subscribeType);
        } else {
            publisher.addSubscriber(consumer);
        }
    }

registerToPublisher:该方法注册Event对应的发布者并返回

    public static EventPublisher registerToPublisher(final Class<? extends Event> eventType,
            final EventPublisherFactory factory, final int queueMaxSize) {
        if (ClassUtils.isAssignableFrom(SlowEvent.class, eventType)) {
            return INSTANCE.sharePublisher;
        }
        
        final String topic = ClassUtils.getCanonicalName(eventType);
        synchronized (NotifyCenter.class) {
            // MapUtils.computeIfAbsent is a unsafe method.
            MapUtil.computeIfAbsent(INSTANCE.publisherMap, topic, factory, eventType, queueMaxSize);
        }
        return INSTANCE.publisherMap.get(topic);
    }
    

publishEvent:根据事件类型,在publisherMap中找到对应的发布者执行发布。

    private static boolean publishEvent(final Class<? extends Event> eventType, final Event event) {
        if (ClassUtils.isAssignableFrom(SlowEvent.class, eventType)) {
            return INSTANCE.sharePublisher.publish(event);
        }
        
        final String topic = ClassUtils.getCanonicalName(eventType);
        
        EventPublisher publisher = INSTANCE.publisherMap.get(topic);
        if (publisher != null) {
            return publisher.publish(event);
        }
        if (event.isPluginEvent()) {
            return true;
        }
        LOGGER.warn("There are no [{}] publishers for this event, please register", topic);
        return false;
    }
    

此外在NotifyCenter初始化时候还采用了Spi机制对默认的EventPublisher进行自定义

    static {

        // 省略部分代码
        // spi加载
        final Collection<EventPublisher> publishers = NacosServiceLoader.load(EventPublisher.class);
        Iterator<EventPublisher> iterator = publishers.iterator();
        if (iterator.hasNext()) {
            clazz = iterator.next().getClass();
        } else {
            clazz = DefaultPublisher.class;
        }
        
        DEFAULT_PUBLISHER_FACTORY = (cls, buffer) -> {
            try {
                EventPublisher publisher = clazz.newInstance();
                publisher.init(cls, buffer);
                return publisher;
            } catch (Throwable ex) {
                LOGGER.error("Service class newInstance has error : ", ex);
                throw new NacosRuntimeException(SERVER_ERROR, ex);
            }
        };
        // 省略部分代码
    }
    

总结

nacos通过NotifyCenter对Publisher进行纳管,每一个Event都会被放入对应Publisher的阻塞队列中等待执行。

调用方通过NotifyCenter注册订阅者时,实际上是根据订阅者的事件类型查找或创建对应的发布者,将订阅者注册进入发布者。

调用方通过NotiryCenter对事件进行注册时,实际上是根据订阅者类型查找或者创建对应的发布者。