发送简单的 HTTP 请求并使用 Akka 获取响应的最佳做法是什么?
What's the best practice to send a simply HTTP request and get its response with Akka?
我试过这样编码:
val requestUri : Uri = baseUri.withQuery(Uri.Query("aid" -> av))//av is a query parameter
val responseFuture: Future[HttpResponse] =
Http(system).singleRequest(HttpRequest(GET, uri = requestUri))
val response = Await.result(responseFuture, Duration.Inf)
我想我得到了正确的响应,但是我怎样才能得到响应的内容呢?这样做的最佳做法是什么?这样使用Future
对吗?
一般来说,在 Future
上使用 Await
是一种糟糕的形式。它通常是不必要的,但如果您不熟悉 Futures,"feels" 喜欢正确的方法。
一旦结果作为 Future 出现,您就可以离开 Future 的计算 "inside" 并在原始值之上添加更多功能。
直接解决您的问题,您可以使用 Future 运算符而不是 Await 来 "get the content of the response"(请参阅 Frederic A. 关于在使用以下解决方案之前为什么应该考虑流的回答):
import scala.concurrent.duration._
val responseFuture : Future[HttpResponse] = ??? //same as in question
val responseBody : Future[ByteString] =
responseFuture
.map(_.entity)
.flatMap(_.toStrict(10 seconds))
.map(_.data)
这一系列操作也可以使用for comprehensions来完成:
val responseBody : Future[ByteString] =
for {
entity <- responseFuture.entity
strict <- entity.toStrict(10 seconds)
body <- strict.data
} yield body
如果你想在结果准备好时打印出来,你可以这样做:
responseBody foreach { body =>
println s"body: ${body.utf8String}"
}
请再次注意,我们能够将 HttpResponse
转换为 ByteString
并在不使用 Await 的情况下打印出结果。相反,我们使用 map
、flatMap
和 foreach
来完成任务。
我在第一次使用 Futures 时使用的一个心理技巧是将 Future 想象成一种特殊的数组。你不能select将元素移出Array,Await就是这样做的,但是当你想对元素进行操作时,你可以在特殊的Array上使用map和flatMap。
Akka http 基于 akka 流的原因有很多,可以帮助您了解这里发生的事情的原因如下:如果您的代码所在的计算机 运行 只有 64Gb 的 RAM,并且 HTTP 响应的大小是 784Tb,即,比您的 ram + swap 所能容纳的要多得多?流媒体解决方案是解决这个问题的唯一方法,而这正是 akka http 擅长的!
所以在你的情况下,当 responseFuture
完成时,基本上这意味着只有 http 响应的 headers 可用,而不是内容。请记住,内容可能很大,需要数小时才能下载,但您仍然可以预期 responseFuture
可以在不到一秒内完成。
你所说的响应内容称为http实体。 Akka http 将其作为流提供,但对于像您这样的情况,您可以在实体上调用 .toStrict
以将其作为(未来的)字节数组而不是流。
最后,使用 Await
几乎是不对的。您应该在 future 完成时采取行动,使用 .onComplete
/.onSuccess
/.onFailure
或将结果映射到某些东西,可能与其他 futures 使用 .map
and/or .flatMap
.
我试过这样编码:
val requestUri : Uri = baseUri.withQuery(Uri.Query("aid" -> av))//av is a query parameter
val responseFuture: Future[HttpResponse] =
Http(system).singleRequest(HttpRequest(GET, uri = requestUri))
val response = Await.result(responseFuture, Duration.Inf)
我想我得到了正确的响应,但是我怎样才能得到响应的内容呢?这样做的最佳做法是什么?这样使用Future
对吗?
一般来说,在 Future
上使用 Await
是一种糟糕的形式。它通常是不必要的,但如果您不熟悉 Futures,"feels" 喜欢正确的方法。
一旦结果作为 Future 出现,您就可以离开 Future 的计算 "inside" 并在原始值之上添加更多功能。
直接解决您的问题,您可以使用 Future 运算符而不是 Await 来 "get the content of the response"(请参阅 Frederic A. 关于在使用以下解决方案之前为什么应该考虑流的回答):
import scala.concurrent.duration._
val responseFuture : Future[HttpResponse] = ??? //same as in question
val responseBody : Future[ByteString] =
responseFuture
.map(_.entity)
.flatMap(_.toStrict(10 seconds))
.map(_.data)
这一系列操作也可以使用for comprehensions来完成:
val responseBody : Future[ByteString] =
for {
entity <- responseFuture.entity
strict <- entity.toStrict(10 seconds)
body <- strict.data
} yield body
如果你想在结果准备好时打印出来,你可以这样做:
responseBody foreach { body =>
println s"body: ${body.utf8String}"
}
请再次注意,我们能够将 HttpResponse
转换为 ByteString
并在不使用 Await 的情况下打印出结果。相反,我们使用 map
、flatMap
和 foreach
来完成任务。
我在第一次使用 Futures 时使用的一个心理技巧是将 Future 想象成一种特殊的数组。你不能select将元素移出Array,Await就是这样做的,但是当你想对元素进行操作时,你可以在特殊的Array上使用map和flatMap。
Akka http 基于 akka 流的原因有很多,可以帮助您了解这里发生的事情的原因如下:如果您的代码所在的计算机 运行 只有 64Gb 的 RAM,并且 HTTP 响应的大小是 784Tb,即,比您的 ram + swap 所能容纳的要多得多?流媒体解决方案是解决这个问题的唯一方法,而这正是 akka http 擅长的!
所以在你的情况下,当 responseFuture
完成时,基本上这意味着只有 http 响应的 headers 可用,而不是内容。请记住,内容可能很大,需要数小时才能下载,但您仍然可以预期 responseFuture
可以在不到一秒内完成。
你所说的响应内容称为http实体。 Akka http 将其作为流提供,但对于像您这样的情况,您可以在实体上调用 .toStrict
以将其作为(未来的)字节数组而不是流。
最后,使用 Await
几乎是不对的。您应该在 future 完成时采取行动,使用 .onComplete
/.onSuccess
/.onFailure
或将结果映射到某些东西,可能与其他 futures 使用 .map
and/or .flatMap
.