test rxjava examples docs akka-stream project-reactor reactive-streams

akka-stream - rxjava - reactor test



En el reactor de proyecto o en los flujos akka, ¿cuál es la diferencia conceptual entre sumidero y abonado? (1)

Los conceptos de sumidero y abonado me parecen similares. Además, no veo que el concepto de sumidero se defina explícitamente en la especificación de flujos reactivos.


Veo que Oleh Dokuka, del Proyecto Reactor (faltando la renuncia), ya publicó una respuesta , sin embargo, muchas de sus suposiciones sobre Akka Streams y Reactive Streams son incorrectas, así que permítanme aclararlas a continuación.

Descargo de responsabilidad: participé en Reactive Streams desde sus inicios y authored mayor parte de su Kit de compatibilidad de tecnología . También mantengo Akka y Akka Streams.

También tenga en cuenta que: Las secuencias reactivas se han incluido en Java 9 y se conocen como java.util.concurrent.Flow.* lo que todos los comentarios a continuación sobre RS se jucFlow.Subscriber exactamente a jucFlow.Subscriber y los otros tipos.

La respuesta

Reactive Streams es una especificación de interfaz de proveedor de servicios (SPI)

Los flujos reactivos, y específicamente los tipos de Procesador / Suscriptor / Suscripción / Procesador, son una Interfaz de Proveedor de Servicios . Esto se confirma incluso en las primeras discusiones sobre la especificación que se remonta a 2014.

En los primeros días de la especificación, incluso los tipos de la especificación intentaron ocultar el Publicador, el Suscriptor y los otros tipos. Lamentablemente, los tipos se filtrarían independientemente de la API considerada anterior, por lo que la API (!) Se eliminó y los tipos de SPI son todo lo que quedaba .

Hoy en día se ven algunas implementaciones de Reactive Streams que afirman que su extensión directa de estos tipos es un beneficio por alguna razón. Esto no es correcto, como tal no lo era, y no es el objetivo de las interfaces de Reactive Streams. Es más bien un malentendido de lo que son estos tipos: estrictamente las interfaces entre operaciones que las bibliotecas de Reactive Streams acuerdan entender y "hablar" (un protocolo).

Para referencia, tanto RxJava 2.0 como Reactor extienden directamente estos tipos, mientras que Akka Streams se mantiene fiel al diseño y los principios de la RS, ocultándolos como una interfaz de programación para desarrolladores de aplicaciones, por lo que Sink no extiende al Suscriptor. Esto no tiene nada que ver con ser "soporte nativo", como he visto a las personas afirmar que la relación IS-A directa es (más bien, afirmar que una biblioteca interoperatoria es su "nativo" es un malentendido del concepto).

Fregaderos y suscriptores, fuentes y editores

Los conceptos de sumidero y abonado me parecen similares.

Correctos, son, a propósito y por diseño, similares.

Como un sumidero es una representación elevada de algo que efectivamente produce un suscriptor. Para simplificar, puedes considerarlo como una "fábrica de suscriptores" (más específicamente, el sumidero es el "plano", y el materializador toma el plano del sumidero y crea las etapas RS adecuadas, incluidos editores para fuentes y suscriptores para sumideros. cuando dice Sink.ignore, en realidad es una fábrica que terminará creando un Suscriptor que realiza todas las solicitudes e ignoraciones, de acuerdo con Reactive Streams. Lo mismo con todos los otros métodos declarados en Sink.

Lo mismo se aplica a Source , que se relaciona 1: 1 con un Publisher Reactive Streams. Por lo tanto, un Source.single(1) es algo que se materializará internamente en un Publisher que hace su trabajo: emite ese elemento 1 si se le permite hacerlo por medio de un flujo descendente.

AKA ¿Por qué no hay sumidero en las corrientes reactivas?

Como se mencionó anteriormente, Akka''s Sink no extiende directamente un Suscriptor. Sin embargo, es básicamente una fábrica para ellos.

Puede preguntar: "¿El usuario nunca ve estos tipos de Publicador / Suscriptor durante el uso normal?" Y la respuesta es: sí, y esta es una característica, así como un objetivo de diseño (de acuerdo con lo que es Reactive Streams). Si las instancias subyacentes de Publicador y Suscriptor estaban expuestas a los usuarios todo el tiempo directamente, uno podría llamarlos incorrectamente, causando errores y confusión. Si estos tipos nunca se exponen a menos que se solicite explícitamente, ¡hay menos posibilidades de errores accidentales!

Algunos han entendido mal el diseño y han afirmado que no hay soporte "nativo" para él en Akka Streams (lo que no es cierto). Veamos a través de lo que el hecho de estar separado del suscriptor en la API nos hace ganar:

Además, no veo que el concepto de sumidero se defina explícitamente en la especificación de flujos reactivos.

De hecho, los sumideros no son parte de Reactive Streams, y eso es absolutamente correcto.

Beneficios de evitar el "Suscriptor IS-A Sink"

Sink son parte de Akka Streams, y su propósito es proporcionar el DSL fluido, así como ser fábricas para los Subscribers . En otras palabras, si el Suscriptor son los bloques LEGO, Sink es lo que los construye (y el Akka Stream Materializer es lo que une los distintos bloques LEGO para "ejecutarlos").

De hecho, es beneficioso para los usuarios que Sink no lleve ningún IS-A definitivo con un Suscriptor (sic!) Como lo hacen otras bibliotecas:

Esto se debe a que dado que org.reactivestreams.Subscriber ahora se ha incluido en Java 9 y se ha convertido en parte de Java, las bibliotecas deberían migrar usando java.util.concurrent.Flow.Subscriber lugar de org.reactivestreams.Subscriber . Las bibliotecas que seleccionaron exponer y extender directamente los tipos de Reactive Streams ahora tendrán un tiempo más difícil para adaptar los tipos JDK9: todas sus clases que extiendan al Suscriptor y amigos deberán copiarse o cambiarse para extender la misma interfaz, pero desde una paquete diferente En Akka, simplemente exponemos el nuevo tipo cuando se nos pide que ya sea compatible con los tipos JDK9, desde el día en que se lanzó JDK9.

Dado que Reactive Streams es un SPI, una interfaz de proveedor de servicios, está destinado a que las bibliotecas compartan para que puedan hablar "del mismo tipo y protocolo". Toda la comunicación que hacen Akka Streams y otras bibliotecas de Reactive Streams se adhieren a esas reglas, y si desea conectar alguna otra biblioteca a Akka Streams, haría exactamente eso: dé a Akka Streams el tipo interoperatorio, que es el suscriptor, procesador o editor; no el Fregadero, ya que es el DSL "específico de Akka" (lenguaje específico del dominio) de Akka, que agrega comodidad y otras sutilezas encima de él, ocultando (a propósito!) el tipo de suscriptor.

Otra razón por la que Akka (y para ser honesto, otras implementaciones de RS fueron alentadas a hacerlo también, pero optó por no hacerlo) oculta estos tipos es porque son fáciles de hacer mal. Si distribuye un Suscriptor, cualquiera puede llamar cosas sobre él, e incluso sin saberlo, rompe las reglas y garantiza que la Especificación de Reactive Streams requiere que cualquiera que interactúe con el tipo.

Para evitar que ocurran errores, los tipos de Reactive Streams en Akka Streams están "ocultos" y solo se exponen cuando se los solicita explícitamente, lo que minimiza el riesgo de que las personas cometan errores al llamar accidentalmente a los métodos de los tipos de Reactive Streams sin procesar su protocolo.