Разница между map и mapAsync
может ли кто-нибудь объяснить мне разницу между map и mapAsync w.r.t AKKA поток? в документации говорят, что
преобразования потока и побочные эффекты включая внешнее non-stream услуги могут быть выполнены с mapAsync или mapAsyncUnordered
Почему мы не можем просто нас карте? Я предполагаю, что поток, источник, потоп все будет Монадическим по своей природе и, следовательно, карта должна работать нормально w.r.T задержка в природе из этих ?
1 ответов
подпись
разница выделен в подписи: Flow.map
принимает функцию, которая возвращает тип T
пока Flow.mapAsync
принимает функцию, которая возвращает тип Future[T]
.
Практический Пример
в качестве примера предположим, что у нас есть функция, которая запрашивает в базе данных полное имя пользователя на основе идентификатора пользователя:
type UserID = String
type FullName = String
val databaseLookup : UserID => FullName = ??? //implementation unimportant
дали Source
of UserID
значения, мы могли бы просто использовать Flow.map
в потоке, чтобы запросить базу данных и распечатать полные имена на консоли:
val userIDSource : Source[UserID, _] = ???
val stream =
userIDSource.via(Flow[UserID].map(databaseLookup))
.to(Sink.foreach[FullName](println))
.run()
одним из ограничений этого подхода является то, что этот поток будет только 1 запрос к БД одновременно. Последовательный запрос будет "узким местом" и, вероятно, предотвратит максимальную пропускную способность в нашем потоке.
мы могли бы попытаться улучшить производительность с помощью параллельных запросов, используя Future
:
def concurrentDBLookup(userID : UserID) : Future[FullName] =
Future { databaseLookup(userID) }
val concurrentStream =
userIDSource.via(Flow[UserID].map(concurrentDBLookup))
.to(Sink.foreach[Future[FullName]](_ foreach println))
.run()
проблема с этим упрощенное добавление заключается в том, что мы эффективно устранили противодавление.
раковина просто тянет в будущем и добавляет foreach println
, что относительно быстро по сравнению с запросами базы данных. Поток будет непрерывно распространять спрос на источник и порождать больше фьючерсов внутри Flow.map
. Таким образом, нет ограничений на количество databaseLookup
работает по совместительству. Неограниченный параллельный запрос может в конечном итоге перегрузить база данных.
Flow.mapAsync
на помощь; мы можем иметь параллельный доступ к БД, в то же время ограничивая количество одновременных поисков:
val maxLookupCount = 10
val maxLookupConcurrentStream =
userIDSource.via(Flow[UserID].mapAsync(maxLookupCount)(concurrentDBLookup))
.to(Sink.foreach[FullName](println))
.run()
Также обратите внимание, что Sink.foreach
стало проще, он больше не принимает Future[FullName]
, а просто FullName
вместо.
Неупорядоченная Асинхронная Карта
если поддержание последовательного порядка UserIDs в FullNames не требуется, вы можете использовать Flow.mapAsyncUnordered
. Например: вам как раз нужно чтобы напечатать все имена на консоли, но не заботился о порядке, они были напечатаны.