Google guava 事件总线 EventBus
Google guava 和 Apache commons类似,都是对java语言和类库进行增强的一个扩展库。
guava的诞生是谷歌的java工程师们从日常的工作中提取出的很多高效率的方法集合。
Google guava 类库中有一个事件总线组件(EventBus),它提供的消息发布-订阅类库实现了观察者设计模式可以方便的将业务代码方便的解耦。
比如我们可以通过EventBus可以快速的在建立一个进程内消息队列的方案。因为EventBus是维护在内存中的一个消息队列,所以不支持跨进程使用。
当然,如果你需要的是一个跨进程、分布式的消息队列应该直接采用中间件(RabbitMQ、kafka等)
EventBus分为同步和异步(AsyncEventBus)。
当一个EventBus发送的一个事件在当前事件总线中没有任何一个监听者可以处理,那么这个事件会被重新包装成DeadEvent重新发送。
创建事件总线流程
事件总线涉及到的概念有:
事件消息生产者
事件消息
事件监听者(可以理解为事件消息消费者)
事件处理句柄
创建一个新的事件总线的处理流程为:
- 选择或者创建一个事件消息类
- 创建一个事件监听类
- 如果你将要使用的是异步事件总线在此时需要创建一个Executor对象(如 线程池)
- 新建一个事件处理总线(EventBus、AsyncEventBus)
- 注册事件监听者(可以是多个)
- 发送事件消息
- 解绑事件监听者
码代码
引入依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.2-jre</version>
</dependency>
一个简单的事件处理
监听者
一个Listener类中可以定义多个subscribe方法
同在一个Listener类里声明的Subscribe方法根据相同或者不相同的形参会有不一样的反应;
/**
* 事件监听类
*/
@Slf4j
class OrderEventListener{
/**
* @Subscribe 监听事件的处理方法;监听的事件对象类型可以是继承自Object的任何类。
* @AllowConcurrentEvents 是在ASyncEventBus(异步事件总线中必要的),如果是EventBus则不用加上此注解
* @param msg
*/
@AllowConcurrentEvents
@Subscribe
public void eventFunc(JSONObject msg) {
log.debug("*************" + this.getClass().getCanonicalName()+"************************");
log.info("on message {} ", msg.toJSONString());
}
}
创建事件生产者总线、注册事件监听者、发送事件
@GetMapping("startEvent")
public void testProc2(){
log.info( "test starting ... ");
// Creates a new EventBus with the given identifier.
EventBus eventBus = new EventBus("orderEventBus");
// Registers all subscriber methods on object to receive events.
eventBus.register(new OrderEventListener());
// 新建一个事件消息对象
JSONObject msg = new JSONObject();
msg.put("orderNo", "orderNo000001");
msg.put("userNo", "ochsaofhdhfodhfos00");
// 咱们来使用一个线程去发送事件消息
Thread t1 = new Thread(()->{
int i = 0;
while (i < 100) {
i++;
try {
Thread.sleep(1000);
// 发送事件消息
eventBus.post(msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
}
运行结果
扩展
同时注册多个事件监听者,使用spring托管的接口类做监听者,DeadEvent使用。
多个事件监听者加DeadEvent处理
接口类
/**
* 事件处理接口
*/
public interface IEventListener{
public void eventFunc(JSONObject msg);
}
/**
* 事件监听类 扩展的
*/
@Component
@Slf4j
public class OrderEventListenerExt implements IEventListener {
/**
* @Subscribe 监听事件的处理方法;监听的事件对象类型可以是继承自Object的任何类。
* @AllowConcurrentEvents 是在ASyncEventBus(异步事件总线中必要的),如果是EventBus则不用加上此注解
* @param msg
*/
@Override
@AllowConcurrentEvents
@Subscribe
public void eventFunc(JSONObject msg) {
log.debug("*************************************");
log.info("on message {} ", msg.toJSONString());
}
}
DeadEvent监听
/**
* DeadEvent事件监听类(可选的)
* 当一个EventBus发送的一个事件在当前事件总线中没有任何一个监听者可以处理,那么这个事件会被重新包装成DeadEvent重新发送。
*/
@Slf4j
class DeadEventListener{
/**
* @Subscribe 监听事件的处理方法;监听的事件对象类型可以是继承自Object的任何类。
* @AllowConcurrentEvents 是在ASyncEventBus(异步事件总线中必要的),如果是EventBus则不用加上此注解
* @param msg
*/
@AllowConcurrentEvents
@Subscribe
public void eventFunc(DeadEvent msg) {
log.debug("*************" + this.getClass().getCanonicalName()+"************************");
log.info("on message {} ", msg.toString());
}
}
@Autowired
private IEventListener eventListener;
@GetMapping("startEvent2")
public void testProc4(){
log.info( "test starting ... ");
// Creates a new EventBus with the given identifier.
EventBus eventBus = new EventBus("orderEventBus");
// Registers all subscriber methods on object to receive events.
eventBus.register(eventListener);
eventBus.register(new OrderEventListener());
eventBus.register(new DeadEventListener());
// 新建一个事件消息对象
JSONObject msg = new JSONObject();
msg.put("orderNo", "orderNo000001");
msg.put("userNo", "ochsaofhdhfodhfos00");
// 咱们来使用一个线程去发送事件消息
Thread t1 = new Thread(()->{
int i = 0;
while (i < 100) {
i++;
try {
Thread.sleep(1000);
// 发送事件消息
eventBus.post(msg);
eventBus.post(new String("deadeventmessage"));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
}
运行结果
出结果可以看出:
事件总线中注册了n个对JSON类监听的对象,当你在发送一个JSON事件的时候这个事件会被处理n次。
如果生产者发送一个已注册的监听者处理不了的事件那么这个事件会被包装为DeadEvent重新发送。
异步事件总线
@GetMapping("startEventAsync")
public void testProc3(){
log.info( "test starting async event ... ");
/**
* 使用google guava async eventbus 我们需要一个线程池对象来管理事件处理线程
* 我们可以选择jdk或者spring的线程池类
*/
// JDK提供的线程池类
ThreadPoolExecutor threadPoolExecutor = new ScheduledThreadPoolExecutor(1);
threadPoolExecutor.setMaximumPoolSize(1);
threadPoolExecutor.setKeepAliveTime(1, TimeUnit.SECONDS);
threadPoolExecutor.allowCoreThreadTimeOut(false);
/**
* spring 提供的线程池类
ThreadPoolTaskExecutor threadPoolTaskExecutor = new org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(1);
threadPoolTaskExecutor.setMaxPoolSize(1);
threadPoolTaskExecutor.setQueueCapacity(100);
threadPoolTaskExecutor.setAllowCoreThreadTimeOut(true);
threadPoolTaskExecutor.setKeepAliveSeconds(1000);
threadPoolTaskExecutor.initialize();*/
// AsyncEventBus eventBus = new AsyncEventBus("orderEventBusAsync", threadPoolTaskExecutor);
AsyncEventBus eventBus = new AsyncEventBus("orderEventBusAsync", threadPoolExecutor);
// 不多说,依然是注册事件
// 不要忘记异步事件总线监听方法要加 @AllowConcurrentEvents
eventBus.register(new OrderEventListener());
// 咱们来使用一个线程去发送事件消息
Thread t1 = new Thread(()->{
int i = 0;
while (i < 100) {
i++;
try {
Thread.sleep(2000);
// 新建事件消息
JSONObject msg = new JSONObject();
msg.put("orderNo", "orderNo000001Async"+i);
msg.put("userNo", "ochsaofhdhfodhfos00");
eventBus.post(msg);
log.info("post message");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
}
}
运行结果
一个Listener类中定义多个subscribe
同一个Listener类里可以注册多个subscribe方法,此类只需要在eventbus注册一次即可,但是不同形参类型的subscribe还是要分开post消息;相应的同一形参类型的subscribe在eventbus.post一次会被相同类型的subscribe同时监听到。
listener类
/**
* 事件监听类
* 在 同一个listener中可以定义多个相同形参或者不同形参的subscribe
* 一样的形参类型的subscribe在eventbus.post一次会同时被监听到
*/
@Slf4j
class OrderEventListener{
/**
* @Subscribe 监听事件的处理方法;监听的事件对象类型可以是继承自Object的任何类。
* @AllowConcurrentEvents 是在ASyncEventBus(异步事件总线中必要的),如果是EventBus则不用加上此注解
* @param msg
*/
@AllowConcurrentEvents
@Subscribe
public void eventFunc(JSONObject msg) {
log.debug("*************" + this.getClass().getCanonicalName()+"************************");
log.info("on message {} ", msg.toJSONString());
}
/**
* 第二个subscribe 方法
* @param msg
*/
@AllowConcurrentEvents
@Subscribe
public void eventFunc2(Userinfo msg) {
log.debug("*************" + this.getClass().getCanonicalName()+"************************");
log.info("on 2222 proc message {} ", msg.toString());
}
}
EventBus定义和发送消息
@GetMapping("startEventAsync")
public void testProc3(){
log.info( "test starting async event ... ");
/**
* 使用google guava async eventbus 我们需要一个线程池对象来管理事件处理线程
* 我们可以选择jdk或者spring的线程池类
*/
// JDK提供的线程池类
ThreadPoolExecutor threadPoolExecutor = new ScheduledThreadPoolExecutor(1);
threadPoolExecutor.setMaximumPoolSize(1);
threadPoolExecutor.setKeepAliveTime(1, TimeUnit.SECONDS);
threadPoolExecutor.allowCoreThreadTimeOut(false);
/**
* spring 提供的线程池类
ThreadPoolTaskExecutor threadPoolTaskExecutor = new org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(1);
threadPoolTaskExecutor.setMaxPoolSize(1);
threadPoolTaskExecutor.setQueueCapacity(100);
threadPoolTaskExecutor.setAllowCoreThreadTimeOut(true);
threadPoolTaskExecutor.setKeepAliveSeconds(1000);
threadPoolTaskExecutor.initialize();*/
// AsyncEventBus eventBus = new AsyncEventBus("orderEventBusAsync", threadPoolTaskExecutor);
AsyncEventBus eventBus = new AsyncEventBus("orderEventBusAsync", threadPoolExecutor);
// 依然是注册事件,一个listener类中有多个subscribe方法只需要register一次即可
// 不要忘记异步事件总线监听方法要加 @AllowConcurrentEvents
eventBus.register(new OrderEventListener());
// 咱们来使用一个线程去发送事件消息
Thread t1 = new Thread(()->{
int i = 0;
while (i < 100) {
i++;
try {
Thread.sleep(1500);
// 新建事件消息
JSONObject msg = new JSONObject();
msg.put("orderNo", "orderNo000001Async"+i);
msg.put("userNo", "ochsaofhdhfodhfos00");
Userinfo newUserinfo =new Userinfo();
newUserinfo.setNickname("user-" + i);
// post 订单消息
eventBus.post(msg);
Thread.sleep(500);
// post 用户消息
eventBus.post(newUserinfo);
log.info("post message");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
}
}