个人理解: 事件驱动(even-driven),字面理解即:由事件去触发某个或者一系列动作。
百度百科: 从事件角度说,事件驱动程序的基本结构是由一个事件收集器、一个事件发送器和一个事件处理器组成。 事件收集器专门负责收集所有事件,包括来自用户的(如鼠标、键盘事件等)、来自硬件的(如时钟事件等)和来自软件的(如操作系统、应用程序本身等)。 事件发送器负责将收集器收集到的事件分发到目标对象中。 事件处理器做具体的事件响应工作。
举个栗子:起床闹钟响了驱动我们该起床了,上课铃声响了驱动我们进教室上课、放学铃声响了驱动我们上课结束了可以爱干嘛干嘛去了。
或许这个栗子符合你,但是对我来说根本不可能?,闹钟响了压根不会起床、不起床听不到上课铃、不去上课也听不到下课铃,GG
转入正题,事件驱动的应用无处不在比如:spring、netty、zookeeper、mq,而且事件【event】和监听器【listener】都是成对存在的,单个存在是没有意义的,下面手动实现一个事件驱动模型。
那么事件驱动模型的组成是怎样的呢?
一、事件驱动模型
先去百度了一张模型图,当当当当~
说实话,图不是自己画的就是不满意,但是 MAC
上画图工具都找不到好用的,就懒得画了,把事件收集器 ==> **事件中心 **,事件和监听器的关系可以是1:1、1:N、N:M
,取决于自己的需求。
说下流程:
1. 系统启动,监听器把自己注册到事件中心,与某种事件进行绑定
2. 事件发送器发送事件到事件中心,事件中心去查找与处理该事件的监听器
复制代码
过程非常简单,为什么要用事件驱动呢?刚好百度百科有个栗子,看完相信优秀的你就明白了:
通常,我们写服务器处理模型的程序时,有以下几种模型:
(1)每收到一个请求,创建一个新的进程,来处理该请求;
(2)每收到一个请求,创建一个新的线程,来处理该请求;
(3)每收到一个请求,放入一个事件列表,让主进程通过非阻塞I/O方式来处理请求
上面的几种方式,各有千秋,
第(1)种方法,由于创建新的进程的开销比较大,所以,会导致服务器性能比较差,但实现比较简单。
第(2)种方式,由于要涉及到线程的同步,有可能会面临死锁等问题。
第(3)种方式,在写应用程序代码时,逻辑比前面两种都复杂。
综合考虑各方面因素,一般普遍认为第(3)种方式是大多数网络服务器采用的方式
实际上,事件驱动模型的核心就是 线程池 !!! 来实现异步非阻塞。
二、Simple实现
OK,到这里就是动手实践的过程,好记性不如烂笔头,读百遍不如敲一遍。看下工程结构
项目地址忘了放 event-driven
理论部分说到事件和监听器的关系可以是1:1、1:N、N:M
,这里基于 spring boot 编写一个事件:监听器=1:N
的实现,老套路,跟着上面的流程分析走:
- 系统启动,监听器把自己注册到事件中心,与某种事件进行绑定
- 事件发送器发送事件到事件中心,事件中心去查找与处理该事件的监听器
写一个监听器接口EventListener
和两个实现类OrderCancelListener
、OrderCreateListener
package com.glmapper.event.driven.listener;
/**
* 监听器接口
* @author: Jerry
* @date: 2018/7/1
*/
public interface EventListener{
/**
* 事件触发时调用
*
* @param event
*/
void trigger(OrderEvent event);
}
复制代码
package com.glmapper.event.driven.listener;
/**
* 订单取消事件监听
* @author: Jerry
* @date: 2018/7/1
*/
@Slf4j
@Component
public class OrderCancelListener implements EventListener {
@Autowired
private EventCenter eventCenter;
@PostConstruct
private void registry() {
eventCenter.registry(this, OrderCancelEvent.class);
}
@Override
public void trigger(OrderEvent event) {
log.info("取消订单,订单id={}", event.getOrderId());
}
}
复制代码
package com.glmapper.event.driven.listener;
/**
* 订单创建事件监听
* @author: Jerry
* @date: 2018/7/1
*/
@Slf4j
@Component
public class OrderCreateListener implements EventListener {
@Autowired
private EventCenter eventCenter;
@PostConstruct
private void registry() {
eventCenter.registry(this, OrderCreateEvent.class);
}
@Override
public void trigger(OrderEvent event) {
log.info("创建订单,订单id={}", event.getOrderId());
}
}
复制代码
@PostConstruct
相当于org.springframework.beans.factory.InitializingBean#afterPropertiesSet
的功能,在构造方法完成后会调用@PostConstruct
注解的 registry()
方法把自己注册到 EventCenter
事件中心。
重点类 EventCenter
事件中心
package com.glmapper.event.driven;
/**
* @author: Jerry
* @date: 2018/7/1
*/
public class EventCenter {
/**
* 事件类型和监听器的绑定映射
*/
private final ConcurrentHashMap<Class<?>, List<EventListener>> subscribers = new ConcurrentHashMap<>();
private final Executor executor;
public EventCenter(Executor executor) {
this.executor = executor;
}
/**
* 绑定 监听器与事件类型
*
* @param eventListener
* @param clazz
*/
public void registry(EventListener eventListener, Class<?> clazz) {
List<EventListener> listeners = subscribers.get(clazz);
if (listeners == null) {
listeners = new ArrayList<>();
}
listeners.add(eventListener);
subscribers.put(clazz, listeners);
}
/**
* 向事件中心发送消息
*
* @param orderEvent
*/
public void post(OrderEvent orderEvent) {
List<EventListener> listeners = subscribers.get(orderEvent.getClass());
if (listeners == null || listeners.size() == 0) {
throw new EventException("找不到该事件的监听器");
}
for (EventListener listener : listeners) {
//线程池异步处理
executor.execute(() -> listener.trigger(orderEvent));
}
}
}
复制代码
他的实例化在配置类里面
package com.glmapper.event.driven;
/**
* @author: Jerry
* @date: 2018/7/1
*/
@Slf4j
@SpringBootApplication
public class EventDrivenApplication {
public static void main(String[] args) throws InterruptedException {
ApplicationContext applicationContext = SpringApplication.run(EventDrivenApplication.class, args);
EventSender eventSender = applicationContext.getBean(EventSender.class);
while (true) {
long orderId = ThreadLocalRandom.current().nextLong();
eventSender.post(new OrderCreateEvent(orderId));
log.info("有一个新订单,订单id={}", orderId);
orderId = ThreadLocalRandom.current().nextLong();
eventSender.post(new OrderCancelEvent(orderId));
log.info("有一个订单取消,订单id={}", orderId);
Thread.sleep(ThreadLocalRandom.current().nextLong(1000, 10000));
}
}
@Bean
public EventCenter eventCenter() {
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("event-bus-%d").build();
int corePoolSize = Runtime.getRuntime().availableProcessors();
int maximumPoolSize = corePoolSize * 2;
// 创建一个线程池
Executor pool = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, 10L,
TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(1024),
namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
return new EventCenter(pool);
}
}
复制代码
系统启动后,事件发送器不停的向消息中心发送事件,事件中心再把事件委派给对应的监听器处理
其他类
剩下的就是事件发送器EventSender
、一个事件接口、两个具体的事件类
package com.glmapper.event.driven.sender;
/**
* @author: Jerry
* @date: 2018/7/1
*/
@Slf4j
@Component
public class EventSender {
@Autowired
private EventCenter eventCenter;
public void post(OrderEvent event) {
eventCenter.post(event);
}
}
===========================================分割线=================================================
package com.glmapper.event.driven.event;
/**
* @author: Jerry
* @date: 2018/7/1
*/
public interface OrderEvent {
Long getOrderId();
OrderEventType getEventType();
}
===========================================分割线=================================================
package com.glmapper.event.driven.event;
/**
* @author: Jerry
* @date: 2018/7/1
*/
public class OrderCreateEvent implements OrderEvent {
private Long orderId;
public OrderCreateEvent(Long orderId) {
this.orderId = orderId;
}
@Override
public Long getOrderId() {
return this.orderId;
}
@Override
public OrderEventType getEventType() {
return OrderEventType.CREATE;
}
}
===========================================分割线=================================================
package com.glmapper.event.driven.event;
/**
* @author: Jerry
* @date: 2018/7/1
*/
public class OrderCancelEvent implements OrderEvent {
private Long orderId;
public OrderCancelEvent(Long orderId) {
this.orderId = orderId;
}
@Override
public Long getOrderId() {
return this.orderId;
}
@Override
public OrderEventType getEventType() {
return OrderEventType.CANCEL;
}
}
复制代码
剩下的这些类就很简单了,不解释。最后看下结果
当然了,这是一个简易的事件驱动实现,如果要在框架中实现必然还要考虑更多的因素如:事件中心定义异常处理器,用于消费方处理事件发生的异常等,本来准备考虑实现这些场景的,出于时间限制就。。。或许是太懒了?,那么推荐一个好用的事件驱动类 google guava
的 EventBus
,这是一个考虑非常完善的事件驱动实现了。
可能有人说了现在都用 MQ
了没人会用这个了,那么它存在必然有他存在的道理,说实话一些小型项目压根都不用 MQ
,就用这个 EventBus
就能够解决节约成本,话虽如此,为了便于扩展还是推荐 MQ
。MQ
是在应用外进行解耦、通过网络传输,EventBus
在应用内实现解耦、直接在同一个虚拟机中完成,不必考虑网络不可用的问题。EventBus
还是非常有学习参考意义的
终于写完了,感觉得睡到下午两三点了。。。。