背景

开发时遇到了一个场景,一个单机程序,在项目运行过程中,需要将一些操作解耦,异步依次执行,并且不需要持久化操作,程序重启后,重新基于最新的数据操作。
开发时,考虑过几个方案:

  1. MQ;
  2. 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);
    }

}

代码笔耕
0 声望1 粉丝