简体   繁体   English

Akka:参与者之间沟通和处理特殊状态(无错误)

[英]Akka: communicating and handling special state (non-error) between actors

I am brand new to Akka (Java lib, v2.3.9) and am trying to understand actor dependency and fallback/fault tolerance. 我是Akka的新手(Java lib,v2.3.9),并试图了解参与者的依存关系和后备/容错能力。

Say I have two actors, StormTrooper and DarthVader : 假设我有两个演员, StormTrooperDarthVader

// Groovy pseudo-code
class StatusReport {
    private final Report report

    StatusReport(Input report) {
        super()
        this.report = deepClone(report)
    }

    Input getReport() {
        deepClone(this.report)
    }
}

class StormTrooper extends UntypedActor {
    ActorRef lordVader  // Injected with reference to DarthVader

    @Override
    void onReceive(Object message) {
        if(message instanceof PerformReport) {
            PerformReport pr = message as PerformReport
            Report report = ReportUtils.generateReport(pr.config)
            StatusReport statusReportMsg = new StatusReport(report)
            lordVader.tell(statusReportMessage, ...)
        }
    }
}

class DarthVader extends UntypedActor {
    @Override
    void onReceive(Object message) {
        if(message instanceof StatusReport) {
            // Do something meaningful with the status report.
        }
    }
}

Under some circumstances, DarthVader is essentially NULL and should be a no-op. 在某些情况下, DarthVader本质上为NULL,并且应该为空。 That is: when StormTrooper decides to send a StatusReport message to DarthVader , he: 即:当StormTrooper决定将StatusReport消息发送给DarthVader ,他:

  1. Might have been configured to be alive and functional, in which case DarthVader will correctly respond to the status report; 可能已经配置为可以运行并且可以运行,在这种情况下, DarthVader将正确响应状态报告; or 要么
  2. The user may have taken action (via configuration) such that DarthVader must be intentionally offline/unresponsive/no-op 用户可能已采取措施(通过配置),使得DarthVader必须故意脱机/无响应/无操作

In the latter case when DarthVader is supposed (I emphasize this to distinguish this from a use case when DarthVader is supposed to be alive/functioning but is in a faulty/error state) to be no-op, I'm not sure how to communicate that back to StormTrooper , who must simply call fizzBuzz#derp() if DarthVader is no-op. 在后一种情况下,当假设使用DarthVader时(我强调这点是为了与在DarthVader应该处于活动状态/功能正常但处于故障/错误状态的用例区别开)时,我不确定如何将其传达给StormTrooper ,如果DarthVader是no-op,则必须简单地调用fizzBuzz#derp()

Solution #1: State-based no-op detection 解决方案1:基于状态的无操作检测

class StormTrooper extends UntypedActor {
    ActorRef lordVader  // Injected with reference to DarthVader

    @Override
    void onReceive(Object message) {
        if(message instanceof PerformReport) {
            if(lordVader.isNoOp) {
                fizzBuzz.derp()
            } else {
                PerformReport pr = message as PerformReport
                Report report = ReportUtils.generateReport(pr.config)
                StatusReport statusReportMsg = new StatusReport(report)
                lordVader.tell(statusReportMessage, ...)
            }
        }
    }
}

class DarthVader extends UntypedActor {
    boolean isNoOpMode = false

    @Override
    void onReceive(Object message) {
        if(message instanceof StatusReport) {
            if(!isNoOpMode) {
                // Do something meaningful with the status report.
            }

            // Obviosuly, if we are in no-op mode, do nothing.
        }
    }
}

My uncertainty here is that ALL instances of DarthVader actors/threads must be in the same state (no-op mode being on/off applies universally to all of them), and so I'm not sure if this solution is even viable of in keeping with Akka best practices. 我的不确定性是DarthVader actor /线程的所有实例都必须处于同一状态(打开/关闭no-op模式普遍适用于所有实例),因此我不确定该解决方案是否适用于遵守Akka最佳做法。

Solution #2: Throw specialized exception 解决方案2:引发专门的异常

class StormTrooper extends UntypedActor {
    ActorRef lordVader  // Injected with reference to DarthVader

