Nacos源码服务端系列第15篇事件中心之共享发布者

 目录

DefaultSharePublisher 

uml 类图

实现方式更优的发布者(NamingEventPublisher)

总结 


学习目标: 了解什么是共享发布者,跟DefaultPublisher有什么区别,它有哪些实现类。 

DefaultSharePublisher 

从代码中可以看出DefaultSharePublisher 的逻辑跟上一节的DefaultPublisher 的区别和联系

区别:

1、 DefaultPublisher 是针对Event类型 而DefaultSharePublisher只针对SlowEvent

2、DefaultPublisher 只能处理单个事件 而DefaultSharePublisher可以管理N个事件

第二点是最核心的区别

联系:DefaultSharePublisher继承自DefaultPublisher, 但不能当DefaultPublisher使用,场景差异很大。

public class DefaultSharePublisher extends DefaultPublisher implements ShardedEventPublisher {
    //每个事件类型一个set集合的Subscriber
    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) {
        //意味着该类之处理 SlowEvent 事件
        Class<? extends SlowEvent> subSlowEventType = (Class<? extends SlowEvent>) 
            subscribeType;
        subscribers.add(subscriber);
        
        lock.lock();
        try {
            //如果找不到该事件类型的订阅者列表就new一个空集合
            //这里大家思考以下如果不加锁是否也可以?
            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) {
        Class<? extends SlowEvent> subSlowEventType = (Class<? extends SlowEvent>) 
               subscribeType;
        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) {              
                continue;
            }
            //执行父类的通知方法
            notifySubscriber(subscriber, event);
        }
    }
}

uml 类图

实现方式更优的发布者(NamingEventPublisher)

NamingEventPublisher代码是DefaultPublisher 和 DefaultSharedEventPublisher的集合

并且摒弃了DefaultSharedEventPublisher里面一些不太好的写法。

这个类结构功能更清晰。不过NamingEventPublisher处理逻辑中没有了事件顺序号校验。

public class NamingEventPublisher extends Thread implements ShardedEventPublisher {
    
    private static final String THREAD_NAME = "naming.publisher-";
    
    private static final int DEFAULT_WAIT_TIME = 60;
    
    private final Map<Class<? extends Event>, Set<Subscriber<? extends Event>>> 
      subscribes = new ConcurrentHashMap<>();
    
    private volatile boolean initialized = false;
    
    private volatile boolean shutdown = false;
    
    private int queueMaxSize = -1;
    
    private BlockingQueue<Event> queue;
    
    private String publisherName;
    
    @Override
    public void init(Class<? extends Event> type, int bufferSize) {
        this.queueMaxSize = bufferSize;
        this.queue = new ArrayBlockingQueue<>(bufferSize);
        this.publisherName = type.getSimpleName();
        super.setName(THREAD_NAME + this.publisherName);
        super.setDaemon(true);
        super.start();
        initialized = true;
    }
    
    @Override
    public long currentEventSize() {
        return this.queue.size();
    }
    
    @Override
    public void addSubscriber(Subscriber subscriber) {
        addSubscriber(subscriber, subscriber.subscribeType());
    }
    
    @Override
    public void addSubscriber(Subscriber subscriber, Class<? extends Event> 
         subscribeType) {
        subscribes.computeIfAbsent(subscribeType, inputType -> new ConcurrentHashSet<> 
        ());
        subscribes.get(subscribeType).add(subscriber);
    }
    
    @Override
    public void removeSubscriber(Subscriber subscriber) {
        removeSubscriber(subscriber, subscriber.subscribeType());
    }
    
    @Override
    public void removeSubscriber(Subscriber subscriber, Class<? extends Event> 
       subscribeType) {
        subscribes.computeIfPresent(subscribeType, (inputType, subscribers) -> {
            subscribers.remove(subscriber);
            return subscribers.isEmpty() ? null : subscribers;
        });
    }
    
    @Override
    public boolean publish(Event event) {
        checkIsStart();
        boolean success = this.queue.offer(event);
        if (!success) {
            handleEvent(event);
            return true;
        }
        return true;
    }
    
    @Override
    public void notifySubscriber(Subscriber subscriber, Event event) {
        final Runnable job = () -> subscriber.onEvent(event);
        final Executor executor = subscriber.executor();
        if (executor != null) {
            executor.execute(job);
        } else {
            try {
                job.run();
            } catch (Throwable e) {
                Loggers.EVT_LOG.error("Event callback exception: ", e);
            }
        }
    }
    
    @Override
    public void shutdown() throws NacosException {
        this.shutdown = true;
        this.queue.clear();
    }
    
    @Override
    public void run() {
        try {
            waitSubscriberForInit();
            handleEvents();
        } catch (Exception e) {
            ...
        }
    }
    
    private void waitSubscriberForInit() {
        for (int waitTimes = DEFAULT_WAIT_TIME; waitTimes > 0; waitTimes--) {
            if (shutdown || !subscribes.isEmpty()) {
                break;
            }
            ThreadUtils.sleep(1000L);
        }
    }
    
    private void handleEvents() {
        while (!shutdown) {
            try {
                final Event event = queue.take();
                handleEvent(event);
            } catch (InterruptedException e) {
                .... 
            }
        }
    }
    
    private void handleEvent(Event event) {
        Class<? extends Event> eventType = event.getClass();
        Set<Subscriber<? extends Event>> subscribers = subscribes.get(eventType);
        .....
            return;
        }
        for (Subscriber subscriber : subscribers) {
            notifySubscriber(subscriber, event);
        }
    }
    
    void checkIsStart() {
        if (!initialized) {
            throw new IllegalStateException("Publisher does not start");
        }
    }
    
    public String getStatus() {
        return String.format("Publisher %-30s: shutdown=%5s, queue=%7d/%-7d", 
                  publisherName, shutdown,  currentEventSize(), queueMaxSize);
    }
}

为什么NamingEventPubliser 别DefaultSharePublisher实现更优秀呢

首先去掉了 lock 的代码。

其次并没有集成DefaultPublisher的鸡肋集成方式。而是重新实现了其中addSubscriber和removeSubscriber 。

总结 

本节讲解了两种类型共享发布者实现,并简单比较了2种实现的区别。代码相似度都很高,所以应该很容易理解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值