nacos2.0-源码分析-事件机制分析

本文深入解析Nacos的事件机制,包括异步事件、常规事件和慢事件,以及单事件和多事件订阅者。详细介绍了事件发布者(DefaultPublisher和DefaultSharePublisher)的工作原理,如队列机制、线程执行和事件处理流程。同时,阐述了订阅中心(notifyCenter)的角色,如注册订阅者、发布事件和管理发布者。测试用例展示了如何注册和发布慢事件。
摘要由CSDN通过智能技术生成

前言

nacos的事件机制是异步的,心跳状态变化,服务状态变化都是通过事件机制传递的。发布者发送事件,订阅者订阅事件,并执行逻辑。

1. 事件(Event)

事件包括常规事件和慢事件,都是虚函数。

1.1 常规事件(SlowEvent)

public abstract class Event implements Serializable {
 //注释1.0
    private static final AtomicLong SEQUENCE = new AtomicLong(0);
    private final long sequence = SEQUENCE.getAndIncrement();

    public long sequence() {
        return sequence;
    }
}

注释1.0:通过原子自增,保证事件的顺序;

1.2 慢事件(SlowEvent)

public abstract class SlowEvent extends Event {
   //注释1.1
    public long sequence() {
        return 0;
    }
}

注释1.1:慢事件继承自常规事件,并覆写sequence()方法,慢事件的序号不是自增的。

2.订阅者(Subscriber)

2.1 单事件订阅者(Subscriber)

//注释2.0
public abstract class Subscriber<T extends Event> {
	//注释2.1
    public abstract void onEvent(T event);
    //注释2.2
    public abstract Class<? extends Event> subscribeType();
	//注释2.3
    public Executor executor() {
        return null;
    };
    //注释2.4
    public boolean ignoreExpireEvent() {
        return false;
    };
}

注释2.0: 订阅者是虚函数;
注释2.1: 事件处理的入口,事件触发时,执行的动作,由对应的事件发布器调用;
注释2.2:订阅者类型,这里只能订阅一种类型;
注释2.3: 线程执行器,由具体的实现类来决定时同步还是异步,如果子类没有实现,则是同步执行;
注释2.4 是否忽略过期事件

2.2 智能订阅者(SmartSubscriber)

//注释2.0
public abstract class SmartSubscriber extends Subscriber {  
    //注释2.1
    public abstract List<Class<? extends Event>> subscribeTypes();
    //注释2.2
    @Override
    public final Class<? extends Event> subscribeType() {
        return null;
    }
    //注释2.3
    @Override
    public final boolean ignoreExpireEvent() {
        return false;
    }
}

注释2.0:smartSubscribe和subscriber的区别是前者可以订阅多个事件,后者只能订阅一个事件,smartSubscribe发布者是DefaultSharePublisher,subscribe的发布者是DefaultPublisher;
注释2.1 : 区别于父类Subscriber,这里支持多个事件类型;
注释2.2:继承父类但是用final修饰,不可继续继承;
注释2.3:继承父类但是用final修饰,不可改变,忽略过期事件;

