Slide 1

Slide 1 text

© Copyright 2017 Pivotal Software, Inc. All rights Reserved. Version 1.0 Stéphane MALDINI Simon BASLÉ Project Reactor Now & Tomorrow SpringOne Platform 2017

Slide 2

Slide 2 text

Stéphane Maldini Senior Product Manager & Lead Engineer Project Reactor, Pivotal & @smaldini Simon Baslé @simonbasle Staff Software Engineer Project Reactor, Pivotal

Slide 3

Slide 3 text

Now 3.0 → 3.1

Slide 4

Slide 4 text

Is this a big deal ? Spoiler: yes

Slide 5

Slide 5 text

Reactor 3.0 Versioning scheme: ERA.MAJOR.MINOR Production ready, but had to be out before Spring Framework 5 Still room for evolution after plenty of feedback

Slide 6

Slide 6 text

Project ____ Downloads : > 300k/month projectreactor.io : > 30k unique/month Gitter.im : #7 Java channel

Slide 7

Slide 7 text

Project ____ 452 Closed Pull Requests 487 Closed Issues 11 Releases 53 Contributors 4 (+2) Core Committers

Slide 8

Slide 8 text

Shoutout to our Contributors

Slide 9

Slide 9 text

Why users want Reactor ?

Slide 10

Slide 10 text

Why users want Reactor ? Reactive Streams sample extract reactive-streams.org

Slide 11

Slide 11 text

Why users want Reactor ? Flux.range(from, count)

Slide 12

Slide 12 text

Do you speak Reactive Streams ?

Slide 13

Slide 13 text

Reactor and Reactive Streams

Slide 14

Slide 14 text

Publisher & Subscriber

Slide 15

Slide 15 text

Publisher

Slide 16

Slide 16 text

Subscriber

Slide 17

Slide 17 text

Publisher void subscribe(Subscriber)

Slide 18

Slide 18 text

Subscriber void onNext(T) void onComplete() void onError(Throwable)

Slide 19

Slide 19 text

Subscription [Music]

Slide 20

Slide 20 text

Subscription void request(long) void cancel()

Slide 21

Slide 21 text

Subscriber void onSubscribe(Subscription) void onNext(T) void onComplete() void onError(Throwable)

Slide 22

Slide 22 text

This is Flow Control

Slide 23

Slide 23 text

Flux 0-N elements “classic” Publisher

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

Mono 0-1 elements “specialized” Publisher

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

String res; for(int i = 1; i <= 1000; i++) { res = blockingHttpGet(“/quote/”+i); handleBody(res); } Without --------

Slide 28

Slide 28 text

String res; for(int i = 1; i <= 1000; i++) { res = blockingHttpGet(“/quote/”+i); handleBody(res); } 1000 calls later... Main Thread Without --------

Slide 29

Slide 29 text

Flux.range(1, 1000) .flatMap(i -> reactiveHttpCall("/quote/"+i)) .subscribe(this::handleBody); With --------

Slide 30

Slide 30 text

Flux.range(1, 1000) .flatMap(i -> reactiveHttpCall("/quote/"+i)) .subscribe(this::handleBody); operator With --------

Slide 31

Slide 31 text

Flux.range(1, 1000) .flatMap(i -> reactiveHttpCall("/quote/"+i)) .subscribe(this::handleBody); 1 call later Main Thread With --------

Slide 32

Slide 32 text

Flux.range(1, 1000) .flatMap(i -> reactiveHttpCall("/quote/"+i)) .subscribe(this::handleBody); 256 http calls With --------

Slide 33

Slide 33 text

Flux.range(1, 1000) .flatMap(i -> reactiveHttpCall("/quote/"+i), 1) .subscribe(this::handleBody); With -------- 1 http call

Slide 34

Slide 34 text

Flux.range(1, 1000) .flatMap(i -> reactiveHttpCall("/quote/"+i), 1) .subscribe(this::handleBody); With -------- J-j-jeez Rick, I like this backpressure stuff 1 http call

Slide 35

Slide 35 text

At every stage, Flux and Mono are Publisher

Slide 36

Slide 36 text

interface SomeReactiveApi { Flux findUsersLike(Publisher usernames) } //... someReactiveApi.findUsersLike(Mono.just(“ricksanchez”)) .subscribe(handleUsers); Everywhere

Slide 37

Slide 37 text

