Выбрать главу

Если вы похожи на меня и многих разработчиков, вы режете свои программные зубы императивным программированием. Есть хороший шанс, что большинство (или весь) код, который вы пишете сегодня, по-прежнему является императивным по своей природе. Императивное программирование достаточно интуитивно, что молодые студенты с легкостью изучают его в своих школьных программах STEM, и оно достаточно мощное, чтобы составлять основную часть кода, который используется во многих крупнейших предприятия.

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

Во время выполнения задачи, особенно если это задача I/O, такая как запись данных в базу данных или выборка данных с удаленного сервера, поток, вызвавший эту задачу, блокируется и не может ничего сделать, пока задача не завершится. Проще говоря, заблокированные потоки расточительны.

Большинство языков программирования, включая Java, поддерживают параллельное программирование. Довольно просто запустить другой поток в Java и отправить его на выполнение некоторой работы, в то время как вызывающий поток продолжает что-то еще. Но хотя создавать потоки легко, эти потоки, скорее всего, сами блокируются. Управление параллелизмом в нескольких потоках является сложной задачей. Больше потоков означает больше сложности.

Напротив, реактивное программирование носит функциональный и декларативный характер. Вместо описания набора шагов, которые должны выполняться последовательно, реактивное программирование включает описание конвейера или потока, через который проходят данные. Вместо того чтобы требовать, чтобы данные были доступны для обработки в целом, реактивный поток обрабатывает данные по мере их поступления. Фактически, поступающие данные могут быть бесконечными (например, постоянный поток данных о температуре в реальном времени).

Чтобы применить аналогию с реальным миром, рассмотрим императивное программирование как водяной шар и реактивное программирование как садовый шланг. Оба являются подходящими способами удивить и замочить ничего не подозревающего друга в жаркий летний день. Но они отличаются по стилю исполнения:

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

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

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

10.1.1 Определение реактивных потоков

Reactive Streams - инициатива, начатая в конце 2013 года инженерами из Netflix, Lightbend и Pivotal (компания, стоящая за Spring). Reactive Streams стремится обеспечить стандарт для асинхронной обработки потока с неблокирующим обратным давлением (backpressure).

Мы уже затронули асинхронную черту реактивного программирования; это то, что позволяет нам выполнять задачи параллельно для достижения большей масштабируемости. Противодавление - это средство, с помощью которого потребители данных могут избежать перегруженности слишком быстрым источником данных, устанавливая ограничения на то, сколько они готовы обработать.

Java Streams vs. Reactive Streams

Существует много общего между Java Streams и Reactive Streams. Для начала, они оба имеют слово потоки в своих именах. Они также предоставляют функциональный API для работы с данными. На самом деле, как вы увидите позже, когда мы посмотрим на Reactor, они даже выполняют одни и те же операции.