Skip to content

Instantly share code, notes, and snippets.

@pmhsfelix
Last active January 5, 2021 18:03
Show Gist options
  • Save pmhsfelix/b21450e6b37d1e8f6b51ced9623585a7 to your computer and use it in GitHub Desktop.
Save pmhsfelix/b21450e6b37d1e8f6b51ced9623585a7 to your computer and use it in GitHub Desktop.
Reactive streams short course
  1. Motivation

    • Reactive streams as extended futures.
      • A future (potentially) produces a single value at a single moment in time. The value can be a list, but all the list's elements need to be produced at the same time. However there are activities that produce multiple values, spread over time (e.g. v_0 at t_0, v_1 at t_1, v_2 at t_2 ).
      • A future represents an operation that already started. There isn't anything on the future's interface to start (or restart) a new operation.
      • A reactive stream can be viewed as a future that produces more than one value through its lifetime, and whose operation is only triggered when a consumer subscribes to the stream.
    • Reactive streams as push-style iterables/enumerables.
      • On a iterable/enumerable, elements are pulled by consumers (e.g. by calling next or MoveNext). If a value is not available, that call will block until the value becomes. Due to this, they aren't a good fit to when the element's availability is spread out through time and threads shouldn't be blocked during those pull operations.
      • A reactive stream can be viewed as an iterable/enumerable where the source pushes the value to the consumers, instead of consumers pulling the value.
  2. The Java 9 Flow.* interfaces and the relation between them.

    • A Publisher<T> is a producer of T items.
    • A Subscriber<T> is a consumer of T items.
    • A Subscriber<T> can be subscribed (i.e. connected) to a Publisher<T>.
      • As a consequence, the Publisher<T> will call Subscriber<T>::obSubscribed, passing in a Subscription.
      • The Subscription object can be used by the Subscriber<T> to control its connection to the Publisher<T>, namely:
        • Cancel the subscription - cancel method.
        • Request more data to be sent (back-pressure) - request(long) method.
    • The data items are pushed to the subscriber via its onNext methods. When there is not more data or there was an error, the onComplete or onError methods are called (respectively).
    • The sequence of methods called on a Subscriber is onSubscribe onNext* (onError | onComplete)?.
  3. Laws

    • A Publisher implementation as a set of laws, not directly visible in the types, which act as:
      • Guarantees for the Publisher consumer.
      • Obligations for the Publisher implementer.
      • These laws are described in the reactive streams specification, and include items such as:
        • The number of items pushed to a subscriber is less or equal to the number of items requested via the subscription.
        • There is a happens-before relation between requesting elements and receiving those elements.
        • The Subscriber methods are called (signalled) serially, meaning that there is a happens-before (HB) relation between those calls.
          • There is an HB between the onSubscribe and the first onNext.
          • There is an HB between the onNext with the n-th item and the onNext with the (n+1)-th item.
    • Subscriber and Subscription also have laws.
  4. Flow.* vs. RxJava vs. Reactor vs. org.reactivestreams

    • The Flow.* interfaces are only available since Java 9. However Java 8 is still very popular and most libraries do support it.
    • The Flow.* only has interfaces without any behavior. However, most of the usefulness of reactive streams lays on the operators - ways of combining reactive streams into new reactive streams. Without that, the Flow.* are almost useless. It is similar to Java 8 introducing CompletionStage without adding CompletableFuture.
    • org.reactivestreams provides interfaces compatible with Java 8. But they are still interfaces; nothing more.
    • RxJava and Reactor are libraries providing implementations of the Publisher interface. More importantly, they provides operators to combine publishers into new publishers. They also provide ways to create publishers.
    • In this course we will use Reactor, mainly because it is the library used in the Spring eco-system. RxJava provides equivalent functionality.
  5. Reactor basics

    • The Flux class is Reactor's Publisher interface.
      • Implements the org.reactivestreams.Publisher interface (and not the Flow.Publisher interface because it supports Java 8).
    • Provides multiple static factory methods.
    • Provides operators as Flux instance methods, allowing for usage as method chaining. Note that since Java doesn't have extension methods, these methods need to belong to the interface.
    Flux<Integer> intPublisher = ...
    Flux<String> stringPublisher = intPublisher.map(i -> i.toString());
    • Reactor also provides the Mono class, which is a specialization of a Publisher that emits at most one value (it is similar to a future, with subscription capabilities).
  6. Simple examples

    • Flux.just with a simple subscriber.
    • Flux.create and asynchronous behaviour.
    • Using a simple function as an unbounded subscriber.
    • map operator.
    • retry operator.
    • cache operator.
    • zip operator.
  7. Threading

    • No implicit threading model
    • Ability to change the "thread" (i.e. the scheduler) where the subscription and publishing occurs.
    • See example using publishOn and subscribeOn.
  8. HttpClient example with a custom BodyHandler

    • See how the HttpClient uses a Subscriber to handle the response body.
  9. Backpressure

    • ...
    • Sinks and associated challenges.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment