Akka消息投递以及Actor的生命周期

关于消息投递的一些说明

整个Akka应用是由消息驱动的,消息是除Actor之外最重要的核心组件。作为并发程序中的核心组件,在Actor之间传递的消息应该满足不可变性,也就是不变模式。因此可变的消息无法高效的在并发环境中使用。理论上Akka中的消息可以使用任何对象实例,但在实际使用中,强烈推荐使用不可变的对象

典型的不可变对象的实现

public  final class ImmutableMessage {
    private final int sequenceNumber;
    
    private final List<String> values;

    public ImmutableMessage(int sequenceNumber, List<String> values) {
        this.sequenceNumber = sequenceNumber;
        this.values = Collections.unmodifiableList(new ArrayList<String>(values));
    }

    public int getSequenceNumber() {
        return sequenceNumber;
    }

    public List<String> getValues() {
        return values;
    }
}

消息投递的三种策略

  1. 至多一次投递

在这种策略中,每一条消息最多会被投递一次。在这种情况下,可能偶尔会出现消息投递失败,而导致消息丢失。这种策略性能最高,成本最低。

  1. 至少一次投递

在这种策略中,每一条消息至少会被投递一次,直到成功为止。因此在一些偶然的场合,接受者可能会收到重复的消息,但不会发生消息丢失。这种策略需要保存消息投递的状态并不断充实。

  1. 精确的消息投递

也就是所有的消息保证被精确地投递并成功接受一次。既不会有丢失,也不会有重复接受。这种策略则是成本最高且最不容易实现的。
实际上,我们没有必要在Akka层保证消息的可靠性。这样做成本太高了,也是没有必要的。消息的可靠性更应该从应用的业务层去维护,因此也许在有些时候,丢失一些消息完全是符合应用要求的。

Akka的顺序性

对于消息投递Akka可以在一定程度上保证顺序性。比如,Actor A1向Actor A2顺序发送M1,M2和M3三条消息,Actor A3向 A2顺序发送M4,M5和M6三条消息,那么系统可以保证;

  1. 如果M1没有丢失,那它一定先于M2和M3被Actor A2收到。
  2. 如果M2没有丢失,那它一定先于M3被Actor A2收到。
  3. 如果M4没有丢失,那它一定先于M5和M6被A2收到。
  4. 如果M5没有丢失,那它一定先于M6被A2收到。
  5. 对于A2来说,来自A1和A3的消息可能交织在一起,没有顺序保证

这种消息投递规则不具备可传递性,比如A向C发送了M1,A向B发送了M2,B再将M2转发给C,那么C收到M1和M2的先后顺序是没有保证的。

Actor的生命周期

在这里插入图片描述

一个Actor再actorof()函数被调用后开始建立,实例创建后会回调preStart()方法。在这个方法里,我们可以进行一些资源的初始化工作。在Actor的工作过程中,可能会出现一些异常,这种情况下Actor需要重启,当Actor被重启时,会回调preRestart()方法,接着创建一个新的Actor对象实例(表示同一个Actor)。之后会回调postRestart()方法,表示启动完成,同时新的实例将会代替旧的实例。可以通过stop()方法或者发送一个PosionPill(毒药丸)停止Actor,停止时会调用postStop()方法,同时这个Actor的监视则会收到一个Terminated消息。

public class MyWorker extends UntypedAbstractActor {

    private final LoggingAdapter loggingAdapter= Logging.getLogger(getContext().system(),this);

    public static enum Msg{
        WORKONG,DONE,CLOSE;
    }

    @Override
    public void preStart() throws Exception {
        System.out.println("MyWorker is starting");
    }

    @Override
    public void postStop() throws Exception {
        System.out.println("MyWorker is stopping");
    }

    @Override
    public void onReceive(Object message) throws Throwable {
        if(message==Msg.WORKONG){
            System.out.println("I am working");
        } else if(message==Msg.DONE){
            System.out.println("Stop working");
        }else if(message==Msg.CLOSE){
            System.out.println("I will shutdown");
            getSender().tell(Msg.CLOSE,getSelf());
            getContext().stop(getSelf());
        }else{
            unhandled(message);
        }
    }
}

监听者

public class WatchActor extends UntypedAbstractActor {

    private final LoggingAdapter loggingAdapter= Logging.getLogger(getContext().system(),this);

    public WatchActor(ActorRef ref){
        getContext().watch(ref); //监视一个Actor
    }

    @Override
    public void onReceive(Object message) throws Throwable {
        if(message instanceof Terminated){
            System.out.println(String.format("%s has terminated,shutting down system",((Terminated) message).getActor().path()));
            return;
        }else {
            unhandled(message);
        }
    }

    public static void main(String[] args) {
        ActorSystem system=ActorSystem.create("deadwatch");
        ActorRef worker=system.actorOf(Props.create(MyWorker.class), "worker");
        system.actorOf(Props.create(WatchActor.class,worker),"watcher");
        worker.tell(MyWorker.Msg.WORKONG,ActorRef.noSender());
        worker.tell(MyWorker.Msg.DONE,ActorRef.noSender());
        worker.tell(PoisonPill.getInstance(),ActorRef.noSender());
    }
}

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值