interface SomeReactiveApi { Flux findUsersLike(Publisher usernames) } //... someReactiveApi.findUsersLike(Mono.just(“ricksanchez”)) .subscribe(handleUsers); //or someReactiveApi.findUsersLike(someFlux.filter(“sanchez”::endsWith)) .subscribe(handleUsers); Everywhere

Slide 38

Slide 38 text

interface SomeReactiveApi { Flux findUsersLike(Publisher usernames) } //... someReactiveApi.findUsersLike(Mono.just(“ricksanchez”)) .subscribe(handleUsers); //or someReactiveApi.findUsersLike(someFlux.filter(“sanchez”::endsWith)) .subscribe(handleUsers); Everywhere Look Morty, the universe wants API consistency !

Slide 39

Slide 39 text

Reactor Core 3.1 Hot from the oven

Slide 40

Slide 40 text

3.0 → LTS 3.1

Slide 41

Slide 41 text

3.1 is a polished programming experience

Slide 42

Slide 42 text

Flux Mono Aligning APIs between Flux and Mono

Slide 43

Slide 43 text

Flux Mono flatmap then

Slide 44

Slide 44 text

Flux Mono flatmap then

Slide 45

Slide 45 text

Flux Mono flatmap flatmap

Slide 46

Slide 46 text

No content

Slide 47

Slide 47 text

Practical static extensions Initial N ullability support

Slide 48

Slide 48 text

“Reactor has matured, laying out the foundations for frameworks and users to integrate their context into the Core .” Enterprise Ready: Hooks & Customization, Matured API

Slide 49

Slide 49 text

Cross-Cutting Concerns

Slide 50

Slide 50 text

Scannable Visit the hierarchy of operators Scan at each step for Attributes... … either upstream (parents()) or downstream (actuals())

Slide 51

Slide 51 text

Tagging and Naming give a chain of operator a name give a chain of operator attributes

Slide 52

Slide 52 text

Tagging and Naming Flux.range(1, 10) .name("myRange") .tag("createdBy", "rick");

Slide 53

Slide 53 text

Hooks On last operator instantiation before subscribe On every operator instantiation

Slide 54

Slide 54 text

Hooks.onLastOperator( Operators.lift(scannable -> scannable.tags() .anyMatch(t -> t.getT1() .contains("createdBy")), (scannable, subscriber) -> { fooService.registerOwnedFlux(scannable); return subscriber; })); Hooks

Slide 55

Slide 55 text

Hooks.onLastOperator( Operators.lift(scannable -> scannable.tags() .anyMatch(t -> t.getT1() .contains("createdBy")), (scannable, subscriber) -> { fooService.registerOwnedFlux(scannable); return subscriber; })); Hooks Once every stream... Only visit streams containing tag... Notify some service with the stream reference and return the operator subscriber

Slide 56

Slide 56 text

Possible Application: Metrics E.g. tag a Flux with “METRICS” and a framework could pick that up and add an operator that increments counters/timers...

Slide 57

Slide 57 text

Improved Testing Support

Slide 58

Slide 58 text

reactor-test StepVerifier to assert expectations on any Publisher TestPublisher to simulate a reactive source PublisherProbe to check which path was used in complex chains

Slide 59

Slide 59 text

StepVerifier StepVerifier.withVirtualTime( () -> Flux.interval(Duration.ofSeconds(45)).take(2)) .thenAwait(Duration.ofSeconds(50)) .expectNext(0L) .thenAwait(Duration.ofSeconds(40)) .assertNext(v -> assertThat(v).isGreaterThan(0L)) .expectComplete() .verify();

Slide 60

Slide 60 text

TestPublisher TestPublisher.create() .next(1, 2, 3, 4) .complete(); TestPublisher.createNonCompliant(Violation.ALLOW_NULLS) .emit(1, 2, 3, null, 4); next() + complete()

Slide 61

Slide 61 text

PublisherProbe probe = PublisherProbe.of(Flux.range(1, 10)); reactiveConditionalPath(Mono.just(1), probe.flux()) .subscribe(); probe.assertWasSubscribed(); PublisherProbe

Slide 62

Slide 62 text

The Context

Slide 63

Slide 63 text

Immutable Context a = Context.of(“key”, “value”); Context b = a.put(“key”, “value2”); a.get(“key”); // ⇒ “value” b.get(“key”); // ⇒ “value2”

Slide 64

Slide 64 text