3.发布者 (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和DefaultSharePublisher

3.1 事件发布者(DefaultPublisher)

//实现EventPublisher,继承Thread类,实现run方法,达到多线程的目的
public class DefaultPublisher extends Thread implements EventPublisher {

    protected static final Logger LOGGER = LoggerFactory.getLogger(DefaultPublisher.class);
    private volatile boolean initialized = false;
	//是否关闭
    private volatile boolean shutdown = false;
	//通知队列的最大值
    private int queueMaxSize = -1;
    //事件放入阻塞队列
    private BlockingQueue<Event> queue;
    //最新的阻塞队列序号
    protected volatile Long lastEventSequence = -1L;
	// 事件序列号更新对象,用于更新原子属性lastEventSequence
    private static final AtomicReferenceFieldUpdater<DefaultPublisher, Long> UPDATER = AtomicReferenceFieldUpdater
            .newUpdater(DefaultPublisher.class, Long.class, "lastEventSequence");
     //事件类型
    private Class<? extends Event> eventType;
     //去重的事件订阅者集合,这里采用的ConcurrentHashSet是自己封装的ConcurrentHashMap,不过value统一为boolean,感兴趣的可以看下
    protected final ConcurrentHashSet<Subscriber> subscribers = new ConcurrentHashSet<>();

初始化

   public void init(Class<? extends Event> type, int bufferSize) {
        setDaemon(true);
        setName("nacos-publisher-" + type.getName());
        this.eventType = type;
        this.queueMaxSize = bufferSize;
        this.queue = new ArrayBlockingQueue<>(bufferSize);
        start();
    }

初始化将线程设置为守护线程,设置线程名称,设置事件类型,带大小的阻塞队列。

    @Override
    public synchronized void start() {
        if (!initialized) {
            //调用线程父类的start,启动线程
            super.start();
            if (queueMaxSize == -1) {
//                queueMaxSize = ringBufferSize;
            }
            initialized = true;
        }
    }

线程执行主体

   @Override
    public void run() {
        System.out.print("启动子线程");
        openEventHandler();
    }

真正执行的方法openEventHandler()

    private void openEventHandler() {
        try {

            int waitTimes = 60;
            //循环等待10分钟
            for (; ; ) {
                if (shutdown || hasSubscriber() || waitTimes <= 0) {
                    break;
                }
                ThreadUtils.sleep(1000);
                waitTimes--;
            }
            //循环获取数据,每次获取一个
            for (; ; ) {
                if (shutdown) {
                    break;
                }
                //阻塞获取数据
                final Event event = queue.take();
                //获取到事件,执行逻辑
                receiveEvent(event);
                UPDATER.compareAndSet(this, lastEventSequence, Math.max(lastEventSequence, event.sequence()));
            }
        } catch (Exception ex) {
            LOGGER.error("common.Event common.listener exception :", ex);
        }
    }

首先等待1分钟,然后从阻塞队列获取事件数据,如果有数据,执行receiveEvent()方法。

     void receiveEvent(Event event) {
        final long currentEventSequence = event.sequence();
        if (!hasSubscriber()) {
            LOGGER.warn("[NotifyCenter] the {} is lost, because there is no subscriber.");
            return;
        }
//如果事件过期了,则跳过
        for (Subscriber subscriber : subscribers) {
            if (subscriber.ignoreExpireEvent() && lastEventSequence > currentEventSequence) {
                LOGGER.debug("[NotifyCenter] the {} is unacceptable to this subscriber, because had expire",
                        event.getClass());
                continue;
            }
            notifySubscriber(subscriber, event);
        }
    }

如果过期,并且要处理的序列id大于最新的序列id则不做处理。否则执 notifySubscriber()

    @Override
    public void notifySubscriber(Subscriber subscriber, Event event) {
        LOGGER.debug("[NotifyCenter] the {} will received by {}", event, subscriber);
        //执行订阅者event
        final Runnable job = () -> subscriber.onEvent(event);
        final Executor executor = subscriber.executor();
        //如果自己实现了executor则调用
        if (executor != null) {
            executor.execute(job);
        } else {
            try {
                job.run(); //如果没有,则当前线程直接调用
            } catch (Throwable e) {
                LOGGER.error("common.notify.Event callback exception: ", e);
            }

        }
    }

这里会发送给订阅者,若订阅者没有自己的执行器,则直接执行run方法启动订阅者消费线程。
外部调用发布事件
前面发布的事件是指从队列内部获取事件并通知订阅者,这里的发布事件区别在于它是开放给外部调用者,接收统一通知中心的事件并放入队列中的。

    @Override
    public boolean publish(common.Event event) {
        checkIsStart();
        //入队
        boolean success = this.queue.offer(event);
        //如果没有入队
        if (!success) {
            LOGGER.warn("Unable to plug in due to interruption, synchronize sending time, event : {}", event);
            receiveEvent(event); //同步发送事件
            return true;
        }
        return false;
    }

在放入队列成功的时候直接返回,若放入队列失败,则直接同步发送事件给订阅者。若队列可用,则外部调用者完成入队直接完成本次调用,不需要 等待循环通知订阅者。使用队列解耦无疑会提升通知中心的工作效率。
这种解耦总的来说就是一个发布者内部维护了一个BlockingQueue,在实现上使用ArrayBlockQueue,它是一个有界阻塞队列,元素先进先出。并且使用非公平的模式提升性能。这也意味着订阅者执行顺序将得不到保证。同时也维护了一个订阅者集合(他们都订阅了同一个事件类型),在死循环中不断从ArrayBlockQueue中获取数据,来循环通知每个订阅者,也就是调用订阅者的onEvent()。

3.2 多事件发布者(DefaultSharePublisher)

用于发布SlowEvent事件并通知所有订阅了该事件的订阅者。

public class DefaultSharePublisher extends DefaultPublisher implements ShardedEventPublisher {
	//用于保存事件类型为SlowEvent的订阅者,一个事件类型对应多个订阅者
    private final Map<Class<? extends SlowEvent>, Set<Subscriber>> subMappings = new ConcurrentHashMap<>();
	//可重入排他锁
    private final Lock lock = new ReentrantLock();
    //重写父类方法添加订阅者,
 	@Override
    public void addSubscriber(Subscriber subscriber, Class<? extends Event> subscribeType) {

        Class<? extends SlowEvent> subSlowEventType = (Class<? extends SlowEvent>) subscribeType;
		//往父类的方法里添加订阅者,用到父类的阻塞队列
        subscribers.add(subscriber);
        //加锁
        lock.lock(); 
        try {
            Set<Subscriber> sets = subMappings.get(subSlowEventType);
            if (sets == null) {
                Set<Subscriber> newSet = new ConcurrentHashSet<Subscriber>();
                newSet.add(subscriber);
                subMappings.put(subSlowEventType, newSet);
                return;
            }
            sets.add(subscriber);
        } finally {
        //解锁
            lock.unlock();
        }

    }
    }

移除订阅者


    @Override
    public void removeSubscriber(Subscriber subscriber, Class<? extends Event> subscribeType) {
        // Actually, do a classification based on the slowEvent type.
        Class<? extends SlowEvent> subSlowEventType = (Class<? extends SlowEvent>) subscribeType;
        // For removing to parent class attributes synchronization.
        subscribers.remove(subscriber);

        lock.lock();
        try {
            Set<Subscriber> sets = subMappings.get(subSlowEventType);

            if (sets != null) {
                sets.remove(subscriber);
            }
        } finally {
            lock.unlock();
        }
    }

接收事件

    //重写获取事件的逻辑
    @Override
    public void receiveEvent(Event event) {
        final long currentEventSequence = event.sequence();

        // 获取事件类型
        final Class<? extends SlowEvent> slowEventType = (Class<? extends SlowEvent>) event.getClass();

        // 获取事件的订阅者集合
        Set<Subscriber> subscribers = subMappings.get(slowEventType);
		//订阅者为空直接返回
        if (null == subscribers) {
            LOGGER.debug("[NotifyCenter] No subscribers for slow event {}", slowEventType.getName());
            return;
        }
		
        // 循环遍历通知
        for (Subscriber subscriber : subscribers) {
            // Whether to ignore expiration events
            if (subscriber.ignoreExpireEvent() && lastEventSequence > currentEventSequence) {
                LOGGER.debug("[NotifyCenter] the {} is unacceptable to this subscriber, because had expire",
                        event.getClass());
                continue;
            }
            // 通知订阅者,调用父类方法
            notifySubscriber(subscriber, event);
        }
    }

DefaultPublisher是一个发布器只负责发布一个事件,并通知订阅了这个事件的所有订阅者;
DefaultSharePublisher则是一个发布器可以发布多个事件,并通知订阅了这个事件的所有订阅者。

4.订阅中心(notifyCenter)

订阅中心是大总管manger,主要用于注册发布者,调用发布者发布事件,为发布者注册订阅者,为注册事件增加指定订阅者等操作。可以说她完全接管了订阅者,发布者和事件他们的组合过程。直接调用通知中心的相关方法即可实现事件发布订阅,注册等功能。

public class NotifyCenter {
	//单事件发布者内部队列容量
    public static int ringBufferSize = 16384;
    //多事件发布者内部的事件队列初始容量
    public static int shareBufferSize = 1024;
	//多事件发布者
    private DefaultSharePublisher sharePublisher;

    private static final NotifyCenter INSTANCE = new NotifyCenter();
	//发布者工厂
    private static final EventPublisherFactory DEFAULT_PUBLISHER_FACTORY;

    private static Class<? extends EventPublisher> clazz;
	//发布者状态
    private static final AtomicBoolean CLOSED = new AtomicBoolean(false);
	//单事件发布者存储容器
    private final Map<String, EventPublisher> publisherMap = new ConcurrentHashMap<>(16);
     //省略部分代码
   }

静态代码块:

{
        String ringBufferSizeProperty = "nacos.core.notify.ring-buffer-size";
        ringBufferSize = Integer.getInteger(ringBufferSizeProperty, 16384);

        // The size of the public publisher's message staging queue buffer
        String shareBufferSizeProperty = "nacos.core.notify.share-buffer-size";
        shareBufferSize = Integer.getInteger(shareBufferSizeProperty, 1024);
//使用Nacos 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 RuntimeException(ex);
            }
        };

        try {
            //new实例发布者
            INSTANCE.sharePublisher = new DefaultSharePublisher();
             //初始化实例发布者
            INSTANCE.sharePublisher.init(SlowEvent.class, shareBufferSize);

        } catch (Throwable ex) {
            LOGGER.error("Service class newInstance has error : ", ex);
        }
	//添加关闭钩子,用于关闭publisher
     ThreadUtils.addShutdownHook(NotifyCenter::shutdown);
    }

