event-bus

  Guava的EventBus是一个基于订阅的消息总线库。在某些业务场景用它来在单进程中替代RabbitMQ来做事件分发。 可以实现类似MQ类似的事件模型,但由于没有持久化,消息堆积能力取决于内存大小和线程池任务队列的配置。以下几点是在过去的使用中总结出来的一些最佳实践, 一般情况下只使用AsyncEventBus,除非必须要同步的场景:
  Listener: 包含handler方法的对象,监听一种或多种类型的Event。 - Handler: 处理某种类型Event的方法,使用@Subscribe注解。
  • 针对一个事件如果有多个任务,注册多个handler,使用不同的handler去处理相应的任务
  • handler里面尽量不要阻塞,以免过早消耗完线程池中的资源
  • handler 不要抛出异常,抛出的异常不会被处理,应该自己捕获异常自己处理
  • 利用继承来实现event的复用
  • 使用不同的event bus实例来隔离业务,关键业务使用专属的实例(即专属的线程池)
  EventBus隔离: 创建一个抽象listener,其他的listener实现getEventBusToRegister方法后拥有自动注册的能力。
public abstract class AbstractEventListener {
    @PostConstruct
    public void init(){
        for(EventBus eventBus: getRegisterBus()){
            eventBus.register(this);
        }
    }
    protected abstract Set<EventBus> getEventBusToRegister();
}

  针对某类别时间使用默认的EventBus

public abstract class BasePaymentEventListener extends AbstractEventListener{
    @Resource
    private EventBus paymentEventBus;
    @Override
    protected Set<EventBus> getEventBusToRegister() {
        return ImmutableSet.of(paymentEventBus);
    }
}

  全局统一的EventListener可以注册在所有EventBus上:

@Component
public class DeadEventHandler extends AbstractEventListener{
    private final static Logger logger = LoggerFactory.getLogger(DeadEventHandler.class);
    @Resource
    private EventBus eventBus1;
    @Resource
    private EventBus eventBus2;
    @Subscribe
    public void handlerDeadEvent(DeadEvent event){
        //handle
    }
    @Override
    protected Set<EventBus> getEventBusToRegister() {
        return ImmutableSet.of(eventBus1, eventBus2);
    }
}

  register方法底层实现

  如下图所示,步骤3中,会找到所有方法上有Subscribe注解的方法,在步骤6中,会判断方法上是否注解AllowConcurrentEvents,如果有,则返回Subscriber,如果没有则返回SynchronizedSubscriber。

 

   SynchronizedSubscriber和Subscriber的区别如下,SynchronizedSubscriber重复了父类的invokeSubscriberMethod,并加上了锁关键字synchronized

post方法底层实现

  

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));
          }
        }
      });
}

  可以看到,是将event和subscriber放入到ConcurrentLinkedQueue中,之后再从queue中poll出来,再调用subscribe的dispatchEvent方法。为什么先放到queue中,之后在poll出来,这是有考虑的,是为了应用整体的吞吐量考虑。

private final ConcurrentLinkedQueue<EventWithSubscriber> queue =
    Queues.newConcurrentLinkedQueue();

@Override
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);
  }
}

 

后续:https://my.oschina.net/realfighter/blog/361827

   https://my.oschina.net/realfighter/blog/406342



posted on 2020-01-18 19:21  溪水静幽  阅读(448)  评论(0)    收藏  举报