Модель параллелизма реактора Springboot webflux

Я хотел бы узнать больше о базовой модели параллелизма для Springboot WebFlux?

Является ли традиционная многопоточная модель с блокировкой более подходящей для веб-сервиса с интенсивным использованием ЦП? Или в целом традиционная модель пула потоков лучше подходит в соответствии с этой статьей https://people.eecs.berkeley.edu/~brewer/papers/threads-hotos-2003.pdf ?

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


person ethan    schedule 07.12.2018    source источник


Ответы (1)


Где WebFlux хорошо подходит и как он работает внутри

С моей точки зрения, Spring WebFlux лучше всего подходит для приложений с интенсивным использованием сети. Под капотом Spring WebFlux использует Reactor-Netty, который поддерживает оболочка для Netty. Reactor-Netty использует почти ту же стратегию Netty Threading и создает Runtime.getRuntime().availableProcessors() * 2 потоков для своих внутренних EventLoopThreadPool. Это означает, что каждый Thread привязан к своему ядру/процессору и работает с минимальной конкуренцией за ресурсы ЦП. Такая модель очень хорошо работает со сквозной неблокирующей связью. Таким образом, в случае, если входящий запрос заканчивается удаленным сетевым вызовом, этот вызов должен выполняться неблокирующим образом, чтобы тот же поток мог вернуться к остальным задачам в очереди с четным циклом. Такой метод позволяет нам эффективно использовать наше оборудование практически без накладных расходов на переключение контекста и высокую конкуренцию, что является общим свойством блокировки связи с большим количеством задействованных потоков.

Роль Проекта Реактор

Центральная роль Project Reactor в Spring WebFlux заключается в предоставлении модели программирования, сохраняющей четкость сложного неблокирующего асинхронного выполнения. Это скрывает сложность продолжения обработки данных и позволяет нам легко построить функциональный декларативный конвейер обработки элементов. Кроме того, модель многопоточности Reactor — это всего лишь пара операторов, которые позволяют без головной боли перепланировать сложные и эффективные элементы, обрабатывающие элементы в выделенном пуле потоков. Под капотом используются те же ThreadPools и ExecutorServices, взятые из библиотеки Java Core.

Как справляться с ресурсоемкими задачами, подходит ли для них Project Reactor?

Я бы сказал, что Reactor Netty также хорошо подходит для задач с интенсивным использованием процессора. Но в этом случае Project Reactor следует использовать надлежащим образом. В случае обработки сложных алгоритмов или подобных работ лучше использовать чистую Java, поскольку Reactor добавляет около 100-150% накладных расходов по производительности.

Как создать эффективную обработку задач с интенсивным использованием ЦП с помощью Project Reactor и Reactor-Netty (WebFlux)

Я бы рекомендовал следовать шаблону «Очередь работы», чтобы каждый поток выполнял новую задачу после завершения предыдущей.

Если у нас есть задача с интенсивным использованием ЦП, всегда рекомендуется планировать ее в выделенном пуле потоков. Несмотря на то, что это добавит немного накладных расходов, у нас будет более высокая задержка для ввода-вывода при чтении и записи, что является неотъемлемой частью любого сетевого приложения. В случае с Netty мы будем уверены, что EventLoop Netty выполняет только чтение и запись в сеть и ничего более.

Чтобы запланировать задачи в выделенном пуле потоков, мы можем следовать методике, показанной в примере кода ниже:

@PostMapping
public Mono<CpuIntensiveResult> cpuIntensiveProcessingHandler(
    Mono<CpuIntensiveInput> monoInput
) {
    return monoInput
        .publishOn(Schedulers.fromExecutorService(myOwnDedicatedExecutor))
        .map(i -> doCpuIntensiveInImperativeStyle(i));
}

Как мы видим из кода выше, с помощью одного оператора из арсенала Project Reactor мы можем легко запланировать обработку работы на выделенном Threadpool. В свою очередь, мы можем быстро обернуть любой существующий Executor Service в Scheduler и использовать экосистему Reactor.

Как насчет блокировки по многопоточности?

При обычном многопоточном методе, когда у нас больше потоков, чем ядер, мы не получим никаких преимуществ. Недостаток здесь тот же — переключение контекста и конкуренция. Казалось бы, задачи обрабатываются одновременно. Однако системный планировщик выполняет такую ​​же тяжелую работу по распределению процессорного времени между параллельными потоками. Он будет использовать один и тот же ЦП для нескольких интенсивных задач, поэтому в конечном итоге это приведет к более высокой задержке для каждой задачи. В среднем время обработки будет выше, чем та же работа, выполняемая тем же количеством потоков, что и процессоры/ядра в системе.

Несколько замечаний относительно моделей Threading как «настоящей» модели обработки.

Согласно упомянутому техническому документу, модель Threading — это настоящая модель программирования. Я предполагаю, что авторы этой статьи говорят о зеленых нитях. Зеленые потоки могли бы быть лучшей моделью программирования, но, в конце концов, вам придется следовать тем же правилам, упомянутым выше для модели программирования Reactor. Недостаток Thread и впоследствии императивной модели программирования заключается в неспособности работать с потоком данных, где модель программирования Reactor отлично подходит.

Кроме того, я бы порекомендовал вернуться к Универсальному закону масштабируемости и рассмотреть проблему разногласий и согласованности ( что относится к текущим исполнениям Java Threading). Кроме того, хороший обзор масштабируемости объясняется в следующем документе.

Еще одним примером эффективного использования асинхронной + неблокирующей обработки запросов является архитектура Facebook, которая при пиковой загрузке преобразует очередь работ в стек работ, что позволяет сохранить минимальную задержку.

person Oleh Dokuka    schedule 08.12.2018