关闭方法:

  public static void shutdown() {
        //如果是true,改为false
        if (!CLOSED.compareAndSet(false, true)) {
            return;
        }
        LOGGER.warn("[NotifyCenter] Start destroying Publisher");
		//遍历单事件发布者
        for (Map.Entry<String, EventPublisher> entry : INSTANCE.publisherMap.entrySet()) {
            try {
                EventPublisher eventPublisher = entry.getValue();
                eventPublisher.shutdown();
            } catch (Throwable e) {
                LOGGER.error("[EventPublisher] shutdown has error : ", e);
            }
        }

        try {
        //关闭多事件发布者
            INSTANCE.sharePublisher.shutdown();
        } catch (Throwable e) {
            LOGGER.error("[SharePublisher] shutdown has error : ", e);
        }

        LOGGER.warn("[NotifyCenter] Destruction of the end");
    }

注册订阅者

 public static void registerSubscriber(final Subscriber consumer, final EventPublisherFactory factory) {
        // If you want to listen to multiple events, you do it separately,
        // based on subclass's subscribeTypes method return list, it can register to publisher.
        //如果是多事件订阅者
        if (consumer instanceof SmartSubscriber) {
            for (Class<? extends Event> subscribeType : ((SmartSubscriber) consumer).subscribeTypes()) {
                // For case, producer: defaultSharePublisher -> consumer: smartSubscriber.
                //添加多事件发布者
                if (ClassUtils.isAssignableFrom(SlowEvent.class, subscribeType)) {
                
                    INSTANCE.sharePublisher.addSubscriber(consumer, subscribeType);
                } else {
                //添加单事件订阅者
                    // For case, producer: defaultPublisher -> consumer: subscriber.
                    addSubscriber(consumer, subscribeType, factory);
                }
            }
            return;
        }

        final Class<? extends Event> subscribeType = consumer.subscribeType();
        if (ClassUtils.isAssignableFrom(SlowEvent.class, subscribeType)) {
            INSTANCE.sharePublisher.addSubscriber(consumer, subscribeType);
            return;
        }

        addSubscriber(consumer, subscribeType, factory);
    }

    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.
            //mapUtils不是线程安全的,要加锁
            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);
        }
    }

