昨天想写个Event Driven模式,看了不少资料也成功写出了一个实现,先简单说下,以后细聊
Event Driven 事件驱动模式
- Event Bus 模式 传递消息的,复杂的是在主机间传递
- Event Driven 模式 处理消息的,通常与 Event Bus 工作在一起,复杂的是实现消息通信模式
- Reactor 模式 同 Event Driven 模式,只是关注 IO 事件
消息通信模式:
- 发布/订阅 模式 消息被发布到一个 address/topic,也就是说派发消息到所有注册在这个address/topic 的处理器去处理
- 点对点 模式 同上,不过消息只被派发到一个处理器上
- 请求/响应 模式 同点对点,不过处理器还能回一个消息给消息生产者,生产者能接受到这个消息
相关阅读:
- 掘金 - 一文理解Netty模型架构
- 大凡的博客 - 《Scalable IO in Java》译文
- Vert.x逐陆记 - 1.7.EventBus初探
- Vert.x Core Manual - The Event Bus
一个简单实现
代码命名和设计真的参考了Vert.X很多,先给一段测试结果
// 接口的静态工厂方法,总是返回同一个单例实现
EventBus eventBus = EventBus.bus();
AtomicInteger id = new AtomicInteger(1);
// 调用 eventbus api 来常见消费者,或者叫处理器
eventBus.<String>consumer("comment.post", msg -> {
System.out.println("消费者-1: 【消息已持久化到数据库】" + msg);
});
eventBus.<String>consumer("comment.post", msg -> {
System.out.println("消费者-2: 【你有一条新回复】" + msg.data());
});
// 通过返回的 MessageConsumer 引用,方便注销这个消费者
var c3 = eventBus.<Integer>consumer("comment.notify", msg -> {
System.out.println("消费者-3: 【点赞通知】已获得 " + msg.data() + " 个赞");
});
// 构建两个消息/事件生产者
MessageProducer<String> producer1 = eventBus.producer("comment.post");
MessageProducer<Integer> producer2 = eventBus.producer("comment.notify");
// 模拟一个事件/消息流
IntStream.range(1, 501).forEach( i -> {
producer1.write("已三连,谢谢UP主 #" + id.getAndIncrement());
producer2.write(i);
});
// 通过 eventbus 直接发消息
eventBus.<String>send("comment.post", "最后一层了!");
// 注销消费者/处理器
// c3.unregister();
接口和类设计
Acceptor.java
package com.onemsg.plan.eda;
/**
* Acceptor 事件/消息 接收者
*/
public interface Acceptor {
/**
* 开启事件循环
*/
void startEventLoop();
/**
* 接受一个消息
*/
void accept(Message<Object> event);
/**
* 关闭
*/
void close();
}
Dispatcher.java
package com.onemsg.plan.eda;
import java.util.Set;
/**
* 事件/消息 调度者
*/
public interface Dispatcher {
/**
* 派发一个事件给对应的处理器
* @param event 事件
*/
void dispatch(Message<Object> event);
/**
* 所有的消息地址/主题
* @return 消息地址集合
*/
Set<String> addresses();
/**
* 返回在指定消息地址注册的处理器集合
* @param address
* @return 处理器集合
*/
Set<MessageConsumer<Object>> consumeres(String address);
/**
* 注册新的事件消费者
* @param consumer
* @return true/false 表示成功或失败
*/
boolean addRegistration(MessageConsumer<Object> consumer);
/**
* 注销一个事件消费者
* @param <T>
* @param consumer
* @return true/false 是否注销成功
*/
<T> boolean removeRegistration(MessageConsumer<T> consumer);
void close();
}
EventBus.java
package com.onemsg.plan.eda;
import java.util.Map;
import com.onemsg.plan.eda.impl.EventBusImpl;
/**
* <p>事件总线</p>
* <p>一般作为主要的用户使用接口</p>
*/
public interface EventBus {
/**
* 向指定地址添加新的事件处理器,返回一个新的事件消费者
* @param <T>
* @param address
* @param handler
* @return 事件消费者
*/
<T> MessageConsumer<T> consumer(String address, Handler<Message<T>> handler);
/**
* 返回给定地址的新的时间消费者
* @param <T> 数据类型
* @param address
* @return 事件消费者
*/
<T> MessageConsumer<T> consumer(String address);
/**
* 向给定地址发送事件,包含数据
* @param <T> 数据类型
* @param address
* @param data 要发送的数据
*/
<T> void send(String address, T data);
/**
* 像给定地址发送事件,包含数据和消息头
* @param <T> 数据类型
* @param address
* @param data 要发送的数据
* @param headers 消息头
*/
<T> void send(String address, T data, Map<String, String> headers);
/**
* 发送一个事件,地址和数据不能为空,否则抛出异常
* @param <T> 数据类型
* @param event 事件
*/
<T> void send(Message<T> event) throws NullPointerException;
/**
* 返回给定地址的消息成产值
* @param <T>
* @param address
* @return
*/
<T> MessageProducer<T> producer(String address);
void close();
/**
* 返回一个单例 EventBus
*/
static EventBus bus(){
return EventBusImpl.instance();
}
}
Handler.java
package com.onemsg.plan.eda;
/**
* 事件/消息 处理器
* <p>这是一个函数式接口</p>
*/
@FunctionalInterface
public interface Handler<E> {
void handle(E event);
}
Message.java
package com.onemsg.plan.eda;
import java.util.Map;
/**
* 事件/消息
* @param <T> 数据类型
*/
public interface Message<T> {
/**
* @return 地址
*/
String address();
/**
* @return 消息头
*/
Map<String,String> headers();
/**
* @return 消息数据
*/
T data();
}
MessageConsumer.java
package com.onemsg.plan.eda;
/**
* 事件/消息 消费者
*/
public interface MessageConsumer<T>{
String address();
/**
* 传入事件处理器
* @param handler
*/
void handler(Handler<Message<T>> handler);
/**
* 判断此消费者是否注册到事件调度者上
* @return 是/否
*/
boolean isRegistered();
/**
* 从事件调度者上注销此消费者
*/
void unregister();
/**
* 消费事件
*/
void handle(Message<T> message);
void close();
}
MessageProducer.java
package com.onemsg.plan.eda;
import java.util.Map;
/**
* 事件/消息 生产者
*/
public interface MessageProducer<T> {
String address();
/**
* 写入新事件
* @param data 事件数据
*/
void write(T data);
/**
* 写入新事件
* @param data 事件数据
* @param headers 事件头
*/
void write(T data, Map<String,String> headers);
void close();
}
下面就是具体实现了
AcceptorImpl.java
package com.onemsg.plan.eda.impl;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import com.onemsg.plan.eda.Acceptor;
import com.onemsg.plan.eda.Dispatcher;
import com.onemsg.plan.eda.Message;
public class AcceptorImpl implements Acceptor {
private BlockingQueue<Message<Object>> queue;
private Dispatcher dispatcher;
private static final int DEFAULT_QUEUE_CAPACITY = 2000;
private boolean running = false;
private volatile boolean closed = false;
// private ThreadPoolExecutor executor;
public AcceptorImpl(Dispatcher dispatcher){
this(dispatcher, DEFAULT_QUEUE_CAPACITY);
}
public AcceptorImpl(Dispatcher dispatcher, int capacity){
this.dispatcher = dispatcher;
queue = new LinkedBlockingQueue<>(capacity);
}
public AcceptorImpl(Dispatcher dispatcher, BlockingQueue<Message<Object>> queue){
this.dispatcher = dispatcher;
this.queue = queue;
}
@Override
public void startEventLoop() {
synchronized (this) {
if(closed||running)
return;
}
running = true;
while( !closed ){
Message<Object> message = null;
try {
message = queue.take();
// System.out.println("take a message from queue");
} catch (InterruptedException e) {
continue;
}
dispatcher.dispatch(message);
}
}
@Override
public void accept(Message<Object> event) {
// System.out.println("queue accept a event");
queue.offer(event);
}
@Override
public void close(){
closed = true;
running = false;
dispatcher = null;
queue.clear();
}
}
DispatcherImpl.java
package com.onemsg.plan.eda.impl;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadPoolExecutor;
import com.onemsg.plan.eda.Dispatcher;
import com.onemsg.plan.eda.Message;
import com.onemsg.plan.eda.MessageConsumer;
public class DispatcherImpl implements Dispatcher {
private ConcurrentHashMap<String, Set<MessageConsumer<Object>>> router;
private ThreadPoolExecutor executor;
private volatile boolean closed = false;
public DispatcherImpl(ThreadPoolExecutor executor) {
router = new ConcurrentHashMap<>();
this.executor = executor;
}
public void dispatch(Message<Object> event) {
if(event == null || closed){
return;
}
if (router.containsKey(event.address())) {
consumeres(event.address()).forEach( consumer -> {
executor.execute( () -> {
consumer.handle(event);
});
} );
}
}
public Set<String> addresses() {
return router.keySet();
}
public Set<MessageConsumer<Object>> consumeres(String address) {
return router.get(address);
}
public boolean addRegistration(MessageConsumer<Object> consumer) {
final MessageConsumer<Object> c = consumer;
if (c == null) {
return false;
}
String address = c.address();
if (!router.containsKey(address)){
router.put(address, new HashSet<>() );
}
boolean r = router.get(address).add(c);
return r;
}
public <T> boolean removeRegistration(MessageConsumer<T> consumer) {
if (consumer == null || consumer.address() == null) {
return false;
}
return router.get(consumer.address()).remove(consumer);
}
public void close(){
closed = false;
router.clear();
router = null;
if(!executor.isShutdown()){
executor.shutdown();
}
}
}
EventBusImpl.java
package com.onemsg.plan.eda.impl;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import com.onemsg.plan.eda.Acceptor;
import com.onemsg.plan.eda.Dispatcher;
import com.onemsg.plan.eda.EventBus;
import com.onemsg.plan.eda.Handler;
import com.onemsg.plan.eda.Message;
import com.onemsg.plan.eda.MessageConsumer;
import com.onemsg.plan.eda.MessageProducer;
public class EventBusImpl implements EventBus{
private Acceptor acceptor;
private Dispatcher dispatcher;
private ThreadPoolExecutor executor;
public static final int DEAFULT_EXECTURE_QUEUE_SIZE = 2000;
public static final int DEAFULT_EVENT_LOOP_QUEUE_SIZE = 2000;
public EventBusImpl(){
this(DEAFULT_EXECTURE_QUEUE_SIZE, DEAFULT_EVENT_LOOP_QUEUE_SIZE);
}
public EventBusImpl(ThreadPoolExecutor executor){
this(executor, DEAFULT_EVENT_LOOP_QUEUE_SIZE);
}
public EventBusImpl(int executorQueueCapacity, int eventLoopQueueCapacity){
this(new ThreadPoolExecutor(4, 9, 20, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(executorQueueCapacity)),
eventLoopQueueCapacity);
}
public EventBusImpl(ThreadPoolExecutor executor, int eventLoopQueueCapacity) {
this.executor = executor;
dispatcher = new DispatcherImpl(this.executor);
acceptor = new AcceptorImpl(dispatcher, eventLoopQueueCapacity);
start();
}
void start(){
executor.execute( () -> acceptor.startEventLoop());
}
@Override
public <T> MessageConsumer<T> consumer(String address, Handler<Message<T>> handler) {
return new MessageConsumerImpl<>(address, dispatcher, handler);
}
@Override
public <T> MessageConsumer<T> consumer(String address) {
return new MessageConsumerImpl<>(address, dispatcher);
}
@Override
public <T> MessageProducer<T> producer(String address) {
return new MessageProducerImpl<>(address, acceptor);
}
@Override
public <T> void send(String address, T data) {
Objects.requireNonNull(address, "event address must not be null");
Objects.requireNonNull(data, "event data must not be null");
acceptor.accept(createMessage(address, data, Map.of()));
}
@Override
public <T> void send(String address, T data, Map<String, String> headers) {
Objects.requireNonNull(address, "event address must not be null");
Objects.requireNonNull(data, "event data must not be null");
acceptor.accept(createMessage(address, data, headers));
}
@Override
@SuppressWarnings("unchecked")
public <T> void send(Message<T> event) {
Objects.requireNonNull(event, "event must not be null");
Objects.requireNonNull(event.address(), "event address must not be null");
Objects.requireNonNull(event.data(), "event data must not be null");
acceptor.accept( (Message<Object>) event);
}
<T> Message<Object> createMessage(String address, T data, Map<String, String> headers) {
return new MessageImpl<Object>(address, data, headers);
}
private static class Holder{
static final EventBusImpl instance = new EventBusImpl();
}
public static EventBus instance(){
return Holder.instance;
}
@Override
public synchronized void close(){
acceptor.close();
dispatcher.close();
if(!executor.isShutdown()){
executor.shutdown();
}
}
}
MessageConsumerImpl.java
package com.onemsg.plan.eda.impl;
import java.util.UUID;
import com.onemsg.plan.eda.Dispatcher;
import com.onemsg.plan.eda.Handler;
import com.onemsg.plan.eda.Message;
import com.onemsg.plan.eda.MessageConsumer;
public class MessageConsumerImpl<T> implements MessageConsumer<T>{
private String uuid = null;
private volatile boolean registered;
private Handler<Message<T>> handler;
private String address;
private Dispatcher dispatcher;
public MessageConsumerImpl(String address, Dispatcher dispatcher){
this(address, dispatcher, null);
}
public MessageConsumerImpl(String address, Dispatcher dispatcher, Handler<Message<T>> handler){
uuid = UUID.randomUUID().toString();
this.address = address;
this.dispatcher = dispatcher;
this.handler = handler;
register();
}
@Override
public String address(){
return address;
}
@Override
public synchronized void handler(Handler<Message<T>> handler){
this.handler = handler;
}
@Override
public boolean isRegistered(){
return registered;
}
@Override
public synchronized void unregister(){
dispatcher.removeRegistration(this);
registered = false;
}
@Override
public void handle(Message<T> event) {
handler.handle(event);
}
@SuppressWarnings("unchecked")
void register(){
dispatcher.addRegistration((MessageConsumer<Object>) this);
registered = true;
}
public void close(){
unregister();
handler = null;
dispatcher = null;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((address == null) ? 0 : address.hashCode());
result = prime * result + ((uuid == null) ? 0 : uuid.hashCode());
return result;
}
@Override
@SuppressWarnings("unchecked")
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof MessageConsumerImpl))
return false;
MessageConsumerImpl<T> other = (MessageConsumerImpl<T>) obj;
if (address == null) {
if (other.address != null)
return false;
} else if (!address.equals(other.address))
return false;
if (uuid == null) {
if (other.uuid != null)
return false;
} else if (!uuid.equals(other.uuid))
return false;
return true;
}
}
MessageImpl.java
package com.onemsg.plan.eda.impl;
import java.util.Map;
import java.util.Objects;
import com.onemsg.plan.eda.Message;
public class MessageImpl<T> implements Message<T>{
private String address;
private Map<String,String> headers;
private T data;
public MessageImpl(String address, T data){
this(address, data, null);
}
public MessageImpl(String address, T data, Map<String,String> headers) {
this.address = Objects.requireNonNull(address);
this.data = Objects.requireNonNull(data);
this.headers = headers;
}
@Override
public String address() {
return address;
}
@Override
public T data() {
return data;
}
@Override
public Map<String, String> headers() {
return headers;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((address == null) ? 0 : address.hashCode());
result = prime * result + ((data == null) ? 0 : data.hashCode());
result = prime * result + ((headers == null) ? 0 : headers.hashCode());
return result;
}
@Override
@SuppressWarnings("unchecked")
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof MessageImpl))
return false;
MessageImpl<T> other = (MessageImpl<T>) obj;
if (address == null) {
if (other.address != null)
return false;
} else if (!address.equals(other.address))
return false;
if (data == null) {
if (other.data != null)
return false;
} else if (!data.equals(other.data))
return false;
if (headers == null) {
if (other.headers != null)
return false;
} else if (!headers.equals(other.headers))
return false;
return true;
}
@Override
public String toString() {
return "Message [address=" + address + ", data=" + data + ", headers=" + headers + "]";
}
}
MessageProducerImpl.java
package com.onemsg.plan.eda.impl;
import java.util.Map;
import com.onemsg.plan.eda.Acceptor;
import com.onemsg.plan.eda.Message;
import com.onemsg.plan.eda.MessageProducer;
public class MessageProducerImpl<T> implements MessageProducer<T>{
private String address;
private Acceptor acceptor;
public MessageProducerImpl(String address, Acceptor acceptor){
this.address = address;
this.acceptor = acceptor;
}
@Override
public String address() {
return address;
}
@Override
public void write(T data) {
acceptor.accept(createMessage(data, Map.of()));
}
@Override
public void write(T data, Map<String,String> headers) {
acceptor.accept(createMessage(data, headers));
}
@Override
public void close() {
address = null;
acceptor = null;
}
Message<Object> createMessage(T data, Map<String, String> headers){
return new MessageImpl<Object>(address, data, headers);
}
}