EventBus是Google.Guava提供的消息发布-订阅组件,采用的是观察者模式(生产/消费者编程模型),
实现了多组件之间通信的解耦合,事件产生方和事件消费方实现解耦分离,提升了通信的简洁性。
相比MQ它更加轻量,易用,但一般只用于进程内部的通信,不能像MQ一样用于分布式系统的服务与服务之间的通信。
EventBus包含3大核心部分:Event事件, Publisher事件发布者,Subscriber事件订阅者。
下面介绍一下它的使用方法:
一、在pom.xml中引入guava的依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
二、自定义事件类
事件是EventBus之间相互通信的基本单位,一个Event可以是任何类型。
为了规范,我们一般在自定义事件类时会以Event作为类名的结尾,例:
import lombok.Data;
/**
* 自定义一个添加告警消息的事件
*/
@Data
public class AddAlertMsgEvent {
private String calcNodeCode;
private String orgCode;
}
三、事件监听
事件监听即事件的订阅者,就是接收事件的一方,这些订阅者需要在自己的方法上,添加@Subscribe注解声明自己为事件订阅者。
一般@Subscribe可以配合@AllowConcurrentEvents注解一起使用,这个注解是用来标识当前订阅者是线程安全的,可以减少同步开销。
例:
/**
* 事件监听器
*/
@Component
public class EventListener {
@Subscribe
@AllowConcurrentEvents
public void addAlertMsg(AddAlertMsgEvent event) {
//在这里可以做你需要的处理
}
}
监听类里面可以写多个监听的方法,记得在方法上添加@Subscribe注解。
四、定义一个工具类用来获取EventBus和发送事件
事件发布者,就是发送事件到EventBus事件总线的一方,事件发布者调用Post()方法,将事件发给EventBus。
例:
import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.EventBus;
import java.util.concurrent.*;
public class EventBusUtil {
private static EventBus eventBus;
private static AsyncEventBus asyncEventBus;
private static Executor executor = new ThreadPoolExecutor(1, 10, 60,
TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(100000), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
/**
* 异步事件单例模式
* @return
*/
private static synchronized AsyncEventBus getAsyncEventBus() {
if(asyncEventBus == null){
asyncEventBus = new AsyncEventBus(executor);
}
return asyncEventBus;
}
/**
* 同步事件单例模式
* @return
*/
private static synchronized EventBus getEventBus() {
if(eventBus == null) {
eventBus = new EventBus();
}
return eventBus;
}
/**
* 同步发送事件
* @param event
*/
public static void post(Object event) {
getEventBus().post(event);
}
/**
* 异步发送事件
* @param event
*/
public static void asyncPost(Object event) {
getAsyncEventBus().post(event);
}
/**
* 监听器注册
* @param object
*/
public static void register(Object object) {
getEventBus().register(object);
getAsyncEventBus().register(object);
}
}
五、监听器(订阅者)注册
只有将监听器所在的类,注册到EventBus中,EventBus才能扫描到这个订阅者。
在项目启动的时候调用EventBusUtil.register(eventListener)即可,下面以我使用的SpringBoot项目为例:
@Order(1)
@Slf4j
@Component
@Configuration
public class ApplicationListenerImpl implements ApplicationListener<ApplicationReadyEvent> {
@Resource
private EventListener eventListener;
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
log.info("ApplicationReadyEvent init restTemplate.");
try {
//监听器注册
EventBusUtil.register(eventListener);
} catch (Exception e) {
log.error("初始化配置失败!", e);
}
log.info("ApplicationReadyEvent init restTemplates finished.");
}
}
六、使用EventBus
至此你就可以愉快的使用EventBus了,下面举个简单的例子:
//使用Eventbus添加告警信息
AddAlertMsgEvent addAlertMsgEvent = new AddAlertMsgEvent();
addAlertMsgEvent.setOrgCode(orgInfoEntity.getOrgCode());
addAlertMsgEvent.setCalcNodeCode(orgInfoEntity.getCalcNodeCode());
//发送异步事件
EventBusUtil.asyncPost(addAlertMsgEvent);
七、拓展:EventBus和AsyncEventBus使用区别
EventBus:同步事件总线
1.同步执行,事件发送方在发出事件之后,会等待所有的事件消费方执行完毕后,才会回来继续执行自己后面的代码。
2.事件发送方和事件消费方会在同一个线程中执行,消费方的执行线程取决于发送方。
3.同一个事件的多个订阅者,在接收到事件的顺序上面有不同。谁先注册到EventBus的,谁先执行,如果是在同一个类中的两个订阅者一起被注册到EventBus的情况,收到事件的顺序跟方法名有关。
AsyncEventBus:异步事件总线
1.异步执行,事件发送方异步发出事件,不会等待事件消费方是否收到,直接执行自己后面的代码。
2.在定义AsyncEventBus时,构造函数中会传入一个线程池。事件消费方收到异步事件时,消费方会从线程池中获取一个新的线程来执行自己的任务。
3.同一个事件的多个订阅者,它们的注册顺序跟接收到事件的顺序上没有任何联系,都会同时收到事件,并且都是在新的线程中,异步并发的执行自己的任务。