注册发布者

实际上并没有直接的注册发布者这个概念,通过前面的章节你肯定知道发布者就两种类型:单事件发布者、多事件发布者。单事件发布者直接就一个实例,多事件发布者会根据事件类型创建不同的实例,存储于publisherMap中。它已经在通知中心了,因此并不需要有刻意的注册动作。需要使用的时候直接取即可,发布者在成员变量里已经生成好了。

注销发布者

    public static void deregisterPublisher(final Class<? extends Event> eventType) {
        final String topic = ClassUtils.getCanonicalName(eventType);
        EventPublisher publisher = INSTANCE.publisherMap.remove(topic);
        try {
            publisher.shutdown();
        } catch (Throwable ex) {
            LOGGER.error("There was an exception when publisher shutdown : ", ex);
        }
    }

这里只是针对单发布者来说,因为多事件发布者只有一个实例,它需要处理多个事件类型,因此发布者不能移除;而单事件发布者一个发布者对应一个事件类型,因此某个类型的事件不需要处理的时候则需要将对应的发布者移除。

发布事件

  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);
        }
        LOGGER.warn("There are no [{}] publishers for this event, please register", topic);
        return false;
    }

5.测试用例

测试注册慢事件,可以注册多个不同类型的事件。

 @Test
    public void testMutipleSlowEventsListenedBySmartsubscriber() throws Exception {
        
        NotifyCenter.registerToSharePublisher(TestSlowEvent3.class);
        NotifyCenter.registerToSharePublisher(TestSlowEvent4.class);
        
        final AtomicInteger count1 = new AtomicInteger(0);
        final AtomicInteger count2 = new AtomicInteger(0);
        
        final CountDownLatch latch1 = new CountDownLatch(3);
        final CountDownLatch latch2 = new CountDownLatch(3);
        
        NotifyCenter.registerSubscriber(new SmartSubscriber() {
            
            @Override
            public void onEvent(Event event) {
                if (event instanceof TestSlowEvent3) {
                    count1.incrementAndGet();
                    latch1.countDown();
                }
                
                if (event instanceof TestSlowEvent4) {
                    count2.incrementAndGet();
                    latch2.countDown();
                }
            }
            
            @Override
            public List<Class<? extends Event>> subscribeTypes() {
                List<Class<? extends Event>> subTypes = new ArrayList<Class<? extends Event>>();
                subTypes.add(TestSlowEvent3.class);
                subTypes.add(TestSlowEvent4.class);
                return subTypes;
            }
        });
        
        for (int i = 0; i < 3; i++) {
            Assert.assertTrue(NotifyCenter.publishEvent(new TestSlowEvent3()));
            Assert.assertTrue(NotifyCenter.publishEvent(new TestSlowEvent4()));
        }
        
        ThreadUtils.sleep(2000L);
        
        latch1.await(3000L, TimeUnit.MILLISECONDS);
        latch2.await(3000L, TimeUnit.MILLISECONDS);
        
        Assert.assertEquals(3, count1.get());
        Assert.assertEquals(3, count2.get());
        
    }

6.代码

我把nacos事件机制的代码单独撸出来了,放在git上,排除其余模块的干扰,有兴趣的可以看看。
nacos事件源码git地址

7.总结

事件类型为Event,订阅者类型是Subscriber
事件类型为SlowEvent, 订阅者类型是SmartSubscriber

单事件发布者
发布者和事件的关系是一对一
事件和订阅者的关系是一对多
发布者和订阅者的关系是一对多
多事件发布者:
发布者和事件的关系是一对多
事件和订阅者的关系是一对多
发布者和订阅者的关系是一对多
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值