Статьи

Reactor: основа для асинхронных приложений в JVM


Первоначальным автором этой статьи был Джон Брисбин

Мы рады сообщить, что после долгого периода внутренней инкубации мы выпускаем основную среду для асинхронных приложений в JVM, которую мы называем  Reactor . Он предоставляет абстракции для Java, Groovy и других языков JVM, чтобы упростить создание приложений, управляемых событиями и данными. Это также очень быстро. На скромном оборудовании можно обрабатывать более 15 000 000 событий в секунду с максимальной скоростью неблокирования Dispatcher. Другие диспетчеры могут предоставить разработчику широкий выбор вариантов: от стиля пула потоков, выполнения долго выполняемых задач до неблокируемой диспетчеризации задач большого объема. Репозиторий GitHub находится здесь  https://github.com/reactor/reactor .

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

Абстракции Reactor предоставляют разработчику набор инструментов, позволяющих не только разрабатывать, но и  составлять приложения таким образом, чтобы более эффективно использовать системные ресурсы — что особенно важно при работе в облаке — и уменьшать или устранять спагетти вложенных обратных вызовов (метко названных  «обратный вызов»). ад » , которая до сих пор обременяла большинство асинхронных приложений.

Чем хорош Reactor?

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

Вот почему  проект Spring XD  (а также несколько других экосистемных проектов Spring, таких как Spring Integration и Spring Batch) намереваются использовать преимущества Reactor. Объединение асинхронной диспетчеризации Reactor с основанными на NIO TCP-адаптерами от Spring Integration для обеспечения высокопроизводительного системного журнала и загрузки MQTT — это только один пример.

Селекторы, потребители и события

Три основных компонента в модуле активной зоны реактора — это SelectorConsumerи  Event. Consumer можно назначить Reactor с помощью a  Selector, который представляет собой простую абстракцию для обеспечения гибкости при поиске  Consumers вызова для  Event. Доступен ряд селекторов по умолчанию. От простых строк до регулярных выражений и шаблонов URL в стиле Spring MVC

Вот пример кода, демонстрирующий, насколько легко создавать приложения, управляемые событиями, с использованием Reactor:

// This helper method is like jQuery’s.
// It creates a Selector instance so you don’t have
// to construct one using 'new Selector("parse")'
import static reactor.Fn.$;
Reactor reactor = R.create();
// Register interest in events published to key "parse"
reactor.on($("parse"), new Consumer<Event<String>>() {
public void call(Event<String> ev) {
service.handleEvent(ev);
}
});
// Send an event to this Reactor and trigger all actions
// that match the given Selector
reactor.notify("parse", Fn.event("Hello World!"));

Groovy, с любовью

В дистрибутив Reactor включен модуль под названием  reactor-groovy. Он включает в себя привязку Groovy, которая обеспечивает выразительный синтаксис, проверку во время компиляции @CompileStatic, неявное преобразование  Closures в  Consumers, а также другие специфичные для Groovy экономия времени.

//Assign a Closure as a Consumer 
reactor.on($('hello')) { Event<String> ev ->
if(ev.headers['specialHeader']) { // Events can have metadata
doSomethingWith(ev.data)
}
}
// Use Groovy helpers for notify
reactor.notify for: 'hello', data: 'Hello World!', specialHeader: 'specialValue'

И лучшая часть: нам не нужно было торговать производительностью, чтобы попасть туда. Те же оптимизации JVM применяются к коду Groovy, который применяется к коду Java. Мы постоянно (некоторые сказали бы «одержимо») на микро-бенчмаркинге диспетчерского кода, чтобы сделать его максимально быстрым и обеспечить пользователям Java и Groovy максимально возможную пропускную способность.

Готов к Java 8, когда вы

Кроме того, Reactor  совместим с лямбда-выражениями в Java SE 8,  и многие компоненты в Reactor можно заменить на лямбды, чтобы сделать ваш код Java более лаконичным. Мы также обнаружили, что использование лямбда-выражений Java 8 (и ссылок на методы) приводит к несколько большей пропускной способности. Когда Java 8 станет GA, вам не придется ждать, пока Reactor его поддержит. Это будет просто работа ™.

// Use a POJO as an event handler
class Service {
public <T> void handleEvent(Event<T> ev) {
// handle the event data
}
}
@Inject
Service service;
// Use a method reference to create a Consumer<Event<T>>
reactor.on($("parse"), service::handleEvent);
// Notify consumers of the 'parse' topic that data is ready
// by passing a Supplier<Event<T>> in the form of a lambda
reactor.notify("parse", () -> {
slurpNextEvent()
});

Функциональные, императивные, обратный вызов или обещание: вы выбираете

