Event Driven 模式介绍与简单实现

789 阅读6分钟

昨天想写个Event Driven模式,看了不少资料也成功写出了一个实现,先简单说下,以后细聊

Event Driven 事件驱动模式

  • Event Bus 模式 传递消息的,复杂的是在主机间传递
  • Event Driven 模式 处理消息的,通常与 Event Bus 工作在一起,复杂的是实现消息通信模式
  • Reactor 模式 同 Event Driven 模式,只是关注 IO 事件

消息通信模式:

  • 发布/订阅 模式 消息被发布到一个 address/topic,也就是说派发消息到所有注册在这个address/topic 的处理器去处理
  • 点对点 模式 同上,不过消息只被派发到一个处理器上
  • 请求/响应 模式 同点对点,不过处理器还能回一个消息给消息生产者,生产者能接受到这个消息

相关阅读:

一个简单实现

代码命名和设计真的参考了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);
    }
    
}