Как реализован ReactiveMongo, чтобы он считался неблокирующим?

Чтение документации о Play Framework и ReactiveMongo заставляет меня поверить, что ReactiveMongo работает таким образом, что использует мало потоков и никогда не блокируется.

Однако кажется, что связь приложения Play с сервером Mongo должна происходить в каком-то потоке. Как это реализовано? Ссылки на исходный код для Play, ReactiveMongo, Akka и т. Д. Также были бы очень признательны.

Play Framework включает некоторую документацию по этому поводу на этой странице о пулах потоков. Это начинается:

Фреймворк Play - это снизу вверх асинхронный веб-фреймворк. Потоки обрабатываются асинхронно с использованием итераций. Пулы потоков в Play настроены на использование меньшего количества потоков, чем в традиционных веб-фреймворках, поскольку ввод-вывод в play-core никогда не блокируется.

Затем он немного рассказывает о ReactiveMongo:

Наиболее частое место, где типичное приложение Play блокирует, - это обращение к базе данных. К сожалению, ни одна из основных баз данных не предоставляет драйверы асинхронных баз данных для JVM, поэтому для большинства баз данных единственным вариантом является использование блокировки ввода-вывода. Заметным исключением из этого правила является ReactiveMongo, драйвер для MongoDB, который использует библиотеку Play Iteratee для взаимодействия с MongoDB.

Ниже приводится примечание об использовании фьючерсов:

Обратите внимание, что у вас может возникнуть соблазн поместить код блокировки в Futures. Это не делает его неблокирующим, это просто означает, что блокировка произойдет в другом потоке. Вам все равно нужно убедиться, что в пуле потоков, который вы используете, достаточно потоков для обработки блокировки.

Аналогичное примечание есть в документации Play на странице Обработка асинхронных результатов:

Вы не можете волшебным образом превратить синхронный ввод-вывод в асинхронный, заключив его в Future. Если вы не можете изменить архитектуру приложения, чтобы избежать блокировки операций, в какой-то момент эта операция должна быть выполнена, и этот поток будет заблокирован. Таким образом, помимо включения операции в Future, необходимо настроить ее для запуска в отдельном контексте выполнения, который был настроен с достаточным количеством потоков для работы с ожидаемым параллелизмом.

В документации, кажется, говорится, что ReactiveMongo не блокирует, поэтому вам не нужно беспокоиться о том, что он съедает много потоков в вашем пуле потоков. Но ReactiveMongo должен связываться с сервером Mongo где-нибудь.

Как реализована эта связь, чтобы Mongo не использовала потоки из пула потоков по умолчанию Play?

Еще раз, ссылки на определенные файлы в Play, ReactiveMongo, Akka и т. Д. Будут очень ценится.


person illabout    schedule 02.09.2014    source источник
comment
Вы должны понимать, как работает Future, это ключ к тому, чтобы не блокировать.   -  person cchantep    schedule 02.09.2014
comment
В одной записке написано in addition to enclosing the operation in a Future, it’s necessary to configure it to run in a separate execution context that has been configured with enough threads to deal with the expected concurrency. Это звучит так, как будто он просто заблокируется в другом потоке. Знаете ли вы какие-либо ресурсы, которые сделали бы это более понятным?   -  person illabout    schedule 02.09.2014
comment
@applicius, фьючерсы Futures - это просто высокоуровневый инструмент для параллелизма, они на самом деле не связаны с неблокирующим вводом-выводом, который является основой реактивности ReactiveMongo, хотя они, безусловно, могут помочь в использовании ReactiveMongo .   -  person Vladimir Matveev    schedule 02.09.2014


Ответы (1)


Да, действительно, вам все равно нужно использовать потоки для выполнения любой работы, включая связь с базой данных. Важно то, как именно происходит это общение.

ReactiveMongo «не использует потоки» в том смысле, что не использует блокировку ввода-вывода. Обычные средства ввода-вывода Java, такие как java.io.InputStream, блокируются; это означает, что чтение из такого InputStream или запись в OutputStream блокирует поток до тех пор, пока «другая сторона» не предоставит требуемые данные или не будет готова их принять. Для сетевого взаимодействия это означает, что потоки будут заблокированы.

Однако Java предоставляет NIO API, который поддерживает не -блокирование и асинхронный ввод-вывод. Я не хочу сейчас вдаваться в подробности, но основная идея, естественно, заключается в том, что неблокирующий ввод-вывод позволяет не блокировать потоки, которым необходимо обмениваться некоторыми данными с внешним миром: например, эти потоки могут опрашивают источник данных, чтобы проверить, доступны ли какие-либо данные, и если их нет, они возвращаются в пул потоков и могут использоваться для других задач. Конечно, там эти возможности предоставляются базовой ОС.

Точные детали реализации неблокирующего ввода-вывода обычно скрыты внутри высокоуровневых библиотек, таких как Netty, потому что их совсем не удобно использовать. Netty (это как раз та библиотека, которую использует ReactiveMongo), например, предоставляет хороший асинхронный API, похожий на обратный вызов, который действительно прост в использовании, но также достаточно мощный и выразительный, чтобы создавать сложные приложения с интенсивным вводом-выводом с высокой пропускной способностью.

Итак, ReactiveMongo использует Netty для общения с сервером базы данных Mongo, и поскольку Netty является реализацией асинхронного сетевого ввода-вывода, ReactiveMongo действительно не нужно блокировать потоки в течение длительного времени.

person Vladimir Matveev    schedule 02.09.2014
comment
Сможете ли вы добавить ссылки на конкретный исходный код ReactiveMongo, который использует Netty для выполнения неблокирующего ввода-вывода? - person illabout; 02.09.2014
comment
Кроме того, в Linux реализован ли Java NIO API с функциями C, такими как select () и epoll ()? - person illabout; 02.09.2014
comment
@illabout, он содержится где-то здесь но похоже, что он распределен по разным исходным файлам. Однако точные биты, которые вы ищете, кажутся здесь, ближе к концу. - person Vladimir Matveev; 02.09.2014
comment
@illabout, это зависит от конкретной реализации JVM. Я не знаю, какие API используют популярные JVM, но подозреваю, что они действительно используют что-то вроде epoll(). Маловероятно, что они используют select(), так как он старый и не рекомендуется, но все может случиться. - person Vladimir Matveev; 02.09.2014