Scala Idiomatic и самый быстрый способ использования нескольких веб-сервисов и объединения результатов - PullRequest
0 голосов
/ 18 октября 2018

У меня есть приложение Scala, которое использует несколько источников данных и объединяет объединенный вывод, приложение вызывает несколько HTTP-вызовов параллельно и ожидает их результатов, когда все они разрешены, оно объединяет их вывод в один объект результата и отправляет ответназад как будущее Scala.Код:

val providers = getProviders()
val futures = for (p <- providers) yield searchSource(p, req)
val result = waitAll(futures)
val emptyList = Option(ProductList(Vector.empty, ListSummary(0, 0, 0)))

result.map { x =>
  x.foldLeft(emptyList)((r, c) => {
    if (c.isSuccess) ProductList.merge(r, c.get) else r
  })
}

Сначала он получает список строк провайдеров, затем запускает параллельные вызовы для каждого из них в searchSource.

Функция searchSource сама возвращает Future[Option[ProductList]]

Метод ожидает завершения всех фьючерсов, для чего он вызывает функцию waitAll:

  protected def waitAll[T](futures: Seq[Future[T]])(implicit ec: CustomExecutionContext): Future[Seq[Try[T]]] =
    Future.sequence(lift(futures))

  protected def lift[T](futures: Seq[Future[T]])(implicit ec: CustomExecutionContext): Seq[Future[Try[T]]] =
    futures.map(_.map { Success(_) }.recover { case t => Failure(t) })

После ожидания завершения всех фьючерсов он объединяет результаты в один составной объект и возвращает его как будущее.,Для этого я использую функцию foldLeft.

Существуют ли какие-либо "официальные" монадические / идиоматические способы решения проблемы этого типа или есть примеры, оптимизированные для производительности, для этого типа сценария использования?

1 Ответ

0 голосов
/ 18 октября 2018

Конечно.Это обычный шаблон, и использование cats-effect IO вместо Future действительно помогает (есть и другие варианты).Например, вы можете реализовать что-то вроде этого (я проясняю типы для ясности):

def searchSource(provider: String, request: Request[_]): IO[Option[ProductList]] = ???

val result: IO[List[Option[ProductList]]] = 
  providers.traverse[IO, Option[ProductList]](p => searchSource(p, req))

Обратите внимание, что traverse по существу совпадает с map, за которым следует sequence.Затем вы можете делать все, что вы хотите в вашей программе result IO (например, map поверх нее и т. Д.), Чтобы преобразовать ее в конечный результат, который вы желаете.В конце вашей программы вы можете сделать следующее:

result.attempt.unsafeRunSync() //Either[Throwable, List[Option[ProductList]]]

Для обработки ошибок проверьте метод IO handleErrorWith.Вы можете обрабатывать ошибки на уровне searchSource и / или на уровне result, так как они оба IO s.

...