背景
开发时遇到了一个场景,一个单机程序,在项目运行过程中,需要将一些操作解耦,异步依次执行,并且不需要持久化操作,程序重启后,重新基于最新的数据操作。
开发时,考虑过几个方案:
- MQ;
- Spring事件或者EventBus。
由于程序单机,并不复杂,再接入MQ过于庞大,增加了系统复杂度,并且也不需要消息持久化,所以就被pass掉了;
Spring事件和EventBus都是基于观察者模式,开发难度小,不会增加系统复杂度,但不能满足事件异步依次执行执行的需求。看了下EventBus的代码后,决定基于此修改。
依赖
这里使用 guava 提供的 EventBus 功能,所以需要以来 guava 包
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
EventBus 执行思路
EventBus
的事件方法方法:EventBus.post
public void post(Object event) {
// 获取所有监听器
Iterator<Subscriber> eventSubscribers = subscribers.getSubscribers(event);
if (eventSubscribers.hasNext()) {
// 执行所有监听器的方法
dispatcher.dispatch(event, eventSubscribers);
} else if (!(event instanceof DeadEvent)) {
// the event had no subscribers and was not itself a DeadEvent
post(new DeadEvent(this, event));
}
}
其中 dispatcher
在声明类时必须指定,AsyncEventBus
中,默认使用 LegacyAsyncDispatcher
类,该类是 EventBus
提供的异步执行器,其中 dispatch
方法如下:
void dispatch(Object event, Iterator<Subscriber> subscribers) {
checkNotNull(event);
while (subscribers.hasNext()) {
queue.add(new EventWithSubscriber(event, subscribers.next()));
}
EventWithSubscriber e;
while ((e = queue.poll()) != null) {
e.subscriber.dispatchEvent(e.event);
}
}
其中,主要逻辑是获取所有的监听器,依次调用 subscriber.dispathEvent
方法。
subscriber.dispathEvent
方法:
final void dispatchEvent(final Object event) {
executor.execute(
new Runnable() {
@Override
public void run() {
try {
invokeSubscriberMethod(event);
} catch (InvocationTargetException e) {
bus.handleSubscriberException(e.getCause(), context(event));
}
}
});
}
这里可以看到,最后都由 executor.execute
来执行。
这里的 executor
指线程池,即,异步 EventBus 最后都交由线程池来完成。
所以,想要设计成异步有序时,可以指定线程池 maxSize 为 1,从而保证依次执行。
所以,在声明 AsyncEventBus
时,需要指定自定义的 Executor
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EventConsume {
/**
* 事件标识
*/
String identifier();
}
public interface EventHandler<T> {
@Subscribe
boolean process(T data);
}
@Slf4j
public class BlockingPolicy implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
log.error("线程池[ {} ]等待队列已满,正在执行阻塞等待", executor.toString());
if (!executor.isShutdown()) {
try {
executor.getQueue().put(r);
} catch (Exception e) {
log.error("阻塞策略异常", e);
}
}
}
}
public class EventThreadPoolFactory {
private static final int DEFAULT_CORE_SIZE = 0;
private static final int DEFAULT_MAX_SIZE = 1;
private static final int DEFAULT_TIMEOUT = 1;
private static final TimeUnit DEFAULT_TIME_UNIT = TimeUnit.HOURS;
private static final int DEFAULT_QUEUE_SIZE = 5000;
private static final BlockingQueue<Runnable> DEFAULT_WORK_QUEUE = new ArrayBlockingQueue<>(DEFAULT_QUEUE_SIZE);
public static Executor buildDefaultExecutor(String identifier) {
return new ThreadPoolExecutor(DEFAULT_CORE_SIZE,
DEFAULT_MAX_SIZE,
DEFAULT_TIMEOUT,
DEFAULT_TIME_UNIT,
DEFAULT_WORK_QUEUE,
ThreadFactoryBuilder.create().setNamePrefix(String.format("%s-", identifier)).build(),
new BlockingPolicy());
}
}
@Component
@Slf4j
public class EventHub {
private final Map<String, AsyncEventBus> eventBusMap = new ConcurrentHashMap<>();
@Autowired
private ApplicationContext applicationContext;
@PostConstruct
public void onStart() {
this.loadEventHandler();
}
public void loadEventHandler() {
Map<String, EventHandler> eventHandlerMap = applicationContext.getBeansOfType(EventHandler.class);
for (EventHandler eventHandler : eventHandlerMap.values()) {
this.register(eventHandler);
}
}
private void register(EventHandler eventHandler) {
EventConsume eventConsume = eventHandler.getClass().getAnnotation(EventConsume.class);
if (eventConsume == null || StrUtil.isBlank(eventConsume.identifier())) {
log.error("EventHandler[ {} ]没有配置 identifier ,注册失败", eventHandler.getClass().getSimpleName());
return;
}
String identifier = eventConsume.identifier();
AsyncEventBus eventBus = eventBusMap.get(identifier);
if (eventBus == null) {
AsyncEventBus asyncEventBus = new AsyncEventBus(identifier, EventThreadPoolFactory.buildDefaultExecutor(identifier));
eventBus = ObjectUtil.defaultIfNull(eventBusMap.putIfAbsent(identifier, asyncEventBus), asyncEventBus);
}
eventBus.register(eventHandler);
}
public EventBus getEventBus(String identifier) {
return eventBusMap.get(identifier);
}
}
@Component
@Slf4j
public class EventProducer {
@Autowired
private EventHub eventHub;
public <T> void post(String identifier, T data) {
EventBus eventBus = eventHub.getEventBus(identifier);
if (eventBus == null) {
log.error("identifier [ {} ] 没有事件监听者", identifier);
return;
}
eventBus.post(data);
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。