Понятия приемник и подписчик мне кажутся похожими. Кроме того, я не вижу, чтобы концепция приемника была явно определена в спецификации реактивных потоков.
В чем концептуальная разница между приемником и подписчиком в проектных потоках «реактор» или «акка»?
Ответы (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, не следуя их протоколу.