Считается ли синхронный HTTP-запрос, заключенный в Future, привязкой к процессору или вводу-выводу?

Рассмотрим следующие два фрагмента, в которых сначала запросы scalaj-http заключаются в Future, а второй использует async-http-client

Клиент синхронизации в оболочке Future с использованием глобального EC

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) }
  }
}

Асинхронный клиент с использованием глобального 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) }
  }
}

Фрагменты используют

Первое занимает 12 секунд, второе - 6 секунд. Кажется, что первый ведет себя так, как будто он связан с процессором, но я не понимаю, как это обстоит так, посколькуFuture#sequenceдолжен выполнять HTTP-запросы параллельно? Почему синхронный клиент завернут вFutureвести себя иначе, чем обычный асинхронный клиент? Разве не тот случай, когда асинхронный клиент делает то же самое, когда он скрывает вызовы в Futures?

1 ответ

Решение

Будущая последовательность # должна выполнять HTTP-запросы параллельно?

Прежде всего, Future#sequenceничего не выполняет. Он просто создает будущее, которое завершается, когда завершаются все параметры. Оценка (исполнение) построенных фьючерсов начинается немедленно, если в EC есть свободный поток. В противном случае он просто отправляет его в своего рода очередь. Я уверен, что в первом случае у вас будет однопоточное исполнение фьючерсов.

println (scala.concurrent.ExecutionContext.Implicits.global) -> parallelism = 6

Не знаю, почему это так, возможно, другие 5 потоков по какой-то причине всегда заняты. Вы можете поэкспериментировать с явно созданным новым EC с 5-10 потоками.

Разница со случаем Async в том, что вы не создаете будущее самостоятельно, оно предоставляется библиотекой, которая внутренне не блокирует поток. Он запускает асинхронный процесс, "подписывается" на результат и возвращает будущее, которое завершается, когда приходит результат.

На самом деле у async lib внутри может быть другой EC, но я сомневаюсь.

Кстати, фьючерсы не должны содержать медленных /io/ блокирующих оценок без blocking. В противном случае вы потенциально заблокируете пул основных потоков (EC), и ваше приложение будет полностью заморожено.

Другие вопросы по тегам