    @Override
    void onReceive(Object message) {
        if(message instanceof PerformReport) {
            try {
                PerformReport pr = message as PerformReport
                Report report = ReportUtils.generateReport(pr.config)
                StatusReport statusReportMsg = new StatusReport(report)
                lordVader.tell(statusReportMessage, ...)
            } catch(DarthVaderNoOpException dvnoExc) {
                fizzBuzz.derp()
            }
        }
    }
}

class DarthVader extends UntypedActor {
    boolean isNoOpMode = false

    @Override
    void onReceive(Object message) {
        if(message instanceof StatusReport) {
            if(!isNoOpMode) {
                // Do something meaningful with the status report.
            } else {
                throw new DarthVaderNoOpException()
            }
        }
    }
}

But using exceptions to control flow is a general no-no, and may even trigger built-in Akka supervisor behavior (reacting to exceptions may cause Akka to restart StormTrooper , etc.). 但是使用异常来控制流是通常的StormTrooper ,甚至可能触发内置的Akka管理员行为(对异常进行反应可能会导致Akka重新启动StormTrooper等)。

Solution #3: Send a response message back 解决方案#3:发回响应消息

class StormTrooper extends UntypedActor {
    ActorRef lordVader  // Injected with reference to DarthVader

    @Override
    void onReceive(Object message) {
        if(message instanceof PerformReport) {
            PerformReport pr = message as PerformReport
            Report report = ReportUtils.generateReport(pr.config)
            StatusReport statusReportMsg = new StatusReport(report)
            lordVader.tell(statusReportMessage, ...)
        } else if(message instanceof DarthVaderNoOp) {
            fizzbuzz.derp()
        }
    }
}

class DarthVader extends UntypedActor {
    ActorRef stormTrooper
    boolean isNoOpMode = false

    @Override
    void onReceive(Object message) {
        if(message instanceof StatusReport) {
            if(!isNoOpMode) {
                // Do something meaningful with the status report.
            } else {
                DarthVaderNoOp noOpMsg = new DarthVaderNoOp()
                stormTrooper.tell(noOpMsg, ...)
            }
        }
    }
}

But this seems like a cumbersome, chatty solution. 但这似乎是一个麻烦的,健谈的解决方案。

So I ask: what's the best way for DarthVader to indicate to StormTrooper that it's in no-op mode, such that StormTrooper knows to call fizzBuzz.derp() ? 所以我问:什么是最好的方式DarthVader来指示StormTrooper ,它在无操作模式,使得StormTrooper知道调用fizzBuzz.derp() Remember that if DarthVader is in no-op mode, all instances/actors/threads of DarthVader are in no-op mode, not just one particular instance. 请记住,如果DarthVader是在无操作模式,所有实例/演员/线程DarthVader是无操作模式,不只是一个特定的实例。

There is few possible solutions. 几乎没有解决方案。 First, you want to read if DarthVader is in NoOp mode from config then actor is created (Typesafe Config will work fine). 首先,您想从配置读取DarthVader是否处于NoOp模式,然后创建actor(Typesafe Config可以正常工作)。

Config vader = conf.getConfig("vader");
bool isNoOpMode = vader.getBoolean("state");

So you can set it universaly for the application. 因此,您可以为应用程序设置通用性。

For DarthVader himself, as you said, you can do chatty solution (respond to StormTrooper ), or utilize your first approach with conjuction with Ask pattern. 正如您所说,对于DarthVader本人而言,您可以进行健谈解决方案(响应StormTrooper ),或者将第一种方法与Ask模式结合使用。 You will send report to DarthVader and will return Future which will await for DarthVader response. 您将把报告发送给DarthVader ,然后返回Future,等待DarthVader回应。

Timeout timeout = new Timeout(Duration.create(5, "seconds"));
Future<Object> future = Patterns.ask(lordVader, statusReportMsg, timeout);

Please note, you do not want to call Await method, but to handle response inside onComplete , for example: 请注意,您不想调用Await方法,而是在onComplete内部处理响应,例如:

final ExecutionContext ec = system.dispatcher();

future.onComplete(new OnComplete<VaderResponse>() {
  public void onComplete(Throwable failure, VaderResponse result) {
    if (failure != null) {
      // Derp
    } else {
      // Report acknowledged
    }
  }
}, ec);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM