Java知识分享
热爱技术,分享技术

Event Driven事件驱动设计模式

1. 事件驱动模式介绍

事件驱动设计模式是一种实现组件松耦合,易拓展的架构方式。一个简单的事件驱动设计需要包括三大组件。

  • Events:需要被处理的数据
  • Event Handlers:处理数据的方式方法
  • Event Loop :维护Events和Event Handlers之间的交互流程。
Event Driven事件驱动设计模式插图
事件驱动模式图解

2. 开发一个Event-Driven框架

今天我们设计一个迷你的EDA框架,该框架用来实现一个简单的聊天程序。

一个基础的事件驱动的架构设计,总体来讲会设计如下几个重要组件,事件消息Event、事件处理器Handler、接受事件消息的通道queue、以及对消息如何进行分配(Event Loop)。

2.1 同步EDA框架设计

2.1.1 Message

每一个Event可以称为Message、message是对Event更高一个层级的抽象,每一个Message都有一个特定的Type用于对应的Handler做关联。接口定义如下:

package cn.hackcloud.concurrency.eda;

public interface Message {
/**
* 返回message的类型
*/
Class<? extends Message> getType();
}

2.1.2 Channel

第二个比较重要的概念就是Channels,channel主要用于接收来自Event Loop分配的消息,每一个channel负责处理一种类型的消息(这取决于对消息如何进行分配),接口定义如下:

package cn.hackcloud.concurrency.eda;
public interface Channel<E extends Message> {
/**
* 负责消息的调度
*/
void dispatch(E message);
}

2.1.3 Dynamic Router

路由的作用类似于Event Loop ,主要作用帮助Event找到合适的Channel,并且传送给他。动态路由接口定义如下:

package cn.hackcloud.concurrency.eda;

public interface DynamicRouter<E extends Message> {
/**
* 针对Message类型注册相关的Channel,只有找到合适的Channel该Message才会被处理
*/
void registerChannel(Class<? extends E> messageType, Channel<? extends E> channel);

/**
* 为相应的Channel分配Message
*/
void dispatch(E message);
}

Router如何知道要将Message分配给哪个Channel呢?换句话说,Router 需要了解到Channel的存在,因此registerChannel)方法的作用就是将相应的Channel注册给Router,dispatch方法则是根据Message的类型进行路由匹配。

2.1.5 EventDispatcher

EventDispatcher是对DynamicRouter的一个最基本的实现,适合在单线程的情况下进行使用,因此不需要考虑线程安全的问题。EventDispatcher 接口的定义如下:

package cn.hackcloud.concurrency.eda;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 路由分发
* Created by laijun.vip
*/
public final class EventDispatcher implements DynamicRouter<Message> {
//用于保存Channel和Message之间的关系
private final Map<Class<? extends Message>, Channel> routerTable;

public EventDispatcher() {
//初始化路由表
this.routerTable = new ConcurrentHashMap<>();
}

@Override
public void registerChannel(Class<? extends Message> messageType, Channel<? extends Message> channel) {
this.routerTable.put(messageType, channel);
}

@Override
public void dispatch(Message message) {
if (routerTable.containsKey(message.getType())) {
//直接获取对应的Channel处理Message
routerTable.get(message.getType()).dispatch(message);
} else {
throw new MessageMatchException("can't match the channel for [" + message.getType() + "] type.");
}
}
}

在EventDispatcher中有一个注册表routerTable,主要用于存放不同类型Message对应的Channel, 如果没有与Message相对应的Channel, 则会抛出无法匹配的异常,示例代码

package cn.hackcloud.concurrency.eda;
public class MessageMatchException extends RuntimeException {
    public MessageMatchException(String message) {
        super(message);
    }
}

下面写一个简单的EDA实现代码如下:

package cn.hackcloud.concurrency.eda;

public class EventDispatcherExample {
//用于在Channel中的运算
static class InputEvent extends Event {
private final int x;
private final int y;

InputEvent(int x, int y) {
this.x = x;
this.y = y;
}

public int getX() {
return x;
}

public int getY() {
return y;
}
}

//用于存放结果
static class ResultEvent extends Event {
private final int result;

ResultEvent(int result) {
this.result = result;
}

public int getResult() {
return result;
}
}

static class ResultEventHandler implements Channel<ResultEvent> {

@Override
public void dispatch(ResultEvent message) {
System.out.println("The result is:" + message.getResult());
}
}

//InputEventHandler向Router发送Event,因此构造时需要传入Dispatcher.
static class InputEventHandler implements Channel<InputEvent> {
private final EventDispatcher dispatcher;

InputEventHandler(EventDispatcher dispatcher) {
this.dispatcher = dispatcher;
}

//将计算的结果构造成新的Event提交给Router
@Override
public void dispatch(InputEvent message) {
int result = message.getX() + message.getY();
dispatcher.dispatch(new ResultEvent(result));
}
}

public static void main(String[] args) {
EventDispatcher dispatcher = new EventDispatcher();
dispatcher.registerChannel(InputEvent.class, new InputEventHandler(dispatcher));
dispatcher.registerChannel(ResultEvent.class, new ResultEventHandler());
dispatcher.dispatch(new InputEvent(1, 2));
}
}

InputEvent 是一个Message,它包含了两个Int类型的属性,而InputEventHandler是对InputEvent消息的处理,接收到了InputEvent消息之后,分别对X和Y进行相加操作,然后将结果封装成ResultEvent提交给EventDispatcher, ResultEvent 相对比较简单,只包含了计算结果的属性,ResultEventHandler 则将计算结果输出到控制台上。


通过上面这个例子的运行你会发现,不同数据的处理过程之间根本无须知道彼此的存在,一切都由EventDispatcher 这个Router来控制,它会给你想要的一一切,这是一种稀疏耦合(松耦合)的设计。


EDA的设计除了松耦合特性之外,扩展性也是非常强的,比如Channel非常容易扩展和替换,另外由于Dispatcher统一负责Event的调配,因此在消息通过Channel之前可以进行很多过滤、数据验证、权限控制、数据增强(Enhance)等工作。

Event Driven事件驱动设计模式插图(1)
同步事件驱动架构类图
打赏
本站所有资源均来源于网络,仅供学习使用,请支持正版!Java技术开源 » Event Driven事件驱动设计模式

评论 抢沙发

评论前必须登录!

 

觉得文章有用就打赏一下文章作者

非常感谢你的打赏,我们将继续提供更多优质内容,让我们一起创建更加美好的网络世界!

支付宝扫一扫

微信扫一扫

登录

找回密码

注册