Исполнитель, Event Loop, Actor, Distributed — существует множество форм для одного из самых важных сценариев использования в программировании на основе событий: диспетчеризация задач. Reactor поддерживает несколько стилей программирования на основе событий. В дополнение к традиционному Consumer интерфейсу, ориентированному на обратный вызов , Reactor имеет интерпретацию  спецификации Promises / A +,  которая упрощает работу с отложенными значениями и потребителями.

Вложенные обратные вызовы, хотя они просты и просты в использовании на императивном языке, таком как Java, усложняются при увеличении сложности приложения. Реактор Composable и  Promise все о легкой композиции действий. Вы можете Composable объединить в последовательность действий, которые преобразуют значения, сохраняют данные в хранилище данных, агрегируют значения и т. П. И поскольку они являются цепочечными, вы можете делать все это на чистой Java безопасным для типов способом. Вот быстрый пример использования a  Composable для простой цепочки ряда асинхронно выполняемых задач, которые выполняют преобразование и фильтрацию потока данных, когда он проходит через  Composable:

Composable<Integer> c = new Composable<>()
.map(new Function<Integer, Integer>() {
public Integer apply(Integer i) {
return i % 2;
}
})
.filter(new Function<Integer, Boolean>() {
public Boolean apply(Integer i) {
return i == 0;
}
})
.consume(new Consumer<Integer>() {
public void accept(Integer eveni) {
// work with only even numbers here
}
});

Каждый шаг  Composable является потенциально асинхронной задачей. Звонки  mapfilterи  consume поручают задания для выполнения , когда значение из предыдущего шага не доступно, нет обратного вызова ад требуется.

Диспетчерская

Там нет серебряной пули для любой диспетчерской проблемы. Reactor предоставляет разные стили Dispatchers, потому что каждое асинхронное приложение имеет разные потребности в диспетчеризации в разных частях приложения. Например, при приеме волн прилива данных Reactor нужно будет использовать высокоскоростную неблокировку  Dispatcher на основе  почтенного Disruptor RingBuffer . Но если a  Reactor выполняет блокирующий вызов на сервере базы данных или хранит большой объем данных в S3, он захочет использовать рабочий пул с более низкой пропускной способностью  Dispatcher. Reactor предоставляет несколько вариантов, чтобы вы могли выбрать подходящий инструмент для работы.

Если встроенные  Dispatcher реализации не соответствуют вашим потребностям, то Reactor предоставляет прочную основу, на которой вы можете создать свою собственную  Dispatcher , адаптированную к вашей проблемной области.

Grails, встречайте события, события, встречайте Grails

Grails  — это полнофункциональная платформа веб-приложений для JVM. Со своей зрелой кодовой базой, поддерживаемой процветающим сообществом, Grails, тем не менее, сталкивается с  новыми архитектурными проблемами . События были введены в Grails через  плагин core-core . Но события настолько мощны, что эта функция действительно принадлежит ядру; поэтому начиная с версии 2.3 приложения Grails будут иметь встроенный, чрезвычайно мощный, но простой в использовании API-интерфейс событий, основанный на соглашениях, который очень похож на текущую реализацию в плагине «ядро платформы». Этот API событий будет построен на основе Reactor.

Целью интеграции событий в Grails является нацеливание на новые виды разработки, особенно «веб в реальном времени» и крупномасштабную неблокирующую разработку приложений. В сочетании с функциями асинхронной GORM API событий станет мощным союзником. Сложные запросы, обращающиеся к хранилищам больших данных — таким образом, занимающие много времени — могут реагировать, когда их результаты готовы, отправляя их прямо в браузер.

Страстное сообщество необходимо

В течение следующих нескольких месяцев мы будем усердно работать над подготовкой к  SpringOne , где многие из наших больших, быстрых и масштабируемых решений для данных будут играть первостепенную роль. Если вы еще не планировали посещать,  вы обязательно должны ! У нас будет  сеанс на Reactor  и его использование для создания высокопроизводительных приложений, управляемых событиями.

Но мы не можем сделать это без тебя! Эти усилия будут успешными только в том случае, если вы поможете нам создать увлеченное и активное сообщество для крупной, быстрой, ориентированной на события разработки приложений в JVM. Если вам интересно, ознакомьтесь  с исходным кодом на GitHub , посмотрите пример кода в  быстрый реакторсообщите о любых найденных проблемах , задайте вопросы о Reactor в StackOverflow с помощью хештега #reactor , присоединяйтесь к обсуждению на  платформе реактора Google Группируйте список рассылки электронной почты или  раскладывайте репозиторий,  чтобы добавлять функции, настраивать объекты для повышения пропускной способности и вносить новые идеи.

Мы будем рады видеть вас там!