【问题标题】:akka http: Akka streams vs actors to build a rest serviceakka http:Akka 流与演员建立休息服务
【发布时间】:2025-11-23 20:05:02
【问题描述】:

当谈到在 akka http 上创建具有 60 多个 API 的 REST Web 服务时。我如何选择应该使用 akka 流还是使用 akka 演员? 在他的post 中,Jos 展示了两种在 akka http 上创建 API 的方法,但他没有说明我应该何时选择其中一种。

【问题讨论】:

    标签: scala akka akka-stream akka-http


    【解决方案1】:

    这是一个难题。显然,这两种方法都有效。所以在某种程度上,这是一个品味/熟悉的问题。所以接下来的一切都只是我个人的看法。

    如果可能,我更喜欢使用 akka-stream,因为它具有更高级别的性质和类型安全性。但这是否是一种可行的方法在很大程度上取决于 REST API 的任务。

    Akka 流

    如果您的 REST API 是一个服务,例如根据外部数据(例如货币汇率 API)回答问题,最好使用 akka-stream 来实现。

    akka-stream 更可取的另一个示例是某种数据库前端,其中 REST API 的任务是解析查询参数,将它们转换为数据库查询,执行查询并根据内容转换结果- 用户请求的类型。在这两种情况下,数据流都可以轻松映射到 akka-stream 原语。

    演员

    如果您的 API 允许查询和更新集群上的多个持久性参与者,则可能更适合使用参与者。在这种情况下,纯基于 actor 的解决方案或混合解决方案(使用 akka-stream 解析查询参数和翻译结果,其余部分使用 actor)可能更可取。

    另一个基于参与者的解决方案可能更可取的示例是,如果您有一个 REST API 用于长时间运行的请求(例如 websockets),并且想要在集群上部署 REST API 本身的处理管道。我认为目前使用akka-stream根本不可能实现这样的事情。

    总结

    总结一下:查看每个 API 的数据流,看看它是否清晰地映射到 akka-stream 提供的原语。如果是这种情况,请使用 akka-stream 实现它。否则,请使用演员或混合解决方案实施。

    【讨论】:

    • 感谢 Rüdiger 的明确回答。我不喜欢将两者混合在一起,因为要获得背压,您需要依赖发布者/订阅者模型来处理您的演员。如果您想将请求向下发送到参与者层次结构,这会迫使您严重依赖 ask 模式而不是 tell。考虑到新参与者的微不足道的成本,每个请求的参与者模式很有价值。我还看到流作为底层子服务的聚合器工作得非常好,可以轻松地对子 API 进行分组。当您为 # 请求应该最少的移动设备进行开发时,这一点尤为重要。你怎么看?
    • 说实话,我尽量使用akka-stream,只有在有特殊部署或其他需求时才使用原始actors。我认为很多用例都很好地映射到 akka-stream。
    • 太棒了!顺便说一句,您能否提供一个简单的用例:“如果您的 API 允许查询和更新集群上的多个持久性参与者,那么参与者可能会更可取。”
    【解决方案2】:

    别忘了期货!

    我要对 Rudiger Klaehn 的好答案做的一个附录是还要考虑 Future 的用例。 Futures 的可组合性和 ExecutionContext 的资源管理使 Futures 成为许多(如果不是大多数)情况的理想选择。

    有一个很好的blog post 描述了Futures 何时是比Actors 更好的选择。此外,Streams 提供的背压带来了相当大的overhead

    仅仅因为您使用 akka-http 陷入困境并不意味着您的请求处理程序中的所有并发都必须限制在 Actor 或 Streams 中。

    路线

    Routetype definition 中固有地容纳期货:

    type Route = (RequestContext) ⇒ Future[RouteResult]
    

    因此,您可以仅使用函数和 Futures 将 Future 直接烘焙到您的 Route 中,而无需 Directives:

    val requestHandler : RequestContext => HttpResponse = ???
    
    val route : Route = 
      (requestContext) => Future(requestHandler(requestContext)) map RouteResult.Complete
    

    onComplete 指令

    onComplete 指令允许您在路线中“解开”未来:

    val route = 
      get {
    
        val future : Future[HttpResponse] = ???
    
        onComplete(future) {
          case Success(httpResponse) => complete(httpResponse)
          case Failure(exception)    => complete(InternalServerError -> exception.toString)
        }
      } 
    

    【讨论】:

      最近更新 更多