Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Adventures with Reactive Streams

Adventures with Reactive Streams

In today's world, more and more people are creating software that is expected to run at large scale. Large scale and cost-effectiveness often lead programmers to asynchronous programming models for handling vertical scalability concerns. Creating an asynchronous application on the JVM is nontrivial and often leads to disparate abstractions. Reactive Streams is one such abstraction that is widely accepted on the JVM today. This presentation is a deep dive into what it takes to implement the Reactive Streams specifications. It delves into various trade-offs that library developers have to consider while implementing Reactive Streams and which aspects of practical applications make such trade-offs easier.

Jo Voordeckers

October 25, 2018
Tweet

More Decks by Jo Voordeckers

Other Decks in Technology

Transcript

  1. Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() transformToJson() HTTP/1.1 200

    OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} …
  2. Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() transformToJson() HTTP/1.1 200

    OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} …
  3. HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First:

    Jo, Last: Voordeckers, Location: SF} … response.send() Find my friends getFriendsCsv() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101
  4. Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() HTTP/1.1 200 OK

    Nitesh, Kant, SF Jo, Voordeckers, SF … 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send()
  5. Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() response.send() HTTP/1.1 200

    OK Nitesh, Kant, SF Jo, Voordeckers, SF … 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send()
  6. Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() response.send() HTTP/1.1 200

    OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send()
  7. Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() response.send() HTTP/1.1 200

    OK Nitesh, Kant, SF Jo, Voordeckers, SF … 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send()
  8. Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() response.send() HTTP/1.1 200

    OK Nitesh, Kant, SF Jo, Voordeckers, SF … 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send()
  9. Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() HTTP/1.1 200 OK

    Nitesh, Kant, SF Jo, Voordeckers, SF … Producer Consumer HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send()
  10. HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First:

    Jo, Last: Voordeckers, Location: SF} … Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … Producer Consumer Control response.send()
  11. I am interested in consuming data! Give me 10 items!

    I am not interested any more. Control
  12. Java Message Service https://docs.oracle.com/javaee/5/tutorial/doc/bncdx.html // Synchronous Message m = consumer.receive();

    Message m = consumer.receive(1000); // time out 1000ms // Asynchronous MessageListener myListener = new MyListener(); // void onMessage(Message message) consumer.setMessageListener(myListener); request(1)
  13. Java Message Service https://docs.oracle.com/javaee/5/tutorial/doc/bncdx.html // Synchronous Message m = consumer.receive();

    Message m = consumer.receive(1000); // time out 1000ms // Asynchronous MessageListener myListener = new MyListener(); // void onMessage(Message message) consumer.setMessageListener(myListener); flow control depends on broker
  14. Apache Kafka https://kafka.apache.org/10/javadoc/?org/apache/kafka/clients/consumer/KafkaConsumer.html KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props); consumer.subscribe(Arrays.asList("foo",

    "bar")); while (true) { ConsumerRecords<String, String> records = consumer.poll(100); // time out for (ConsumerRecord<String, String> record : records) { // iterate over batch } consumer.commitSync(); } // Flow control public void pause(java.util.Collection<TopicPartition> partitions) public void resume(java.util.Collection<TopicPartition> partitions) request(n)
  15. Apache Kafka https://kafka.apache.org/10/javadoc/?org/apache/kafka/clients/consumer/KafkaConsumer.html KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props); consumer.subscribe(Arrays.asList("foo",

    "bar")); while (true) { ConsumerRecords<String, String> records = consumer.poll(100); // time out for (ConsumerRecord<String, String> record : records) { // iterate over batch } consumer.commitSync(); } // Flow control public void pause(java.util.Collection<TopicPartition> partitions) public void resume(java.util.Collection<TopicPartition> partitions) additional flow control by topic/partition
  16. Apache Kafka JMS // Synchronous Message m = consumer.receive(); Message

    m = consumer.receive(1000); // Asynchronous // void onMessage(Message message) MessageListener myListener = new MyListener(); consumer.setMessageListener(myListener); KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props); consumer.subscribe(Arrays.asList("foo", "bar")); while (true) { ConsumerRecords<String, String> records = consumer.poll(100); for (ConsumerRecord<String, String> record : records) { // iterate over batch } consumer.commitSync(); } // Flow control public void pause(java.util.Collection<TopicPartition> partitions) public void resume(java.util.Collection<TopicPartition> partitions)
  17. Apache Kafka JMS // Synchronous Message m = consumer.receive(); Message

    m = consumer.receive(1000); // Asynchronous // void onMessage(Message message) MessageListener myListener = new MyListener(); consumer.setMessageListener(myListener); KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props); consumer.subscribe(Arrays.asList("foo", "bar")); while (true) { ConsumerRecords<String, String> records = consumer.poll(100); for (ConsumerRecord<String, String> record : records) { // iterate over batch } consumer.commitSync(); } // Flow control public void pause(java.util.Collection<TopicPartition> partitions) public void resume(java.util.Collection<TopicPartition> partitions) how do we bridge these APIs?
  18. Service A Service B cache Service C DB ? define

    contract for interoperability
  19. Service A Service B cache Service C DB write adapters

    for all the things \o/ ? ✓ cancelation ✓ buffering
  20. Reactive Streams public interface Subscriber<T> { public void onSubscribe(Subscription s);

    public void onNext(T t); public void onError(Throwable t); public void onComplete(); }
  21. Reactive Streams public interface Subscriber<T> { public void onSubscribe(Subscription s);

    public void onNext(T t); public void onError(Throwable t); public void onComplete(); } Control exchange
  22. Reactive Streams public interface Subscription { public void request(long n);

    public void cancel(); } I am not interested any more.
  23. under-specification risk of chaos between actors of the interaction rules

    Image source: https://newrepublic.com/article/118416/what-dhaka-bangladesh-traffic-capital-world-can-teach-us
  24. under-specification risk of chaos between actors target least common denominator

    of the interaction rules Image source: https://newrepublic.com/article/118416/what-dhaka-bangladesh-traffic-capital-world-can-teach-us
  25. under-specification risk of chaos between actors target least common denominator

    systemic inefficiency of the interaction rules Image source: https://newrepublic.com/article/118416/what-dhaka-bangladesh-traffic-capital-world-can-teach-us
  26. sequence onSubscribe() onComplete() subscribe() request() onSubscribe() subscribe() onError() request() onSubscribe()

    subscribe() onNext() onError() request() onSubscribe() subscribe() onNext() onNext() onComplete() request() request() cancel() onSubscribe() subscribe() request() cancel() onNext() onComplete() onSubscribe() subscribe()
  27. lifecycle management onNext() X subscribe() cancel() request() subscribe() onSubscribe() onNext()

    request() X onNext() onSubscribe() onNext() request() onComplete() X onError() subscribe() X
  28. Single item, asynchronously produced and delivered (Attempt #1) class SingleValue<T>

    implements Publisher<T> { private T value; private boolean subscriberReady; private Subscriber<? super T> subscriber; @Override public void subscribe(Subscriber<? super T> s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { if (value != null) { s.onNext(value); } else { subscriberReady = true; } } @Override public void cancel() { subscriberReady = false; } }); } public void emit(T value) { if (subscriber != null && subscriberReady) { subscriber.onNext(value); } else { this.value = value; } } } * This is a hot/eager source, i.e. the work has started before a Subscriber arrives
  29. Single item, asynchronously produced and delivered (Attempt #1) class SingleValue<T>

    implements Publisher<T> { private T value; private boolean subscriberReady; private Subscriber<? super T> subscriber; @Override public void subscribe(Subscriber<? super T> s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { if (value != null) { s.onNext(value); } else { subscriberReady = true; } } @Override public void cancel() { subscriberReady = false; } }); } public void emit(T value) { if (subscriber != null && subscriberReady) { subscriber.onNext(value); } else { this.value = value; } } } * This is a hot/eager source, i.e. the work has started before a Subscriber arrives emit() subscribe() request() cancel()
  30. Single item, asynchronously produced and delivered (Attempt #1) * This

    is a hot/eager source, i.e. the work has started before a Subscriber arrives Asynchrony emit() subscribe() request() cancel() class SingleValue<T> implements Publisher<T> { private T value; private boolean subscriberReady; private Subscriber<? super T> subscriber; @Override public void subscribe(Subscriber<? super T> s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { if (value != null) { s.onNext(value); } else { subscriberReady = true; } } @Override public void cancel() { subscriberReady = false; } }); } public void emit(T value) { if (subscriber != null && subscriberReady) { subscriber.onNext(value); } else { this.value = value; } } }
  31. Single item, asynchronously produced and delivered (Attempt #1) * This

    is a hot/eager source, i.e. the work has started before a Subscriber arrives Asynchrony emit() subscribe() request() cancel() class SingleValue<T> implements Publisher<T> { private T value; private boolean subscriberReady; private Subscriber<? super T> subscriber; @Override public void subscribe(Subscriber<? super T> s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { if (value != null) { s.onNext(value); } else { subscriberReady = true; } } @Override public void cancel() { subscriberReady = false; } }); } public void emit(T value) { if (subscriber != null && subscriberReady) { subscriber.onNext(value); } else { this.value = value; } } }
  32. Single item, asynchronously produced and delivered (Attempt #1) * This

    is a hot/eager source, i.e. the work has started before a Subscriber arrives Asynchrony emit() subscribe() request() cancel() class SingleValue<T> implements Publisher<T> { private T value; private boolean subscriberReady; private Subscriber<? super T> subscriber; @Override public void subscribe(Subscriber<? super T> s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { if (value != null) { s.onNext(value); } else { subscriberReady = true; } } @Override public void cancel() { subscriberReady = false; } }); } public void emit(T value) { if (subscriber != null && subscriberReady) { subscriber.onNext(value); } else { this.value = value; } } }
  33. class SingleValue<T> implements Publisher<T> { private T value; private boolean

    subscriberReady; private Subscriber<? super T> subscriber; @Override public void subscribe(Subscriber<? super T> s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { if (value != null) { s.onNext(value); } else { subscriberReady = true; } } @Override public void cancel() { subscriberReady = false; } }); } public void emit(T value) { if (subscriber != null && subscriberReady) { subscriber.onNext(value); } else { this.value = value; } } } Single item, asynchronously produced and delivered (Attempt #1) * This is a hot/eager source, i.e. the work has started before a Subscriber arrives Asynchrony emit() subscribe() request() cancel()
  34. Single item, asynchronously produced and delivered (Attempt #1) * This

    is a hot/eager source, i.e. the work has started before a Subscriber arrives Concurrency emit() subscribe() request() cancel() class SingleValue<T> implements Publisher<T> { private T value; private boolean subscriberReady; private Subscriber<? super T> subscriber; @Override public void subscribe(Subscriber<? super T> s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { if (value != null) { s.onNext(value); } else { subscriberReady = true; } } @Override public void cancel() { subscriberReady = false; } }); } public void emit(T value) { if (subscriber != null && subscriberReady) { subscriber.onNext(value); } else { this.value = value; } } }
  35. Single item, asynchronously produced and delivered (Attempt #1) * This

    is a hot/eager source, i.e. the work has started before a Subscriber arrives Concurrency emit() subscribe() request() cancel() class SingleValue<T> implements Publisher<T> { private T value; private boolean subscriberReady; private Subscriber<? super T> subscriber; @Override public void subscribe(Subscriber<? super T> s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { if (value != null) { s.onNext(value); } else { subscriberReady = true; } } @Override public void cancel() { subscriberReady = false; } }); } public void emit(T value) { if (subscriber != null && subscriberReady) { subscriber.onNext(value); } else { this.value = value; } } }
  36. Single item, asynchronously produced and delivered (Attempt #1) * This

    is a hot/eager source, i.e. the work has started before a Subscriber arrives Concurrency emit() subscribe() request() cancel() class SingleValue<T> implements Publisher<T> { private T value; private boolean subscriberReady; private Subscriber<? super T> subscriber; @Override public void subscribe(Subscriber<? super T> s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { if (value != null) { s.onNext(value); } else { subscriberReady = true; } } @Override public void cancel() { subscriberReady = false; } }); } public void emit(T value) { if (subscriber != null && subscriberReady) { subscriber.onNext(value); } else { this.value = value; } } }
  37. Single item, asynchronously produced and delivered (Attempt #1) * This

    is a hot/eager source, i.e. the work has started before a Subscriber arrives Concurrency emit() subscribe() request() cancel() X class SingleValue<T> implements Publisher<T> { private T value; private boolean subscriberReady; private Subscriber<? super T> subscriber; @Override public void subscribe(Subscriber<? super T> s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { if (value != null) { s.onNext(value); } else { subscriberReady = true; } } @Override public void cancel() { subscriberReady = false; } }); } public void emit(T value) { if (subscriber != null && subscriberReady) { subscriber.onNext(value); } else { this.value = value; } } }
  38. Single item, asynchronously produced and delivered (Attempt #1) * This

    is a hot/eager source, i.e. the work has started before a Subscriber arrives Concurrency emit() subscribe() request() cancel() https://github.com/reactive-streams/reactive-streams-jvm#1.3 class SingleValue<T> implements Publisher<T> { private T value; private boolean subscriberReady; private Subscriber<? super T> subscriber; @Override public void subscribe(Subscriber<? super T> s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { if (value != null) { s.onNext(value); s.onComplete(); } else { subscriberReady = true; } } @Override public void cancel() { subscriberReady = false; } }); } public void emit(T value) { if (subscriber != null && subscriberReady) { subscriber.onNext(value); } else { this.value = value; } } }
  39. Single item, asynchronously produced and delivered (Attempt #1) * This

    is a hot/eager source, i.e. the work has started before a Subscriber arrives Concurrency emit() subscribe() request() cancel() https://github.com/reactive-streams/reactive-streams-jvm#2.7 class SingleValue<T> implements Publisher<T> { private T value; private boolean subscriberReady; private Subscriber<? super T> subscriber; @Override public void subscribe(Subscriber<? super T> s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { if (value != null) { s.onNext(value); s.onComplete(); } else { subscriberReady = true; } } @Override public void cancel() { subscriberReady = false; } }); } public void emit(T value) { if (subscriber != null && subscriberReady) { subscriber.onNext(value); } else { this.value = value; } } }
  40. Single item, asynchronously produced and delivered (Attempt #2) * This

    is a hot/eager source, i.e. the work has started before a Subscriber arrives Concurrency emit() subscribe() request() cancel() class SingleValue<T> implements Publisher<T> { private T value; private boolean subscriberReady; private Subscriber<? super T> subscriber; @Override public void subscribe(Subscriber<? super T> s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { if (value != null) { s.onNext(value); s.onComplete(); } else { subscriberReady = true; } } @Override public void cancel() { subscriberReady = false; } }); } public void emit(T value) { if (subscriber != null && subscriberReady) { subscriber.onNext(value); } else { this.value = value; } } }
  41. Single item, asynchronously produced and delivered (Attempt #2) * This

    is a hot/eager source, i.e. the work has started before a Subscriber arrives Concurrency emit() subscribe() request() cancel() class SingleValue<T> implements Publisher<T> { private T value; private boolean subscriberReady; private Subscriber<? super T> subscriber; @Override public synchronized void subscribe(Subscriber<? super T> s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { if (value != null) { s.onNext(value); s.onComplete(); } else { subscriberReady = true; } } @Override public void cancel() { subscriberReady = false; } }); } public synchronized void emit(T value) { if (subscriber != null && subscriberReady) { subscriber.onNext(value); } else { this.value = value; } } }
  42. Single item, asynchronously produced and delivered (Attempt #3) * This

    is a hot/eager source, i.e. the work has started before a Subscriber arrives https://github.com/reactive-streams/reactive-streams-jvm#3.4
  43. class SingleValue<T> implements Publisher<T> { private static final int IDLE

    = 0; private static final int REQUESTED = 1; private static final int EMITTED = 2; private T value; private Subscriber<? super T> subscriber; private final AtomicInteger state = new AtomicInteger(IDLE); @Override public void subscribe(Subscriber<? super T> s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { int old = state.getAndSet(REQUESTED); if (old == EMITTED) { s.onNext(value); s.onComplete(); } } @Override public void cancel() { } }); } public void emit(T value) { this.value = value; if (state.getAndSet(EMITTED) == REQUESTED) { subscriber.onNext(value); subscriber.onComplete(); } } } Single item, asynchronously produced and delivered (Attempt #4) * This is a hot/eager source, i.e. the work has started before a Subscriber arrives State Machine IDLE EMITTED REQUESTED
  44. Single item, asynchronously produced and delivered (Attempt #4) * This

    is a hot/eager source, i.e. the work has started before a Subscriber arrives State Machine IDLE class SingleValue<T> implements Publisher<T> { private static final int IDLE = 0; private static final int REQUESTED = 1; private static final int EMITTED = 2; private T value; private Subscriber<? super T> subscriber; private final AtomicInteger state = new AtomicInteger(IDLE); @Override public void subscribe(Subscriber<? super T> s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { int old = state.getAndSet(REQUESTED); if (old == EMITTED) { s.onNext(value); s.onComplete(); } } @Override public void cancel() { } }); } public void emit(T value) { this.value = value; if (state.getAndSet(EMITTED) == REQUESTED) { subscriber.onNext(value); subscriber.onComplete(); } } } EMITTED REQUESTED
  45. Single item, asynchronously produced and delivered (Attempt #5) * This

    is a hot/eager source, i.e. the work has started before a Subscriber arrives State Machine IDLE class SingleValue<T> implements Publisher<T> { private static final int IDLE = 0; private static final int REQUESTED = 1; private static final int EMITTED = 2; private static final int CANCELLED = 3; private T value; private Subscriber<? super T> subscriber; private final AtomicInteger state = new AtomicInteger(IDLE); @Override public void subscribe(Subscriber<? super T> s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { int old = state.getAndSet(REQUESTED); if (old == EMITTED) { s.onNext(value); s.onComplete(); } } @Override public void cancel() { state.set(CANCELLED); } }); } public void emit(T value) { this.value = value; if (state.getAndSet(EMITTED) == REQUESTED) { subscriber.onNext(value); subscriber.onComplete(); } } } EMITTED REQUESTED CANCELLED
  46. class SingleValue<T> implements Publisher<T> { private static final int IDLE

    = 0; private static final int REQUESTED = 1; private static final int EMITTED = 2; private static final int CANCELLED = 3; private T value; private Subscriber<? super T> subscriber; private final AtomicInteger state = new AtomicInteger(IDLE); @Override public void subscribe(Subscriber<? super T> s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { int old = state.getAndSet(REQUESTED); if (old == EMITTED) { s.onNext(value); s.onComplete(); } } @Override public void cancel() { state.set(CANCELLED); } }); } public void emit(T value) { this.value = value; if (state.getAndSet(EMITTED) == REQUESTED) { subscriber.onNext(value); subscriber.onComplete(); } } } Single item, asynchronously produced and delivered (Attempt #5) * This is a hot/eager source, i.e. the work has started before a Subscriber arrives State Machine IDLE EMITTED REQUESTED CANCELLED
  47. Single item, asynchronously produced and delivered (Attempt #5) * This

    is a hot/eager source, i.e. the work has started before a Subscriber arrives subscription.cancel(); subscription.request(1) class SingleValue<T> implements Publisher<T> { private static final int IDLE = 0; private static final int REQUESTED = 1; private static final int EMITTED = 2; private static final int CANCELLED = 3; private T value; private Subscriber<? super T> subscriber; private final AtomicInteger state = new AtomicInteger(IDLE); @Override public void subscribe(Subscriber<? super T> s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { int old = state.getAndSet(REQUESTED); if (old == EMITTED) { s.onNext(value); s.onComplete(); } } @Override public void cancel() { state.set(CANCELLED); } }); } public void emit(T value) { this.value = value; if (state.getAndSet(EMITTED) == REQUESTED) { subscriber.onNext(value); subscriber.onComplete(); } } }
  48. Single item, asynchronously produced and delivered (Attempt #6) * This

    is a hot/eager source, i.e. the work has started before a Subscriber arrives State Machine IDLE class SingleValue<T> implements Publisher<T> { private static final int IDLE = 0; private static final int REQUESTED = 1; private static final int EMITTED = 2; private T value; private Subscriber<? super T> subscriber; private final AtomicInteger state = new AtomicInteger(IDLE); @Override public void subscribe(Subscriber<? super T> s) { subscriber = s; s.onSubscribe(new Subscription() { @Override public void request(long n) { int old = state.getAndSet(REQUESTED); if (old == EMITTED) { s.onNext(value); s.onComplete(); } } @Override public void cancel() { } }); } public void emit(T value) { this.value = value; if (state.getAndSet(EMITTED) == REQUESTED) { subscriber.onNext(value); subscriber.onComplete(); } } } EMITTED REQUESTED https://github.com/reactive-streams/reactive-streams-jvm#3.6
  49. Single item, asynchronously produced and delivered (Attempt #6) * This

    is a hot/eager source, i.e. the work has started before a Subscriber arrives class SingleValue<T> implements Publisher<T> { private static final int IDLE = 0; private static final int REQUESTED = 1; private static final int EMITTED = 2; private static final int CANCELLED = 3; private T value; private Subscriber<? super T> subscriber; private final AtomicInteger state = new AtomicInteger(IDLE); @Override public void subscribe(Subscriber<? super T> s) { s.onSubscribe(new Subscription() { @Override public void request(long n) { int old = state.getAndUpdate(val -> val == CANCELLED ? CANCELLED : REQUESTED); if (old == EMITTED) { s.onNext(value); s.onComplete(); } } @Override public void cancel() { state.set(CANCELLED); } }); subscriber = s; } public void emit(T value) { this.value = value; int old = state.getAndUpdate(val -> val == CANCELLED ? CANCELLED : EMITTED); if (old == REQUESTED) { subscriber.onNext(value); subscriber.onComplete(); } } }
  50. class SingleValue<T> implements Publisher<T> { private static final int IDLE

    = 0; private static final int REQUESTED = 1; private static final int EMITTED = 2; private static final int CANCELLED = 3; private T value; private Subscriber<? super T> subscriber; private final AtomicInteger state = new AtomicInteger(IDLE); @Override public void subscribe(Subscriber<? super T> s) { s.onSubscribe(new Subscription() { @Override public void request(long n) { int old = state.getAndUpdate(val -> val == CANCELLED ? CANCELLED : REQUESTED); if (old == EMITTED) { s.onNext(value); s.onComplete(); } } @Override public void cancel() { state.set(CANCELLED); } }); subscriber = s; } public void emit(T value) { this.value = value; int old = state.getAndUpdate(val -> val == CANCELLED ? CANCELLED : EMITTED); if (old == REQUESTED) { subscriber.onNext(value); subscriber.onComplete(); } } } Single item, asynchronously produced and delivered (Attempt #6) * This is a hot/eager source, i.e. the work has started before a Subscriber arrives
  51. Single item, asynchronously produced and delivered (Attempt #6) * This

    is a hot/eager source, i.e. the work has started before a Subscriber arrives class SingleValue<T> implements Publisher<T> { private static final int IDLE = 0; private static final int REQUESTED = 1; private static final int EMITTED = 2; private static final int TERMINATED = 3; private T value; private Subscriber<? super T> subscriber; private final AtomicInteger state = new AtomicInteger(IDLE); @Override public void subscribe(Subscriber<? super T> s) { s.onSubscribe(new Subscription() { @Override public void request(long n) { if (n <= 0 && state.getAndSet(TERMINATED) != TERMINATED) { s.onError(new IllegalArgumentException("Non positive request signals are illegal.")); return; } int old = state.getAndUpdate(val -> val == TERMINATED ? TERMINATED : REQUESTED); if (old == EMITTED) { state.set(TERMINATED); s.onNext(value); s.onComplete(); } } @Override public void cancel() { state.set(TERMINATED); } }); subscriber = s; } public void emit(T value) { this.value = value; int old = state.getAndUpdate(val -> val == TERMINATED ? TERMINATED : EMITTED); if (old == REQUESTED) { state.set(TERMINATED); subscriber.onNext(value); subscriber.onComplete(); } } } Want to see how multiple item emission will look like?
  52. response.send() Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() response.send() HTTP/1.1

    200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 011101100 101110011 101100101 110011100 101110111 011001101 HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} …
  53. Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() transformToJson() HTTP/1.1 200

    OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} …
  54. Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() transformToJson() HTTP/1.1 200

    OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … Socket Socket Subscriber Publisher
  55. Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() transformToJson() HTTP/1.1 200

    OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … Socket Socket Subscriber Publisher Control
  56. Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() transformToJson() HTTP/1.1 200

    OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … Socket Socket Subscriber Publisher subscription.request(1)
  57. Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() transformToJson() HTTP/1.1 200

    OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … Socket Socket Subscriber Publisher subscription.request(1) Demand is required to compose through multiple components
  58. Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() transformToJson() HTTP/1.1 200

    OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … Socket Socket Subscriber Publisher subscription.cancel() Cancellation is required to compose through multiple components
  59. Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() transformToJson() HTTP/1.1 200

    OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … Socket Socket Subscriber Publisher Publisher client.get("friends?id=1") response.send(Publisher resp)
  60. Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() transformToJson() HTTP/1.1 200

    OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … Socket Socket Subscriber Publisher response.send( client.get("friends?id=1") );
  61. Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() transformToJson() HTTP/1.1 200

    OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … Socket Socket Subscriber Publisher List<Friend> transformToJson(List<String> csvs) { List<Friend> friends; for (String csv : csvs) { friends.add(toJson(csv)); } return friends; }
  62. Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() transformToJson() HTTP/1.1 200

    OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … Socket Socket Subscriber Publisher response.send( client.get("friends?id=1") );
  63. Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() transformToJson() HTTP/1.1 200

    OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … Socket Socket Subscriber Publisher response.send( client.get("friends?id=1") .map(csv -> toJson(csv)) );
  64. Find my friends GET /friends?id=3 HTTP/1.1 getFriendsCsv() transformToJson() HTTP/1.1 200

    OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … response.send() HTTP/1.1 200 OK Nitesh, Kant, SF Jo, Voordeckers, SF … HTTP/1.1 200 OK {First: Nitesh, Last: Kant, Location: SF} {First: Jo, Last: Voordeckers, Location: SF} … Socket Socket Subscriber Publisher response.send( client.get("friends?id=1") .map(csv -> toJson(csv)) ); Function composition.