事件驱动,Do you know?

个人理解: 事件驱动(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的实现,老套路,跟着上面的流程分析走:

  1. 系统启动,监听器把自己注册到事件中心,与某种事件进行绑定
  2. 事件发送器发送事件到事件中心,事件中心去查找与处理该事件的监听器
写一个监听器接口EventListener 和两个实现类OrderCancelListenerOrderCreateListener
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 guavaEventBus ,这是一个考虑非常完善的事件驱动实现了。

可能有人说了现在都用 MQ 了没人会用这个了,那么它存在必然有他存在的道理,说实话一些小型项目压根都不用 MQ,就用这个 EventBus 就能够解决节约成本,话虽如此,为了便于扩展还是推荐 MQMQ 是在应用外进行解耦、通过网络传输,EventBus在应用内实现解耦、直接在同一个虚拟机中完成,不必考虑网络不可用的问题。EventBus还是非常有学习参考意义的

终于写完了,感觉得睡到下午两三点了。。。。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值