В чем концептуальная разница между приемником и подписчиком в проектных потоках «реактор» или «акка»?

Понятия приемник и подписчик мне кажутся похожими. Кроме того, я не вижу, чтобы концепция приемника была явно определена в спецификации реактивных потоков.


person Jatin    schedule 09.01.2018    source источник


Ответы (1)


Я вижу, что Олег Докука из Project Reactor (здесь отсутствует отказ от ответственности), уже опубликовал ответ, однако многие из его предположений о Потоки Akka и Реактивные потоки неверны, поэтому позвольте мне уточнить ниже.

Отказ от ответственности: я участвовал в Reactive Streams с самого начала, и был автором большая часть его Technology Compatibility Kit. Я также поддерживаю Akka и Akka Streams.

Также обратите внимание, что: Реактивные потоки были включены в Java 9 и известны как java.util.concurrent.Flow. *, поэтому все нижеприведенные комментарии относительно RS точно так же относятся к j.u.c.Flow.Subscriber и другим типам.


Ответ

Реактивные потоки - это спецификация интерфейса поставщика услуг (SPI)

Реактивные потоки, и особенно типы издателей / подписчиков / подписок / процессоров, представляют собой интерфейс поставщика услуг. Это подтверждается даже в самых ранних обсуждениях спецификации, относящейся к прошлому. к 2014 г.

В первые дни спецификации даже типы спецификации пытались скрыть издателя, подписчика и другие типы. К сожалению, типы будут просачиваться независимо от тогдашнего API, , таким образом, API ( !) был удален, остались только типы SPI.

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

Для справки, RxJava 2.0, а также Reactor напрямую расширяют эти типы, в то время как Akka Streams остается верным дизайну и принципам RS, скрывая их как интерфейс программирования разработчика приложений - вот почему Sink не расширяет Subscriber. Это не имеет ничего общего с тем, что является «нативной поддержкой», как я видел, как люди утверждали, что существует прямая связь IS-A (скорее, утверждение, что межоперационная библиотека является вашей «родной», - это неправильное понимание концепции).

Источники и подписчики, источники и издатели

Понятия приемник и подписчик мне кажутся похожими.

Правильные, они намеренно и по своей конструкции похожи.

Поскольку приемник - это расширенное представление чего-то, что эффективно дает подписчика. Для упрощения, вы можете думать об этом как о «фабрике подписчиков» (более конкретно, Sink - это «план», а Materializer берет план приемника и создает соответствующие этапы RS, включая Publishers для источников и Subscribers for Sinks. Итак, когда вы говорите Sink.ignore, на самом деле это фабрика, которая в конечном итоге создает подписчика, который выполняет все запросы и игнорирование, как в соответствии с реактивными потоками. То же самое со всеми другими методами, объявленными в Sink.

То же самое относится к Source, который относится 1: 1 к реактивным потокам Publisher. Таким образом, Source.single(1) - это то, что внутренне материализуется в Publisher, который выполняет свою работу - излучает этот 1 элемент, если ему разрешено это делать ниже по течению.

А.К.А. Почему в реактивных потоках нет стока?

Как упоминалось выше, приемник Akka не расширяет подписку напрямую. Однако это в основном фабрика для них.

Вы можете спросить: «Разве пользователь вообще никогда не видит эти типы издателей / подписчиков при нормальном использовании?» И ответ: да, действительно, и это функция, а также цель дизайна (в соответствии с тем, что такое Reactive Streams). Если базовые экземпляры Publisher и Subscriber были доступны пользователям все время напрямую, их можно было назвать неправильно, что привело к ошибкам и путанице. Если эти типы никогда не раскрываются без явного запроса, вероятность случайных ошибок снижается!

Некоторые неправильно поняли этот дизайн и утверждали, что в Akka Streams нет «родной» поддержки для него (что неверно). Давайте посмотрим, что дает нам отключение от подписчика в API:

Кроме того, я не вижу, чтобы концепция приемника была явно определена в спецификации реактивных потоков.

Действительно, Sinks не являются частью Reactive Streams, и это абсолютно нормально.

Преимущества отказа от "Sink IS-A Subscriber"

Sink являются частью Akka Streams, и их цель - обеспечить свободный доступ к DSL, а также быть фабриками для Subscribers. Другими словами, если подписчик - это блоки LEGO, Sink - это то, что их строит (а Akka Stream Materializer - это то, что объединяет различные блоки LEGO, чтобы «запускать» их).

Фактически, для пользователей выгодно, чтобы Sink не содержал окончательного IS-A с подписчиком (sic!), Как это делают другие библиотеки:

Это связано с тем, что, поскольку org.reactivestreams.Subscriber теперь включен в Java 9 и стал частью самой Java, библиотеки должны перейти на использование java.util.concurrent.Flow.Subscriber вместо org.reactivestreams.Subscriber. Библиотеки, которые выбрали для раскрытия и прямого расширения типов реактивных потоков, теперь будут труднее адаптировать типы JDK9 - все их классы, расширяющие подписчика и друзей, необходимо будет скопировать или изменить, чтобы расширить тот же интерфейс, но из другой пакет. В Akka мы просто открываем новый тип по запросу - уже поддерживающий типы JDK9 со дня выпуска JDK9.

Поскольку реактивные потоки являются SPI - интерфейсом поставщика услуг - он предназначен для совместного использования библиотеками, чтобы они могли «говорить об одних и тех же типах и протоколах». Все коммуникации, которые осуществляют Akka Streams и другие библиотеки Reactive Streams, подчиняются этим правилам, и если вы хотите подключить какую-то другую библиотеку к Akka Streams, вы бы сделали именно это - дайте Akka Streams тип взаимодействия, который является подписчиком, обработчиком или издателем; не Sink, так как это Akka «специфичный для Akka» DSL (язык, специфичный для домена), который добавляет к нему удобство и другие тонкости, скрывая (намеренно!) тип подписчика.

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

Чтобы избежать ошибок, типы Reactive Streams в Akka Streams «скрыты» и доступны только по явному запросу, что сводит к минимуму риск того, что люди совершат ошибки, случайно вызывая методы для «сырых» типов Reactive Streams, не следуя их протоколу.

person Konrad 'ktoso' Malawski    schedule 10.01.2018
comment
Удалил свой ответ. ;) - person Oleh Dokuka; 10.01.2018
comment
О, я не знал, что это возможно :) Спасибо, что обратились к нам через твиттер, чтобы я мог прояснить ситуацию здесь! @OlehDokuka - person Konrad 'ktoso' Malawski; 10.01.2018
comment
Фантастический пост! Просто хочу убедиться, что я правильно понимаю: будет ли перенос моего текущего приложения Akka Streams на JDK9 полностью беспроблемным? Есть ли что-то особенное, о чем мне нужно быть осторожным? - person Nirmalya; 12.01.2018
comment
В нем уже есть все встроенные функции, и если вы запустите Java 9, вы уже можете получить типы jucFlow. *: См. Источники и источники в JavaFlowSupport: github.com/akka/akka/pull/23650/ мы предоставляем этот класс только при запуске на Java 9, благодаря мульти-выпуску баночки :-) - person Konrad 'ktoso' Malawski; 13.01.2018