我们从使用Camel HTTP4迁移到了Akka HTTP,尽管现在我们能够更好地控制错误,但是考虑到Akka HTTP(客户端)中的所有可调参数,要获得更好的性能确实变得非常困难。
我们有一个参与者,该参与者接收消息,向外部服务发出HTTP GET请求(可以轻松管理超过1500 RPS),然后以http响应主体作为String进行响应。
我们目前的最高RPS为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下所有其他参数的大小,但情况再好不过了。
欢迎提出任何建议!
答案 0 :(得分:2)
请勿混用并发并发
大概您的查询功能只是向Actor
发送消息并等待响应:
//what your code may look like now
object Message
val queryActorRef : ActorRef = ???
val responseBody : Future[String] = (queryActorRef ? Message).mapTo[String]
但这是不必要的。在此用例中使用Actor
的唯一原因是保护有限的资源。但是底层的http连接池可为您处理资源利用率。删除Actor中介将使您可以单独使用Future:
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
}