Propagates during Subscription [a=2,b=2] [a=1,b=2] [] ctx.put(b,2) [a=1] ctx.put(a,1) ) subscribe( Subscriber Subscribe & Context Data ctx.put(a,2)

Slide 65

Slide 65 text

See Reference Guide for Snippets like this: String key = "message"; Mono r = Mono.just("Hello") .subscriberContext(ctx -> ctx.put(key,"World")) .flatMap( s -> Mono .subscriberContext() .map(ctx -> s + " " + ctx.getOrDefault(key, "Stranger")) ); StepVerifier.create(r) .expectNext("Hello Stranger") .verifyComplete();

Slide 66

Slide 66 text

More about Bismuth Release https://spring.io/blog/2017/09/28/reactor-bismuth-is-out Executive summary of latest 3.1 additions

Slide 67

Slide 67 text

What’s new in The Ecosystem ? In-house or community grown initiatives

Slide 68

Slide 68 text

reactor-netty

Slide 69

Slide 69 text

Reactor Netty only turns Netty into a Reactive Streams ready bridge

Slide 70

Slide 70 text

Just a few threads for a production-ready volume of connections with various latencies write read select worker worker worker

Slide 71

Slide 71 text

Non Blocking TCP, UDP and HTTP Decouples Read and Write Pauses reading on local Backpressure Pauses local producing on Backpressure

Slide 72

Slide 72 text

Webflux WebClient Cloud Gateway Boot 2.0 Used and Distributed by Spring

Slide 73

Slide 73 text

Vendor warning

Slide 74

Slide 74 text

Reactor Netty 0.7 API

Slide 75

Slide 75 text

Reactor Netty 0.7 API

Slide 76

Slide 76 text

Seriously, it’s 0.7.2. It runs well. But we’re polishing API before 0.8.0.M1 Best to use--------

Slide 77

Slide 77 text

reactor-kafka

Slide 78

Slide 78 text

Publish & Consume messages from Kafka... … with a backpressure-ready Reactive API https://github.com/reactor/reactor-kafka Reactor-Kafka

Slide 79

Slide 79 text

Reactor-Kafka KafkaSender.create() .send(Flux.range(1, 10) .map(i -> SenderRecord.create(topic, partition, timestamp, i, "msg_" + i,i))) .subscribe();

Slide 80

Slide 80 text

reactor-rabbitmq

Slide 81

Slide 81 text

Reactor-RabbitMQ Publish & Consume messages from RabbitMQ... … with a backpressure-ready Reactive API https://github.com/reactor/reactor-rabbitmq

Slide 82

Slide 82 text

Reactor-RabbitMQ Flux inboundFlux = ReactorRabbitMq.receiver(receiverOptions) .consumeNoAck("reactive.queue") .subscribe(messageHandler);

Slide 83

Slide 83 text

reactor-addons

Slide 84

Slide 84 text

Reactor-Addons Extend Reactor Core API Adapt Reactor to different execution contracts https://github.com/reactor/reactor-addons

Slide 85

Slide 85 text

Retry Support Factory methods that provide a fluent API to incrementally configure a Function suitable for Flux.retryWhen. (tip: use static import) Retry.allBut(Class...) Retry.any() Retry.anyOf(Class...) Retry.onlyIf(Predicate)

Slide 86

Slide 86 text

Retry Support flux.retryWhen(allBut(BusinessException.class) .retryMax(3) .exponentialBackoff(Duration.ofMillis(100), Duration.ofMillis(500)) .timeout(Duration.ofMillis(1500)) .jitter(Jitter.random()) .doOnRetry(this::retryCallback) );

Slide 87

Slide 87 text

Math Take a Flux and apply operations to its whole set of elements using MathFlux static methods. Mono sum = Flux.range(1, 10) .as(MathFlux::sumLong);

Slide 88

Slide 88 text

Math (with kotlin) Mono sum = Flux.range(1, 10) .sumLong();

Slide 89

Slide 89 text

An opinionated abstraction that helps you store and lookup a Mono or a Flux to/from a cache. CacheMono will store the Mono value or completion as a Signal. CacheFlux will collect the elements and terminal event as a List. Both only work with finite sources. Cache Support

Slide 90

Slide 90 text

Mono test = CacheMono.lookup(reader(data), "foo") .onCacheMissResume(() -> Mono.just(1L)) .andWriteWith(writer(data)); Reader and writer interfaces to adapt to any type of cache implementation + out-of-the-box variant for Map. Cache Support

Slide 91

Slide 91 text

Community Highlights

Slide 92

Slide 92 text

Spring Ecosystem #1 user and source of feedback

Slide 93

Slide 93 text

No content

Slide 94

Slide 94 text

No content

Slide 95

Slide 95 text

Reactive talk with

Slide 96

Slide 96 text

No content

Slide 97

Slide 97 text

reactive-gRPC https://github.com/salesforce/reactive-grpc Integrates reactive programming with grpc-java From --

Slide 98

Slide 98 text

Lettuce http://lettuce.io A Reactor-based reactive driver for Redis

Slide 99

Slide 99 text

Tech.io https://tech.io/playgrounds/929/reactive-programming-with-reactor-3 A community-driven programming learning platform

Slide 100

Slide 100 text

CloudFoundry Java Client https://github.com/cloudfoundry/cf-java-client The official Java client for CloudFoundry API

Slide 101

Slide 101 text

https://github.com/reactor/reactor-scala-extensions Idiomatic Scala for Reactor

Slide 102

Slide 102 text

Reactor Tomorrow Near-and-far Future

Slide 103

Slide 103 text

Near-and-far Future Reactor Tomorrow

Slide 104

Slide 104 text

Reactor Tomorrow Near-and-far future

Slide 105

Slide 105 text

2019 2020 2017 2018 the Road from Here

Slide 106

Slide 106 text

2017 First, a patch release

Slide 107

Slide 107 text

3.1.3 3.1.0 September October November December 3.1.1 3.1.2

Slide 108

Slide 108 text

3.1.3 3.1.0 September October November December 3.1.1 3.1.2 limitRequest, Kotlin extensions...

Slide 109

Slide 109 text

3.1.3 3.1.0 September October November December 3.1.1 3.1.2 index, blockOptional...

Slide 110

Slide 110 text

3.1.3 3.1.0 September October November December 3.1.1 3.1.2 Mostly bugfixes...

Slide 111

Slide 111 text

2018 Themes for

Slide 112

Slide 112 text

Continue On Error

Slide 113

Slide 113 text

errors are terminal

Slide 114

Slide 114 text

continue on (transient) errors?

Slide 115

Slide 115 text

Now Flux.range(0, 3) .flatMap(v -> Mono.just(v) .map(i -> 100 / i) .doOnError(...) .onErrorResume( Mono.empty() ) ); processing that can fail your face when you read this code

Slide 116

Slide 116 text

Tomorrow Flux.range(0, 3) .map(i -> 100 / i) .errorStrategyContinue() .filter(i -> 100 / i > 4); this is not impacted and can still fail this is protected by this

Slide 117

Slide 117 text

Observability & Traceability

Slide 118

Slide 118 text

Micrometer “SLF4J for metrics”

Slide 119

Slide 119 text

Sleuth & Zipkin Currently focusing on tracing WebFlux and WebClient any guess as to why these are different? flatMap concatMap

Slide 120

Slide 120 text

reactor-netty

Slide 121

Slide 121 text

API iteration release : 0.8.x Immutable server and client builders Lifecycle and interceptor API (doOnRequest...) Initial user guide

Slide 122

Slide 122 text

API iteration release : 0.8.x HttpClient.prepare() .port(c.address().getPort()) .wiretap() .get() .uri("/test/test.css") .responseContent() .asString() .reduce(String::concat) .subscribe(System.out::println);

Slide 123

Slide 123 text

Beyond 0.8 HTTP 2 support Plug-And-Play Metrics Connection Pool improvements Backpressure strategies

Slide 124

Slide 124 text

File I/O Addon?

Slide 125

Slide 125 text

Java 9 continuously improve support

Slide 126

Slide 126 text

Java 9 continuously improve support RSocket

Slide 127

Slide 127 text

Exploring RSocket (rsocket.io) Duplex flow-control support Multi-transport and multi-language Decoupled from any serialization Contributors from Facebook

Slide 128

Slide 128 text

Beyond and

Slide 129

Slide 129 text

Reactor Kore?

Slide 130

Slide 130 text

Reactor Console?

Slide 131

Slide 131 text

ADBA ?

Slide 132

Slide 132 text

Just Wondering Flux Buffers Lifecycle Hooks ? Persistent Flux ? Coordinated demand ?

Slide 133

Slide 133 text

No content

Slide 134

Slide 134 text

What do you need ?

Slide 135

Slide 135 text

The end Thank You!