Akka设计负责人

qfe3c7zg  于 2022-11-06  发布在  其他
关注(0)|答案(1)|浏览(171)

当我在一个相当大的Akka应用程序上工作时,我遇到了一个非常简单的结构,当使用普通的方法和非Akka类时,但是当使用Akka时,这实际上是很难解决的,这就是为什么我来到这里,问你建议什么是解决这个问题的最好方法。
问题是这样的,我有一个父参与者,让我们称他为“连接器”,连接器有一个行为描述当它接收到一个ConnectCommand示例时它应该做什么。首先,它使用HttpClient提交一个表单,然后它转到几个URL来检查一些会话参数,最后向发送者(称为“消费者”)发送一个连接消息,其中包含它使用API所需的一切。
现在,我非常喜欢tell,而不是pull / ask,所以在我看来,实现这是一个很难的任务。让我们来回顾一下。HttpClientActor返回的所有响应都是一个Response示例,所以首先想到的是在我的actor中定义多个行为,并在连接过程的某个步骤完成后,逐渐地将行为更改为下一步。

private final PartialFunction<Object, BoxedUnit> inital = ReceiveBuilder
    .match(ConnectCommand.class, c -> this.startConnection())
    .matchAny(this::unhandled)
    .build();

private final PartialFunction<Object, BoxedUnit> stage1 = ReceiveBuilder
    .match(Response.class, this::stage1)
    .matchAny(this::unhandled)
    .build();

private final PartialFunction<Object, BoxedUnit> stage2 = ReceiveBuilder
    .match(Response.class, this::stage2)
    .matchAny(this::unhandled)
    .build();

private final PartialFunction<Object, BoxedUnit> stage3 = ReceiveBuilder
    .match(Response.class, this::stage3)
    .matchAny(this::unhandled)
    .build();

private final PartialFunction<Object, BoxedUnit> stage4 = ReceiveBuilder
    .match(Response.class, this::stage4)
    .matchAny(this::unhandled)
    .build();

private final PartialFunction<Object, BoxedUnit> stage5 = ReceiveBuilder
    .match(Response.class, this::stage5)
    .matchAny(this::unhandled)
    .build();

private final PartialFunction<Object, BoxedUnit> stage6 = ReceiveBuilder
    .match(Response.class, this::stage6)
    .matchAny(this::unhandled)
    .build();

private final PartialFunction<Object, BoxedUnit> stage7 = ReceiveBuilder
    .match(Response.class, this::stage7)
    .matchAny(this::unhandled)
    .build();

这样做的优点是使用tell,而不是ask,但主要的缺点是代码变得非常不可读。
现在我在这一点上,我觉得这个演员需要一些改变,在一个好的方式,但有两个选择,在我看来。
第一种方法涉及将每个HttpRequest和Response拆分为一个单独的Actor,并将结果聚合到Connector Actor中,使用tell,不应该影响性能,因为Akka是为处理大量演员而构建的。这样做的唯一缺点是,我需要为这些需要从Stage 5Actor传递到连接器参与者。这导致了大量的内存开销(如果我错了,请纠正我)。
第二种方法是使用Ask模式将步骤连接在一起。这将产生一个连接器参与者,因为Spray也为它的Http客户端这样做,我认为这可能是一个有效的解决方案。唯一的缺点是,因为所有东西都依赖于外部Http API,超时可能会成为一个问题。如果Akka团队推荐这种方法,如何处理所有完全不可预测的超时?
请注意,此实现需要能够使用监督策略,因为我们当前的整个方法都是基于此。
如果你觉得有比我提到的更好的解决方案,请告诉我!我真的很喜欢Akka a.t.m.,我得到的每一条建议,都是经验和知识的收获,不仅对我,而且对整个社区:D.另外,我认为这是一个越来越多的人偶尔遇到的问题。
提前感谢,也非常感谢Akka团队制作了如此令人敬畏的lib!
这个问题是第一次问Akka GitHub本身,但我决定张贴在这里,因为这是一个演员模型相关的问题,因为一个Akka相关的问题。
链接到GitHub上的问题:https://github.com/akka/akka/issues/16080

xurqigkl

xurqigkl1#

在我看来,你不一定需要N个阶段来收集响应,如果你知道你在等待多少个响应,你就可以在一个行为下收集所有的响应。
此外,在使用ask的任何场景中,都可以轻松地将其替换为一个中间Actor来保存此上下文,并通过tell传递所有消息。(好吧,对于整个请求,您应该仍然有一个超时时间),并且您可以将所有未完成的阶段捆绑到一个Actor中,而不是为每个ask额外添加一个Actor。
Jamie艾伦很好地描述了这个场景,如Effective Akka中的Extra and Cameo Patterns
因此,考虑到这一切,你也许可以遵循以下思路:

  • 当使用者将消息发送到连接器时,连接器可以为此请求上下文创建新的使用者(Cameo)。您必须在此使用者中捕获sender使用者。
  • Cameo Actor可以通过tell启动后续请求。此时,您可以让Cameo或连接器作为监督者,这样您的监督策略仍然可以按您的意愿工作。
  • Receive区块中的Cameo可以等待来自Connections的回应。这不一定是ask上的await。只要接受receive中的消息,然后更新您的内部状态即可。
  • 当所有连接完成后,您可以通过tell响应原始Consumer。

相关问题