Akka工具(二)—Event Bus

某一天

小明:你好,你是报刊工作人员吗?

报刊:是的,请问,你有什么需要吗?

小明:我想订阅报刊,关于娱乐、经济、社会类的,其它的我不想要,可以吗?

报刊:可以的,并且你可以随时取消订阅。

小明:好的,谢谢你。祝你工作愉快!

通常该过程我们都是在软件上实现的,但是这并不影响我们要说明的内容。实际项目中,业务是复杂多样的,一个业务可能需要依赖另外一个业务,同时也可能取消和该业务的关联,如果我们不做好处理,各业务耦合度将会大大提高,不利于我们扩展和维护。怎样才能解耦这些依赖关系呢?通过上述小故事,大家是不是想到了什么呢,某个设计模式?没错,我们要使用的就是“发布-订阅”模式,使用该模式,订阅者可以订阅自己需要的东西,并且可以随时取消订阅,在这个过程中,发布者不知道订阅者是谁。

事件总线

Akka系统中,是不是需要我们自己去实现“发布-订阅”模式呢?答案肯定是no,Akka系统提供了一种事件总线(Event Bus)工具,内部实现了发布-订阅,通过使用它,我们就可以实现业务之间的解耦。

事件总线,Akka定义为EventBus类型,拥有发布、订阅、取消订阅等功能。处理过程:发布者将Event发布到EventBus上,订阅者将会接受到需要的消息,通常来讲,订阅者是一个Actor,通过createReceive方法接受消息。处理过程中,Classifier被用来描述事件分类,不同的事件有不同的订阅者,当然一个订阅者可以订阅多个事件,EventBus将会通过Classifier来选择订阅者并向其发送消息。常见的Classifier有LookupClassification、SubchannelClassification、ScanningClassification等。

下面我们来看看LookupClassification类型(按照指定的实际类型匹配)的事件分类的EventBus如何实现。

首先定义一个Event实体,拥有事件类型和消息:

@AllArgsConstructor
public class Event {
    @Getter
    private final String type;
    @Getter
    private final String message;
}

实体定义使用lombok,一个第三方依赖,可以方便我们开发,减少get、set等重复工作,如果没有使用过,大家可以在网上搜索学习一下,这里不做过多介绍。

继承EventBus,实现发布、订阅、分类等相关方法:

public class MyLookupEventBus extends LookupEventBus<Event,ActorRef,String> {
    //期望的classify数,一般为2的n次幂
    @Override
    public int mapSize() {
        return 8;
    }

    @Override
    public int compareSubscribers(ActorRef a1, ActorRef a2) {
        return a1.compareTo(a2);
    }

    @Override
    public String classify(Event event) {
        return event.getType();
    }

    @Override
    public void publish(Event event, ActorRef actorRef) {
            actorRef.tell(event.getMessage(),ActorRef.noSender());
    }
}

使用EventBus通常需要三个泛型,分别为指定事件类型、订阅者类型、Classifier类型。当EventBus发布Event,将会通过classify方法来选择目标订阅者,然后使用publish方法通知他们。

定义EventBusActor,作为订阅者:

public class EventBusActor extends AbstractActor {
     @Override public Receive createReceive() { 
        return receiveBuilder().matchAny(other -> {
            System.out.println("接受的消息:" + other);
     }).build(); 
} }

下面就使用EventBus.subscribe方法订阅所需事件,如下:

        ActorSystem system = ActorSystem.create("system");
        ActorRef eventActor = system.actorOf(Props.create(EventBusActor.class), "eventActor");
        //定义消息总线
        MyLookupEventBus bus = new MyLookupEventBus();
        //eventActor订阅add、update消息
        bus.subscribe(eventActor, "add");
        bus.subscribe(eventActor, "update");

        //发布add消息
        bus.publish(new Event("add", "insert object"));

        //取消订阅
        bus.unsubscribe(eventActor, "update");

        //发布update消息
        bus.publish(new Event("update", "update object"));

测试代码中,eventActor定义了add和update事件,但是它只会收到add事件消息,因为bus在发布update事件消息时,我们已经取消了update事件订阅,所以不会收到update object消息。调用subscribe方法,该方法会在EventBus内部维护一个Map结构,key为classfiy,也就是事件类型,value为订阅者列表,在订阅的过程中,eventBus使用compareSubscribers方法做排序比较。采用事件总线方式,我们可以订阅自己需要的事件,不会收到无关消息,结构清晰,并且可以随时取消订阅,降低代码耦合度。

事件流

事件流是ActorSystem上下文环境中的事件总线,使用它可以很方便的将“发布-订阅模式”用于任意事件类型,可以是自定义类型,也可以是死信(Dead Letter)或者日志(log messages)。订阅者通常都是Actor,当Actor终止时,会自动从订阅者列表移除。

下面我们定义一个订阅死信消息的Actor:

public class DeadLetterActor extends AbstractActor {
    @Override
    public Receive createReceive() {
        return receiveBuilder().match(DeadLetter.class, d -> {
            System.out.println("deadLetter:" + d);
        }).matchAny(o -> {
            System.out.println("其它消息:" + o);
        }).build();
    }
}

使用事件流非常简单,示例:

        ActorSystem system = ActorSystem.create("system");
        ActorRef deadLetterActor = system.actorOf(Props.create(DeadLetterActor.class), "deadLetterActor");
        system.eventStream().subscribe(deadLetterActor,DeadLetter.class);

DeadLetter.class方式只能订阅常规的死信提醒,DeadLetterSuppression消息除外,如果需要订阅这类死信提醒,我们可以使用

system.eventStream().subscribe(deadLetterActor,DeadLetterSuppression.class);

system.eventStream().subscribe(deadLetterActor,AllDeadLetters.class);

AllDeadLetters方式可以获取所有死信提醒消息。

EventStream实现了SubchannelClassification分类器,所以我们可以指定层次事件匹配,订阅一个父类下的所有事件。我们可以定义一个事件接口,定义两个子事件实现它,然后使用EventStream.subscibe定义父事件类型,大家可以自行尝试。

总结

事件总线,基于观察者模式思想,实现了发布-订阅的消息处理,发布者不需要关心订阅者具体是谁,订阅者可以随时取消订阅,在某种程度上降低了系统之间的耦合度,有利于扩展和维护系统。

猜你喜欢

转载自blog.csdn.net/p_programmer/article/details/84679474
今日推荐