Nacos事件驱动架构源码
1.体验Nacos源码提供的事件驱动架构功能
角色
- 事件
- 事件订阅者
- 事件发送者
源码
事件
/**
* @author IT贱男,继承Event是Nacos源码里面的
* @version v1.0.0
* @date 2023/3/13 11:01
*/
public class TestEvent extends Event {
}
事件订阅者
/**
* 订阅者
*
* @author IT贱男
* @version v1.0.0
* @date 2023/3/13 10:57
*/
@Component
public class TestSubscriber extends SmartSubscriber {
public TestSubscriber() {
NotifyCenter.registerSubscriber(this);
}
@Override
public List<Class<? extends Event>> subscribeTypes() {
List<Class<? extends Event>> result = new LinkedList<>();
result.add(TestEvent.class);
return result;
}
@Override
public void onEvent(Event event) {
System.out.println("TestSubscriber onEvent");
}
}
事件发送者
@RestController
@RequestMapping("/sub")
public class SubscriberController {
@GetMapping("/test")
public void test(){
NotifyCenter.publishEvent(new TestEvent());
}
}
2.图示
EventPublisher图示
添加事件订阅者流程
发布事件流程
3.源码
DefaultPublisher类
介绍
负责存储事件和推送事件给绑定事件的所有消费者
继承结构
核心属性
/**启动标识*/
private volatile boolean initialized = false;
/**终止标识*/
private volatile boolean shutdown = false;
/**该事件发布者对应的类型*/
private Class<? extends Event> eventType;
/**该事件发布者绑定的所有监听者*/
protected final ConcurrentHashSet<Subscriber> subscribers = new ConcurrentHashSet<>();
/**队列的最大大小*/
private int queueMaxSize = -1;
/**存储事件的队列*/
private BlockingQueue<Event> queue;
protected volatile Long lastEventSequence = -1L;
核心方法init
设置各种属性和启动Thread
@Override
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 just called once
super.start();
if (queueMaxSize == -1) {
queueMaxSize = ringBufferSize;
}
initialized = true;
}
}
核心方法addSubscriber
添加对应的事件订阅者
@Override
public void addSubscriber(Subscriber subscriber) {
// 添加订阅者
subscribers.add(subscriber);
}
核心方法publish
发布事件
@Override
public boolean publish(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);
// 直接消费事件然后返回true
receiveEvent(event);
return true;
}
return true;
}
核心方法run
线程拉取事件并消费的方法,由当前线程执行
@Override
public void run() {
openEventHandler();
}
void openEventHandler() {
try {
// 定义此变量是为了解决队列中信息过量的问题
int waitTimes = 60;
// 为确保信息不会丢失,请在以下情况下启用 EventHandler
// 等待第一个订阅者注册
for (; ; ) {
if (shutdown || hasSubscriber() || waitTimes <= 0) {
break;
}
ThreadUtils.sleep(1000L);
waitTimes--;
}
// 自选
for (; ; ) {
// 如果shutdown了就停止
if (shutdown) {
break;
}
// 从队列中获取一个Event
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);
}
}
void receiveEvent(Event event) {
final long currentEventSequence = event.sequence();
if (!hasSubscriber()) {
LOGGER.warn("[NotifyCenter] the {} is lost, because there is no subscriber.", event);
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;
}
// Because unifying smartSubscriber and subscriber, so here need to think of compatibility.
// Remove original judge part of codes.
//通知
notifySubscriber(subscriber, event);
}
}
@Override
public void notifySubscriber(final Subscriber subscriber, final Event event) {
LOGGER.debug("[NotifyCenter] the {} will received by {}", event, subscriber);
// 把subscriber的执行封装到Runnable中
final Runnable job = () -> subscriber.onEvent(event);
// 获取执行器,默认是NULL
final Executor executor = subscriber.executor();
// 如果有则用这个监听器返回的线程池来异步执行任务
if (executor != null) {
executor.execute(job);
} else {
try {
// 否则用这个线程来执行任务
job.run();
} catch (Throwable e) {
LOGGER.error("Event callback exception: ", e);
}
}
}
NotifyCenter类
核心属性
/**创建事件发布者对象的队列的大小*/
public static int ringBufferSize;
private static final AtomicBoolean CLOSED = new AtomicBoolean(false);
/**用于创建事件发布者的工厂*/
private static final EventPublisherFactory DEFAULT_PUBLISHER_FACTORY;
/**实例*/
private static final NotifyCenter INSTANCE = new NotifyCenter();
/**默认sharePublisher的*/
private DefaultSharePublisher sharePublisher;
/**要创建EventPublisher的类型*/
private static Class<? extends EventPublisher> clazz;
/**
* 发布者管理容器
* key:topic
* value:对应的EventPublisher
*/
private final Map<String, EventPublisher> publisherMap = new ConcurrentHashMap<>(16);
核心方法静态代码块
主要就是初始化各种东西
static {
// Internal ArrayBlockingQueue buffer size. For applications with high write throughput,
// this value needs to be increased appropriately. default value is 16384
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);
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 {
// 创建一个DefaultPublisher
EventPublisher publisher = clazz.newInstance();
// 调用init方法
publisher.init(cls, buffer);
// 返回
return publisher;
} catch (Throwable ex) {
LOGGER.error("Service class newInstance has error : ", ex);
throw new NacosRuntimeException(SERVER_ERROR, ex);
}
};
try {
// Create and init DefaultSharePublisher instance.
INSTANCE.sharePublisher = new DefaultSharePublisher();
INSTANCE.sharePublisher.init(SlowEvent.class, shareBufferSize);
} catch (Throwable ex) {
LOGGER.error("Service class newInstance has error : ", ex);
}
ThreadUtils.addShutdownHook(NotifyCenter::shutdown);
}
核心方法publishEvent
发布事件
public static boolean publishEvent(final Event event) {
try {
return publishEvent(event.getClass(), event);
} catch (Throwable ex) {
LOGGER.error("There was an exception to the message publishing : ", ex);
return false;
}
}
private static boolean publishEvent(final Class<? extends Event> eventType, final Event event) {
if (ClassUtils.isAssignableFrom(SlowEvent.class, eventType)) {
return INSTANCE.sharePublisher.publish(event);
}
// 获取所传类从java语言规范定义的格式输出
final String topic = ClassUtils.getCanonicalName(eventType);
// 根据topic获取EventPublisher
EventPublisher publisher = INSTANCE.publisherMap.get(topic);
// 如果存在则调用publish方法
if (publisher != null) {
return publisher.publish(event);
}
LOGGER.warn("There are no [{}] publishers for this event, please register", topic);
return false;
}
核心方法registerSubscriber
注册事件订阅者
public static void registerSubscriber(final Subscriber consumer) {
registerSubscriber(consumer, DEFAULT_PUBLISHER_FACTORY);
}
public static void registerSubscriber(final Subscriber consumer, final EventPublisherFactory factory) {
// 如果要监听多个事件,则需要分别进行
// 根据子类的 subscribeTypes 方法返回列表,它可以向发布者注册
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 {
// 添加订阅者
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) {
// 根据事件类型获取topic
final String topic = ClassUtils.getCanonicalName(subscribeType);
// 上锁
synchronized (NotifyCenter.class) {
// MapUtils.computeIfAbsent 是一个不安全的方法
// 这里很关键,会创建 EventPublisher 对象,一个事件会对应一个 EventPublisher 一个对象
MapUtil.computeIfAbsent(INSTANCE.publisherMap, topic, factory, subscribeType, ringBufferSize);
}
// 获取EventPublisher
EventPublisher publisher = INSTANCE.publisherMap.get(topic);
if (publisher instanceof ShardedEventPublisher) {
((ShardedEventPublisher) publisher).addSubscriber(consumer, subscribeType);
} else {
// 往 EventPublisher 对象添加订阅者信息
publisher.addSubscriber(consumer);
}
}