包含在 Future 中的同步 HTTP 请求是否被视为 CPU 或 IO 绑定?
Is synchronous HTTP request wrapped in a Future considered CPU or IO bound?
考虑以下两个片段,其中第一个使用 Future
包装 scalaj-http 请求,第二个使用 async-http-client
Sync 使用全局 EC
用 Future 包裹的客户端
object SyncClientWithFuture {
def main(args: Array[String]): Unit = {
import scala.concurrent.ExecutionContext.Implicits.global
import scalaj.http.Http
val delay = "3000"
val slowApi = s"http://slowwly.robertomurray.co.uk/delay/${delay}/url/https://www.google.co.uk"
val nestedF = Future(Http(slowApi).asString).flatMap { _ =>
Future.sequence(List(
Future(Http(slowApi).asString),
Future(Http(slowApi).asString),
Future(Http(slowApi).asString)
))
}
time { Await.result(nestedF, Inf) }
}
}
Async 客户端使用全局 EC
object AsyncClient {
def main(args: Array[String]): Unit = {
import scala.concurrent.ExecutionContext.Implicits.global
import sttp.client._
import sttp.client.asynchttpclient.future.AsyncHttpClientFutureBackend
implicit val sttpBackend = AsyncHttpClientFutureBackend()
val delay = "3000"
val slowApi = uri"http://slowwly.robertomurray.co.uk/delay/${delay}/url/https://www.google.co.uk"
val nestedF = basicRequest.get(slowApi).send().flatMap { _ =>
Future.sequence(List(
basicRequest.get(slowApi).send(),
basicRequest.get(slowApi).send(),
basicRequest.get(slowApi).send()
))
}
time { Await.result(nestedF, Inf) }
}
}
片段正在使用
- Slowwly 模拟慢 API
- scalaj-http
- async-http-client sttp 后端
- time
前者需要12秒,后者需要6秒。看起来前者的行为就好像它是 CPU 绑定的,但是我不明白这是怎么回事,因为 Future#sequence
应该并行执行 HTTP 请求?为什么包裹在 Future
中的同步客户端的行为与适当的异步客户端不同?异步客户端在幕后将调用包装在 Futures 中是不是做同样的事情?
Future#sequence should execute the HTTP requests in parallel?
首先,Future#sequence
不执行任何操作。它只是产生一个在所有参数完成时完成的未来。
如果 EC 中有空闲线程,则立即开始构建期货的评估(执行)。否则,它只是将它提交给某种队列。
我敢肯定,在第一种情况下,您会单线程执行期货。
println(scala.concurrent.ExecutionContext.Implicits.global) -> parallelism = 6
不知道为什么会这样,可能是另外5个线程因为某些原因总是很忙。您可以尝试使用 5-10 个线程明确创建的新 EC。
与 Async 情况的不同之处在于您不自己创建 future,它由库提供,在内部不会阻塞线程。它启动异步进程,“订阅”结果,以及 returns 未来,在结果到来时完成。
实际上,async lib 可以在内部有另一个 EC,但我怀疑。
顺便说一句,Futures 不应该包含 slow/io/blocking 没有 blocking
的评估。否则,您可能会阻塞主线程池 (EC),您的应用程序将被完全冻结。
考虑以下两个片段,其中第一个使用 Future
包装 scalaj-http 请求,第二个使用 async-http-client
Sync 使用全局 EC
用 Future 包裹的客户端object SyncClientWithFuture {
def main(args: Array[String]): Unit = {
import scala.concurrent.ExecutionContext.Implicits.global
import scalaj.http.Http
val delay = "3000"
val slowApi = s"http://slowwly.robertomurray.co.uk/delay/${delay}/url/https://www.google.co.uk"
val nestedF = Future(Http(slowApi).asString).flatMap { _ =>
Future.sequence(List(
Future(Http(slowApi).asString),
Future(Http(slowApi).asString),
Future(Http(slowApi).asString)
))
}
time { Await.result(nestedF, Inf) }
}
}
Async 客户端使用全局 EC
object AsyncClient {
def main(args: Array[String]): Unit = {
import scala.concurrent.ExecutionContext.Implicits.global
import sttp.client._
import sttp.client.asynchttpclient.future.AsyncHttpClientFutureBackend
implicit val sttpBackend = AsyncHttpClientFutureBackend()
val delay = "3000"
val slowApi = uri"http://slowwly.robertomurray.co.uk/delay/${delay}/url/https://www.google.co.uk"
val nestedF = basicRequest.get(slowApi).send().flatMap { _ =>
Future.sequence(List(
basicRequest.get(slowApi).send(),
basicRequest.get(slowApi).send(),
basicRequest.get(slowApi).send()
))
}
time { Await.result(nestedF, Inf) }
}
}
片段正在使用
- Slowwly 模拟慢 API
- scalaj-http
- async-http-client sttp 后端
- time
前者需要12秒,后者需要6秒。看起来前者的行为就好像它是 CPU 绑定的,但是我不明白这是怎么回事,因为 Future#sequence
应该并行执行 HTTP 请求?为什么包裹在 Future
中的同步客户端的行为与适当的异步客户端不同?异步客户端在幕后将调用包装在 Futures 中是不是做同样的事情?
Future#sequence should execute the HTTP requests in parallel?
首先,Future#sequence
不执行任何操作。它只是产生一个在所有参数完成时完成的未来。
如果 EC 中有空闲线程,则立即开始构建期货的评估(执行)。否则,它只是将它提交给某种队列。
我敢肯定,在第一种情况下,您会单线程执行期货。
println(scala.concurrent.ExecutionContext.Implicits.global) -> parallelism = 6
不知道为什么会这样,可能是另外5个线程因为某些原因总是很忙。您可以尝试使用 5-10 个线程明确创建的新 EC。
与 Async 情况的不同之处在于您不自己创建 future,它由库提供,在内部不会阻塞线程。它启动异步进程,“订阅”结果,以及 returns 未来,在结果到来时完成。
实际上,async lib 可以在内部有另一个 EC,但我怀疑。
顺便说一句,Futures 不应该包含 slow/io/blocking 没有 blocking
的评估。否则,您可能会阻塞主线程池 (EC),您的应用程序将被完全冻结。