【问题标题】:Akka HTTP client and Akka actor performance tuningAkka HTTP 客户端和 Akka Actor 性能调优
【发布时间】:2019-03-04 10:10:04
【问题描述】:

我们从使用 Camel HTTP4 转移到 Akka HTTP,虽然我们现在能够更好地控制错误,但考虑到 Akka HTTP(客户端)中的所有可调整参数,要获得更好的性能变得非常困难。

我们有一个参与者接收消息,向外部服务发出 HTTP GET 请求(可以轻松管理超过 1500 RPS),然后以字符串形式的 http 响应正文进行响应。

我们现在的上限为 650 RPS,即使我们没有遇到错误(如以前使用 Camel 的 HTTP4),我们也无法超过这 650(与之前使用默认参数的 HTTP4 的 800 RPS 不同)。

我们的 HTTP 请求是使用 singleRequest 发出的:

val httpResponseFuture: Future[HttpResponse] = http.singleRequest(HttpRequest(uri = uri))

val tokenizationResponse = for {
  response <- httpResponseFuture
  body <- Unmarshal(response.entity).to[String]
} yield transformResponse(response.status, body, path, response.headers)

然后这些是产生最佳结果的设置(检查这些数字并没有显示任何真正的改进:

akka {

    actor.deployment {
      /HttpClient {
        router = balancing-pool
        nr-of-instances = 7
      }
    }

    http {
      host-connection-pool {
        max-connections = 30
        max-retries = 5
        max-open-requests = 8192
        pipelining-limit = 200
        idle-timeout = 30 s
      }
    }

}

我们尝试调整池的大小、actor 实例以及 host-connection-pool 下的所有其他参数,但我们无法做得更好。

欢迎提出任何建议!

【问题讨论】:

    标签: scala akka akka-http


    【解决方案1】:

    不要混搭并发

    大概您的查询功能只是向Actor 发送消息并等待回复:

    //what your code may look like now
    
    object Message
    
    val queryActorRef : ActorRef = ???
    
    val responseBody : Future[String] = (queryActorRef ? Message).mapTo[String]
    

    但这是不必要的。在这个用例中使用Actor 的唯一原因是保护有限的资源。但是底层的 http 连接池会为您处理资源利用。删除 Actor 中介将允许您单独使用 Futures:

    val entityTimeout : FiniteDuration = 10.seconds
    
    val responseBodyWithoutAnActor : Future[String] = 
        http
          .singleRequest(HttpRequest(uri = uri))
          .flatMap(response => response.entity.toStrict(timeout))
          .map(_.data.utf8String)
    

    如果发送给 Actor 的“消息”有一个潜在的来源,例如Iterable,那么您可以使用流式传输:

    type Message = ???
    
    val messagesSource : Iterable[Message] = ???
    
    val uri : String = ???
    
    val poolClientFlow = Http().cachedHostConnectionPool[Promise[HttpResponse]](uri)
    
    val entityParallelism = 10
    
    Source
      .apply(messagesSource)
      .via(poolClientFlow)
      .mapAsync(entityParallelism)(resp.entity.toStrict(entityTimeout).data.utf8String)
      .runForeach { responseBody : String =>
        //whatever you do with the bodies
      }
    

    【讨论】:

    • 我实际上是在 Actor 内部解析 Future。我不知道这是否是最好的做法。我目前正在使用类似:def receive = case request: Message =&gt; httpRequest(request).PipeTo(sender)。删除演员可能会对应用程序产生巨大影响,这就是我没有选择这种方式的原因。
    • @FedeE。您不能在保持业务逻辑的同时轻松更换并发框架,这一事实也是一种“反模式”。 Actor 应该是围绕普通业务功能的层。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-04-11
    • 2019-12-19
    • 1970-01-01
    • 1970-01-01
    • 2023-04-02
    • 2017-01-11
    • 1970-01-01
    相关资源
    最近更新 更多