Movatterモバイル変換


[0]ホーム

URL:


Toshiaki Maki, profile picture
Uploaded byToshiaki Maki
18,867 views

Introduction to Spring WebFlux #jsug #sf_a1

The document provides an introduction and overview of Spring WebFlux, a non-blocking web framework for Spring. It discusses the differences between blocking and non-blocking web stacks, and how Spring WebFlux uses reactive streams and programming. Code examples are provided showing how to build reactive controllers and streams in Spring WebFlux that support backpressure.

Embed presentation

1Introduction to Spring WebFlux2017-11-24Toshiaki Maki (@making) #jsug #sf_a1
Who am I ?2Toshiaki Maki (@making) https://blog.ik.amSr. Solutions Architect @Pivotal JapanSpring Framework 💖Cloud Foundry 💖
DEMO
4📱📱📱📱📱📱📱📱📱📱📱📱📱📱📱📱📱📱4📱📱📱📱📱📱📱📱📱4🗄📈Devices DashboardHTTP POSTServer-Sent Events∞🔄http://bit.ly/demoiot
❓How many threads are this app using?5
❓How many threads are this app using?51. 200 -2. 100 - 2003. 50 - 1004. 10 - 505. 1 - 10
6
Web Stacks in Spring 57Servlet ContainerServlet APISpring MVCServlet Stack
Web Stacks in Spring 57Servlet ContainerServlet APISpring MVCReactive StackNetty, Servlet 3.1+,UndertowReactive StreamsSpring WebFluxServlet Stack
Web Stacks in Spring 57Servlet ContainerServlet APISpring MVCReactive StackNetty, Servlet 3.1+,UndertowReactive StreamsSpring WebFluxServlet StackBlocking
Web Stacks in Spring 57Servlet ContainerServlet APISpring MVCReactive StackNetty, Servlet 3.1+,UndertowReactive StreamsSpring WebFluxServlet StackBlocking
Web Stacks in Spring 57Servlet ContainerServlet APISpring MVCReactive StackNetty, Servlet 3.1+,UndertowReactive StreamsSpring WebFluxServlet StackBlockingNon-Blocking
Web Stacks in Spring 57Servlet ContainerServlet APISpring MVCReactive StackNetty, Servlet 3.1+,UndertowReactive StreamsSpring WebFluxServlet StackBlockingNon-Blocking
8Servlet StackThreadThreadThreadThreadThread…⏳
9Reactive Stack🔄 Thread🔄 Thread🔄 Thread🔄 Thread
❓Why are we introducing Spring WebFlux?10
❓Why are we introducing Spring WebFlux?10The goal of Spring WebFlux is to offer Springdevelopers a non-blocking event-loop styleprogramming model similar to node.js.
the non-blocking async programming model is moreefficient for latency-sensitive workloads.– Blocking threads consume resources– mobile applications and interconnectedmicroservices❓Why are we introducing Spring WebFlux?10The goal of Spring WebFlux is to offer Springdevelopers a non-blocking event-loop styleprogramming model similar to node.js.
Web Stacks in Spring 511Servlet ContainerServlet APISpring MVCReactive StackNetty, Servlet 3.1+,UndertowReactive StreamsSpring WebFluxServlet Stack
Publisher SubscriberReactive Streams2
Publisher SubscribersubscribeReactive Streams2
Publisher Subscriberrequest(n)BackpressureReactive Streams2
Publisher SubscriberBackpressureonNext(data)onNext(data)onNext(data)Reactive Streams2
Publisher SubscriberBackpressureError|CompleteReactive Streams2
13
13
4Flux<T>Mono<T>
5Flux<T> is a Publisher<T> for 0..n elements
6Flux<T>Mono<T>
7Mono<T> is a Publisher<T> for 0..1 element
Reactor18Flux<String> s = Flux.just("a", "b", "c", "d").map(String::toUpperCase);s.log("demo").subscribe();
Reactor18Flux<String> s = Flux.just("a", "b", "c", "d").map(String::toUpperCase);s.log("demo").subscribe();Need to be subscribed!
Reactor18Flux<String> s = Flux.just("a", "b", "c", "d").map(String::toUpperCase);s.log("demo").subscribe();Need to be subscribed!00:58:33.902 [main] INFO demo - | request(unbounded)00:58:33.903 [main] INFO demo - | onNext(A)00:58:33.903 [main] INFO demo - | onNext(B)00:58:33.903 [main] INFO demo - | onNext(C)00:58:33.903 [main] INFO demo - | onNext(D)00:58:33.903 [main] INFO demo - | onComplete()
Reactor19Flux<String> s = Flux.just("a", "b", "c", "d").map(String::toUpperCase).delayElements(Duration.ofSeconds(1));s.log("demo").subscribe();
Reactor19Flux<String> s = Flux.just("a", "b", "c", "d").map(String::toUpperCase).delayElements(Duration.ofSeconds(1));s.log("demo").subscribe();00:59:42.949 [main] INFO demo - request(unbounded)00:59:43.992 [parallel-1] INFO demo - onNext(A)00:59:44.994 [parallel-2] INFO demo - onNext(B)00:59:45.997 [parallel-3] INFO demo - onNext(C)00:59:46.999 [parallel-4] INFO demo - onNext(D)00:59:47.001 [parallel-4] INFO demo - onComplete()
Reactor (Hot Stream♨)20Flux<String> s = Flux.<String>create(sink ->sink.next(...);});s.log("demo").subscribe();
Reactor (Hot Stream♨)21twitter4j.TwiterStream tw = ...;Flux<String> s = Flux.<String>create(sink ->sink.next(status.getText());});s.log("tweet").subscribe();
Reactor (Hot Stream♨)22twitter4j.TwiterStream tw = ...;Flux<String> s = Flux.<String>create(sink ->tw.addListener(new StatusAdapter() {public void onStatus(Status status) {sink.next(status.getText()); }public void onException(Exception e) {sink.error(e); }});sink.onCancel(tw::shutdown);tw.sample();});s.log("tweet").subscribe();
Operators in Reactor23• map / indexed / flatMap / flatMapMany• collectList / collectMap / count• concat / merge / zip / when / combineLatest• repeat / interval• filter / sample / take / skip• window / windowWhile / buffer• ...https://projectreactor.io/docs/core/release/reference/docs/index.html#which-operator
zip24
zip24
zip25Flux<GHUser> github = findGitHubUsers("foo", "bar","hoge");
Flux<FBUser> facebook = findFacebookUsers("foo","bar", "hoge");Flux<User> users = Flux.zip(github, facebook).map(tpl -> new User(tpl.getT1(), tpl.getT2()));users.subscribe();
flatMap26
flatMap27Flux<GHUser> github = findGitHubUsers("foo", "bar","hoge");
Flux<User> users = github.flatMap(g-> findTweets(g).collectList().map(tweets -> new User(g, tweets)));users.subscribe();
flatMap28Flux<GHUser> github = findGitHubUsers("foo", "bar","hoge");
Flux<User> users = github.map(g-> findTweets(g).collectList().map(tweets -> new User(g, tweets)));users.subscribe();
flatMap28Flux<GHUser> github = findGitHubUsers("foo", "bar","hoge");
Flux<User> users = github.map(g-> findTweets(g).collectList().map(tweets -> new User(g, tweets)));users.subscribe();⚠ Can be compiled butfindTweets won't besubscribed
flatMap29Flux<GHUser> github = findGitHubUsers("foo", "bar","hoge");Flux<User> users = github.map(g -> {Mono<User> u = findTweets(g).collectList().map(tweets -> new User(g, tweets));u.subscribe();return u;});users.subscribe();This will work,but use flatMap instead
Web Stacks in Spring 530Servlet ContainerServlet APISpring MVCReactive StackNetty, Servlet 3.1+,UndertowReactive StreamsSpring WebFluxServlet Stack
Spring MVC controller31@RestControllerpublic class HelloController {private final Hello hello;public HelloController(Hello hello) {this.hello = hello;}@GetMapping("hello")public String hello() {return hello.sayHello();}}
Spring MVC controller31@RestControllerpublic class HelloController {private final Hello hello;public HelloController(Hello hello) {this.hello = hello;}@GetMapping("hello")public String hello() {return hello.sayHello();}}Blocking / Synchronous
Spring WebFlux controller32@RestControllerpublic class HelloController {private final Hello hello;public HelloController(Hello hello) {this.hello = hello;}@GetMapping("hello")public String hello() {return hello.sayHello();}}
Spring WebFlux controller32@RestControllerpublic class HelloController {private final Hello hello;public HelloController(Hello hello) {this.hello = hello;}@GetMapping("hello")public String hello() {return hello.sayHello();}}Non-blocking / Synchronous
Spring WebFlux controller33@RestControllerpublic class HelloController {private final Hello hello;public HelloController(Hello hello) {this.hello = hello;}@GetMapping("hello")public Mono<String> hello() {return Mono.fromCallable(hello::sayHello);}}
Spring WebFlux controller33@RestControllerpublic class HelloController {private final Hello hello;public HelloController(Hello hello) {this.hello = hello;}@GetMapping("hello")public Mono<String> hello() {return Mono.fromCallable(hello::sayHello);}}Non-blocking / Asynchronous
Spring WebFlux controller33@RestControllerpublic class HelloController {private final Hello hello;public HelloController(Hello hello) {this.hello = hello;}@GetMapping("hello")public Mono<String> hello() {return Mono.fromCallable(hello::sayHello);}}Non-blocking / AsynchronousYou don't need to subscribe the stream
Spring WebFlux controller34@RestControllerpublic class HelloController {private final Hello hello;public HelloController(Hello hello) {this.hello = hello;}@GetMapping("hello")public Mono<String> hello() {return Mono.fromCallable(hello::sayHello).flatMap(foo::abc).map(bar::xyz);}Non-blocking / Asynchronous
Spring WebFlux controller34@RestControllerpublic class HelloController {private final Hello hello;public HelloController(Hello hello) {this.hello = hello;}@GetMapping("hello")public Mono<String> hello() {return Mono.fromCallable(hello::sayHello).flatMap(foo::abc).map(bar::xyz);}Non-blocking / AsynchronousCould be executedin the other thread
Spring MVC controller35@RestControllerpublic class HelloController {private final Hello hello;public HelloController(Hello hello) {this.hello = hello;}@GetMapping("hello")public Mono<String> hello() {return Mono.fromCallable(hello::sayHello).flatMap(foo::abc).map(bar::xyz);}Blocking / Asynchronous
Spring MVC controller35@RestControllerpublic class HelloController {private final Hello hello;public HelloController(Hello hello) {this.hello = hello;}@GetMapping("hello")public Mono<String> hello() {return Mono.fromCallable(hello::sayHello).flatMap(foo::abc).map(bar::xyz);}Blocking / AsynchronousAsync support inServlet 3
∞ Stream36@RestControllerpublic class HelloController {@GetMapping("hello")public Flux<String> hello() {Flux<String> hello = Flux.just("hello").delayElement(Duration.ofMillis(100));hello.subscribe();return hello.repeat(); // ∞ Stream}}
DEMO
Content Negotiation38Accept: text/event-streamAccept: application/stream+jsonAccept: application/json
Content Negotiation38Accept: text/event-streamAccept: application/stream+jsonAccept: application/json✅ Backpressure✅ Backpressure❌ Backpressure
Content Negotiation39@GetMapping("hello")public Flux<Message> hello() {return Mono.just(new Message("a")).repeat().take(100).log("message");}
40$ curl -v -H "Accept: text/event-stream" localhost:8080/messages> GET /messages HTTP/1.1> Host: localhost:8080> Accept: text/event-stream>< HTTP/1.1 200 OK< transfer-encoding: chunked< Content-Type: text/event-stream<data:{"text":"a"}data:{"text":"a"}data:{"text":"a"}...
40$ curl -v -H "Accept: text/event-stream" localhost:8080/messages> GET /messages HTTP/1.1> Host: localhost:8080> Accept: text/event-stream>< HTTP/1.1 200 OK< transfer-encoding: chunked< Content-Type: text/event-stream<data:{"text":"a"}data:{"text":"a"}data:{"text":"a"}...INFO 48330 --- [ctor-http-nio-2] message : request(1)INFO 48330 --- [ctor-http-nio-2] message : onNext(a)INFO 48330 --- [ctor-http-nio-2] message : request(31)INFO 48330 --- [ctor-http-nio-2] message : onNext(a)INFO 48330 --- [ctor-http-nio-2] message : onNext(a)... ...INFO 48330 --- [ctor-http-nio-2] message : onNext(a)INFO 48330 --- [ctor-http-nio-2] message : request(24)INFO 48330 --- [ctor-http-nio-2] message : onNext(a)INFO 48330 --- [ctor-http-nio-2] message : onNext(a)... ...INFO 48330 --- [ctor-http-nio-2] message : request(24)INFO 48330 --- [ctor-http-nio-2] message : onNext(a)... ...INFO 48330 --- [ctor-http-nio-2] message : onComplete()
40$ curl -v -H "Accept: text/event-stream" localhost:8080/messages> GET /messages HTTP/1.1> Host: localhost:8080> Accept: text/event-stream>< HTTP/1.1 200 OK< transfer-encoding: chunked< Content-Type: text/event-stream<data:{"text":"a"}data:{"text":"a"}data:{"text":"a"}...INFO 48330 --- [ctor-http-nio-2] message : request(1)INFO 48330 --- [ctor-http-nio-2] message : onNext(a)INFO 48330 --- [ctor-http-nio-2] message : request(31)INFO 48330 --- [ctor-http-nio-2] message : onNext(a)INFO 48330 --- [ctor-http-nio-2] message : onNext(a)... ...INFO 48330 --- [ctor-http-nio-2] message : onNext(a)INFO 48330 --- [ctor-http-nio-2] message : request(24)INFO 48330 --- [ctor-http-nio-2] message : onNext(a)INFO 48330 --- [ctor-http-nio-2] message : onNext(a)... ...INFO 48330 --- [ctor-http-nio-2] message : request(24)INFO 48330 --- [ctor-http-nio-2] message : onNext(a)... ...INFO 48330 --- [ctor-http-nio-2] message : onComplete()Backpressure
(FYI) In case of Servlet Stack41
(FYI) In case of Servlet Stack41INFO 61528 --- [nio-8080-exec-1] message : request(1)INFO 61528 --- [nio-8080-exec-1] message : onNext(a)INFO 61528 --- [ MvcAsync1] message : request(1)INFO 61528 --- [ MvcAsync1] message : onNext(a)INFO 61528 --- [ MvcAsync2] message : request(1)... ...INFO 61528 --- [ MvcAsync98] message : request(1)INFO 61528 --- [ MvcAsync98] message : onNext(a)INFO 61528 --- [ MvcAsync99] message : request(1)INFO 61528 --- [ MvcAsync99] message : onNext(a)INFO 61528 --- [ MvcAsync99] message : onComplete()
(FYI) In case of Servlet Stack41INFO 61528 --- [nio-8080-exec-1] message : request(1)INFO 61528 --- [nio-8080-exec-1] message : onNext(a)INFO 61528 --- [ MvcAsync1] message : request(1)INFO 61528 --- [ MvcAsync1] message : onNext(a)INFO 61528 --- [ MvcAsync2] message : request(1)... ...INFO 61528 --- [ MvcAsync98] message : request(1)INFO 61528 --- [ MvcAsync98] message : onNext(a)INFO 61528 --- [ MvcAsync99] message : request(1)INFO 61528 --- [ MvcAsync99] message : onNext(a)INFO 61528 --- [ MvcAsync99] message : onComplete()✅ Backpressure
42$ curl -v -H "Accept: application/stream+json" localhost:8080/messages> GET /messages HTTP/1.1> Host: localhost:8080> Accept: application/stream+json>< HTTP/1.1 200 OK< transfer-encoding: chunked< Content-Type: application/stream+json;charset=UTF-8<{"text":"a"}{"text":"a"}{"text":"a"}...
42$ curl -v -H "Accept: application/stream+json" localhost:8080/messages> GET /messages HTTP/1.1> Host: localhost:8080> Accept: application/stream+json>< HTTP/1.1 200 OK< transfer-encoding: chunked< Content-Type: application/stream+json;charset=UTF-8<{"text":"a"}{"text":"a"}{"text":"a"}...INFO 48330 --- [ctor-http-nio-4] message : request(1)INFO 48330 --- [ctor-http-nio-4] message : onNext(a)INFO 48330 --- [ctor-http-nio-4] message : request(31)INFO 48330 --- [ctor-http-nio-4] message : onNext(a)INFO 48330 --- [ctor-http-nio-4] message : onNext(a)... ...INFO 48330 --- [ctor-http-nio-4] message : onNext(a)INFO 48330 --- [ctor-http-nio-4] message : request(24)INFO 48330 --- [ctor-http-nio-4] message : onNext(a)INFO 48330 --- [ctor-http-nio-4] message : onNext(a)... ...INFO 48330 --- [ctor-http-nio-4] message : request(24)INFO 48330 --- [ctor-http-nio-4] message : onNext(a)... ...INFO 48330 --- [ctor-http-nio-4] message : onComplete()
42$ curl -v -H "Accept: application/stream+json" localhost:8080/messages> GET /messages HTTP/1.1> Host: localhost:8080> Accept: application/stream+json>< HTTP/1.1 200 OK< transfer-encoding: chunked< Content-Type: application/stream+json;charset=UTF-8<{"text":"a"}{"text":"a"}{"text":"a"}...INFO 48330 --- [ctor-http-nio-4] message : request(1)INFO 48330 --- [ctor-http-nio-4] message : onNext(a)INFO 48330 --- [ctor-http-nio-4] message : request(31)INFO 48330 --- [ctor-http-nio-4] message : onNext(a)INFO 48330 --- [ctor-http-nio-4] message : onNext(a)... ...INFO 48330 --- [ctor-http-nio-4] message : onNext(a)INFO 48330 --- [ctor-http-nio-4] message : request(24)INFO 48330 --- [ctor-http-nio-4] message : onNext(a)INFO 48330 --- [ctor-http-nio-4] message : onNext(a)... ...INFO 48330 --- [ctor-http-nio-4] message : request(24)INFO 48330 --- [ctor-http-nio-4] message : onNext(a)... ...INFO 48330 --- [ctor-http-nio-4] message : onComplete()✅ Backpressure
43$ curl -v -H "Accept: application/json" localhost:8080/messages> GET /messages HTTP/1.1> Host: localhost:8080> Accept: application/json>< HTTP/1.1 200 OK< transfer-encoding: chunked< Content-Type: application/json;charset=UTF-8<[{"text":"a"},{"text":"a"},...,{"text":"a"},{"text":"a"},{"text":"a"},{"text":"a"}]
43$ curl -v -H "Accept: application/json" localhost:8080/messages> GET /messages HTTP/1.1> Host: localhost:8080> Accept: application/json>< HTTP/1.1 200 OK< transfer-encoding: chunked< Content-Type: application/json;charset=UTF-8<[{"text":"a"},{"text":"a"},...,{"text":"a"},{"text":"a"},{"text":"a"},{"text":"a"}]INFO 48330 --- [ctor-http-nio-3] message : request(unbounded)INFO 48330 --- [ctor-http-nio-3] message : onNext(a)INFO 48330 --- [ctor-http-nio-3] message : onNext(a)INFO 48330 --- [ctor-http-nio-3] message : onNext(a)INFO 48330 --- [ctor-http-nio-3] message : onNext(a)... ...INFO 48330 --- [ctor-http-nio-3] message : onNext(a)INFO 48330 --- [ctor-http-nio-3] message : onNext(a)INFO 48330 --- [ctor-http-nio-3] message : onNext(a)INFO 48330 --- [ctor-http-nio-3] message : onNext(a)INFO 48330 --- [ctor-http-nio-3] message : onComplete()
43$ curl -v -H "Accept: application/json" localhost:8080/messages> GET /messages HTTP/1.1> Host: localhost:8080> Accept: application/json>< HTTP/1.1 200 OK< transfer-encoding: chunked< Content-Type: application/json;charset=UTF-8<[{"text":"a"},{"text":"a"},...,{"text":"a"},{"text":"a"},{"text":"a"},{"text":"a"}]INFO 48330 --- [ctor-http-nio-3] message : request(unbounded)INFO 48330 --- [ctor-http-nio-3] message : onNext(a)INFO 48330 --- [ctor-http-nio-3] message : onNext(a)INFO 48330 --- [ctor-http-nio-3] message : onNext(a)INFO 48330 --- [ctor-http-nio-3] message : onNext(a)... ...INFO 48330 --- [ctor-http-nio-3] message : onNext(a)INFO 48330 --- [ctor-http-nio-3] message : onNext(a)INFO 48330 --- [ctor-http-nio-3] message : onNext(a)INFO 48330 --- [ctor-http-nio-3] message : onNext(a)INFO 48330 --- [ctor-http-nio-3] message : onComplete()❌ Backpressure
Flux -> Mono44@GetMapping("hello")public Mono<List<Message>> hello() {return Mono.just(new Message("a")).repeat().take(100).collectList().log("message");}
Flux -> Mono45@GetMapping("hello")public Mono<List<Message>> hello() {return Mono.just(new Message("a")).repeat().take(100).collectList().log("message");}$ curl -v -H "Accept: application/stream+json" localhost:8080/messages> GET /messages HTTP/1.1> Host: localhost:8080> Accept: application/stream+json>< HTTP/1.1 200 OK< transfer-encoding: chunked< Content-Type: application/stream+json;charset=UTF-8<[{"text":"a"},{"text":"a"},...,{"text":"a"},{"text":"a"},{"text":"a"},{"text":"a"}]
Flux -> Mono45@GetMapping("hello")public Mono<List<Message>> hello() {return Mono.just(new Message("a")).repeat().take(100).collectList().log("message");}$ curl -v -H "Accept: application/stream+json" localhost:8080/messages> GET /messages HTTP/1.1> Host: localhost:8080> Accept: application/stream+json>< HTTP/1.1 200 OK< transfer-encoding: chunked< Content-Type: application/stream+json;charset=UTF-8<[{"text":"a"},{"text":"a"},...,{"text":"a"},{"text":"a"},{"text":"a"},{"text":"a"}]INFO 48330 --- [ctor-http-nio-3] message : | request(1)INFO 48330 --- [ctor-http-nio-3] message : | onNext([a, a, a, a,a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a,a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a,a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a])INFO 48330 --- [ctor-http-nio-3] message : | request(31)INFO 48330 --- [ctor-http-nio-3] message : | onComplete()
Flux -> Mono45@GetMapping("hello")public Mono<List<Message>> hello() {return Mono.just(new Message("a")).repeat().take(100).collectList().log("message");}$ curl -v -H "Accept: application/stream+json" localhost:8080/messages> GET /messages HTTP/1.1> Host: localhost:8080> Accept: application/stream+json>< HTTP/1.1 200 OK< transfer-encoding: chunked< Content-Type: application/stream+json;charset=UTF-8<[{"text":"a"},{"text":"a"},...,{"text":"a"},{"text":"a"},{"text":"a"},{"text":"a"}]INFO 48330 --- [ctor-http-nio-3] message : | request(1)INFO 48330 --- [ctor-http-nio-3] message : | onNext([a, a, a, a,a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a,a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a,a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a])INFO 48330 --- [ctor-http-nio-3] message : | request(31)INFO 48330 --- [ctor-http-nio-3] message : | onComplete()❌ Backpressure
Spring WebFlux controller (Receiving Flux)46@RestControllerpublic class HelloController {@PostMapping("upper")public Flux<String>upper(@RequestBody Flux<String> input) {Flux<String> output = input.map(String::toUpperCase);return output;}}
47$ telnet localhost 8080POST /upper HTTP/1.1Host: localhost:8080Transfer-Encoding: chunkedAccept: text/event-streamContent-Type: text/plain3fooHTTP/1.1 200 OKtransfer-encoding: chunkedContent-Type: text/event-stream5data:4FOO14java5data:5JAVA1
47$ telnet localhost 8080POST /upper HTTP/1.1Host: localhost:8080Transfer-Encoding: chunkedAccept: text/event-streamContent-Type: text/plain3fooHTTP/1.1 200 OKtransfer-encoding: chunkedContent-Type: text/event-stream5data:4FOO14java5data:5JAVA1Request Body
47$ telnet localhost 8080POST /upper HTTP/1.1Host: localhost:8080Transfer-Encoding: chunkedAccept: text/event-streamContent-Type: text/plain3fooHTTP/1.1 200 OKtransfer-encoding: chunkedContent-Type: text/event-stream5data:4FOO14java5data:5JAVA1Request BodyResponse Body
Spring WebFlux controller (Receiving Flux)48@PostMapping("upper")public Flux<String>upper(@RequestBody Flux<String> input) {Flux<String> output = input.map(String::toUpperCase).flatMap(s -> Flux.interval(Duration.ofSeconds(1)).map(i -> s + i).take(3));return output;}
493fooHTTP/1.1 200 OKtransfer-encoding: chunkedContent-Type: text/event-stream5data:5FOO015data:5FOO115data:5FOO21
DEMO
Spring MVC controller (Receiving Flux)51@RestControllerpublic class HelloController {@PostMapping("upper")public Flux<String>hello(@RequestBody Flux<String> input) {Flux<String> output = input.map(String::toUpperCase);return output;}}
Spring MVC controller (Receiving Flux)51@RestControllerpublic class HelloController {@PostMapping("upper")public Flux<String>hello(@RequestBody Flux<String> input) {Flux<String> output = input.map(String::toUpperCase);return output;}}🙅
Spring MVC controller (Receiving Flux)51@RestControllerpublic class HelloController {@PostMapping("upper")public Flux<String>hello(@RequestBody Flux<String> input) {Flux<String> output = input.map(String::toUpperCase);return output;}}🙅Reactive type are supported only ascontroller method return values
Broadcast stream52@RestControllerpublic class HelloController {private final Flux<String> flux;public HelloController() {this.flux = this.createHotStream().share();this.flux.subscribe();}@GetMapping("hello")public Flux<String> hello() {return this.flux;}}
Broadcast stream52@RestControllerpublic class HelloController {private final Flux<String> flux;public HelloController() {this.flux = this.createHotStream().share();this.flux.subscribe();}@GetMapping("hello")public Flux<String> hello() {return this.flux;}}this stream is shared by all httpclients!
53📱📱📱📱📱📱📱📱📱📱📱📱📱📱📱📱📱📱53📱📱📱📱📱📱📱📱📱53🗄📈Devices DashboardHTTP POSTServer-Sent Events∞🔄♨
54📱📱📱📱📱📱📱📱📱📱📱📱📱📱📱📱📱📱54📱📱📱📱📱📱📱📱📱54🗄📈Devices DashboardHTTP POSTServer-Sent Events∞🔄📈📈📈♨
54📱📱📱📱📱📱📱📱📱📱📱📱📱📱📱📱📱📱54📱📱📱📱📱📱📱📱📱54🗄📈Devices DashboardHTTP POSTServer-Sent Events∞🔄📈📈📈♨ Shared
Web Stacks in Spring 555Servlet ContainerServlet APISpring MVCReactive StackNetty, Servlet 3.1+,UndertowReactive StreamsSpring WebFluxServlet Stack
Web Stacks in Spring 555Servlet ContainerServlet APISpring MVCReactive StackNetty, Servlet 3.1+,UndertowReactive StreamsSpring WebFluxServlet Stack•Annotated Controller•Function Endpoints (WebFlux.fn)
@Configurationpublic class RouterConfig {@Beanpublic RouterFunctions<ServerResponse> routes() {return route(GET("hello"), req -> {return ServerReponse.ok().body(Mono.just("hello"), String.class);});}}Spring WebFlux.fn56
@Configurationpublic class RouterConfig {@Beanpublic RouterFunctions<ServerResponse> routes() {return route(GET("hello"), req -> {return ServerReponse.ok().body(Mono.just("hello"), String.class);});}}lambdaSpring WebFlux.fn56
Spring WebFlux.fn57@Configurationpublic class RouterConfig {@Beanpublic RouterFunctions<ServerResponse>routes(HelloHandler helloHandler) {return route(GET("hello"), helloHandler::hello);}}
Spring WebFlux.fn58public RouterFunctions<ServerResponse>routes(PersonHandeler ph) {return route(GET("person"), ph::findAll).andRoute(POST("person"), ph::create).andRoute(GET("person/{id}"), ph::findOne).andRoute(PUT("person/{id}"), ph::update).andRoute(DELETE("person/{id}"), ph::delete));}
Spring WebFlux.fn59public RouterFunctions<ServerResponse>routes(PersonHandeler ph) {return nest(path("person"),route(GET("/"), ph::findAll).andRoute(POST("/"), ph::create).andRoute(GET("/{id}"), ph::findOne).andRoute(PUT("/{id}"), ph::update).andRoute(DELETE("/{id}"), ph::delete)));}
Web Stacks in Spring 560Servlet ContainerServlet APISpring MVCReactive StackNetty, Servlet 3.1+,UndertowReactive StreamsSpring WebFluxServlet Stack
Web Stacks in Spring 560Servlet ContainerServlet APISpring MVCReactive StackNetty, Servlet 3.1+,UndertowReactive StreamsSpring WebFluxServlet StackReactive Web Client
Reactive Web Client61@RestControllerpublic class HelloController {private final WebClient client;public HelloController(WebClinet.Builer b) {this.client = b.build();}@GetMapping("hello")public Flux<String> hello() {return this.client.get().uri("http://blahblah").retrieve().bodyToFlux(String.class);}}
Reactive Web Client62Flux<User> users = webClient.get().uri("http://user-service").exchange() // => Mono<ClientResponse>.flatMap(r -> r.bodyToFlux(User.class));// short-cutFlux<User> users = webClient.get().uri("http://user-service").retrieve().bodyToFlux(User.class);
Reactive Web Client63Mono<User> user = webClient.get().uri("http://user-service/{id}", id).header("X-Foo", "foo").retrieve().bodyToMono(User.class);Mono<User> user = ...Mono<Void> created = webClient.post().uri("http://user-service").body(user, User.class).retrieve().bodyToMono(Void.class);
From RestTemplate to WebClient64@Controllerpublic class UserController {@GetMapping("users")public String hello(Model model) {List<User> users = restTemplate.getForObject("/users", List.class);model.addAttribute("users", users);return "users";}}
From RestTemplate to WebClient65@Controllerpublic class UserController {@GetMapping("users")public String hello(Model model) {Flux<User> users = client.get().uri("/users").retrieve().bodyToFlux(String.class);model.addAttribute("users", users);return "users";}}
66<html><body><ul><li data-th-each="user : ${user}">[[${user}]]</li></ul></body></html>
66<html><body><ul><li data-th-each="user : ${user}">[[${user}]]</li></ul></body></html>😀 Spring WebFlux
66<html><body><ul><li data-th-each="user : ${user}">[[${user}]]</li></ul></body></html>😀 Spring WebFlux😨 Spring MVC
From RestTemplate to WebClient (Spring MVC)67@Controllerpublic class UserController {@GetMapping("users")public Mono<String> hello(Model model) {Flux<User> users = client.get().uri("/users").retrieve().bodyToFlux(String.class);users.collectList().map(x -> {model.addAttribute("users", x);return "users";});}
From RestTemplate to WebClient (Spring MVC)67@Controllerpublic class UserController {@GetMapping("users")public Mono<String> hello(Model model) {Flux<User> users = client.get().uri("/users").retrieve().bodyToFlux(String.class);users.collectList().map(x -> {model.addAttribute("users", x);return "users";});}😀 Spring MVC
From RestTemplate to WebClient (Spring MVC)68@Controllerpublic class UserController {@GetMapping("users")public Mono<String> hello(Model model) {Flux<User> users = ...;Mono<Sting> foo = ...;return Mono.zip(user.collectList(), foo)   .map(tpl -> {model.addAttribute("users", tpl.getT1());model.addAttribute("foo", tpl.getT2());return "users";});}}
69String url = "http://httpbin.org/delay/1";@GetMapping("hello")public Mono<String> hello() {Mono<String> mono = Mono.just(restTemplate.getForObject(url));return mono;}
⚠ Don't block the thread in WebFlux!69String url = "http://httpbin.org/delay/1";@GetMapping("hello")public Mono<String> hello() {Mono<String> mono = Mono.just(restTemplate.getForObject(url));return mono;}
⚠ Don't block the thread in WebFlux!69String url = "http://httpbin.org/delay/1";@GetMapping("hello")public Mono<String> hello() {Mono<String> mono = Mono.just(restTemplate.getForObject(url));return mono;} 😠 Blocking!
⚠ Don't block the thread in WebFlux!70String url = "http://httpbin.org/delay/1";@GetMapping("hello")public Mono<String> hello() {Mono<String> mono = Mono.fromCallable(() ->restTemplate.getForObject(url)).subscribeOn(Schedulers.elastic());return mono;}
⚠ Don't block the thread in WebFlux!70String url = "http://httpbin.org/delay/1";@GetMapping("hello")public Mono<String> hello() {Mono<String> mono = Mono.fromCallable(() ->restTemplate.getForObject(url)).subscribeOn(Schedulers.elastic());return mono;} Switch the execution context
71ConcurrencyThroughput [trans / sec]Core i7 2.7 GHz4 Core x HT
Reactive Support in Spring Projects72Spring DataReactive StackNetty, Servlet 3.1+,UndertowReactive StreamsSpring WebFluxSpring SecurityThymeleaf
Spring Data Kay73Reactive support for• Redis• MongoDB• Couchbase• CassandraInfinite streams from the database with @Tailable
Reactive Non-Blocking Data Access74public interface ReactiveCrudRepository<ID,T> {Mono<T> findById(ID id);Mono<T> findById(Mono<ID> id);Flux<T> findAll();Mono<Long> count();Mono<T> save(T entity);Mono<T> saveAll(Publisher<T> entityStream);Mono<Void> delete(T entity)// ...}
Tailable Cursor Support for MongoDB75public interface MessageRepositoryextends ReactiveMongoRepository<Message,String>{@TailableFlux<Message>findByUpdatedAtGreaterThan(Instant target);}
Broadcast updated messages in MongoDB76@RestControllerpublic class HelloController {private final Flux<Message> messages;public HelloController(MessageRepository repo) {this.messages = repo.findByUpdatedAtGreaterThan(Instant.now()).share();this.messages.subscribe();}@GetMapping("messages")public Flux<Message> messages() {return this.messages;}}
But, but, JDBC is blocking .... 😭77
But, but, JDBC is blocking .... 😭77Let's see what will happen in next Java
But, but, JDBC is blocking .... 😭77Let's see what will happen in next Javahttps://static.rainfocus.com/oracle/oow17/sess/1491948952321001dm4m/PF/JavaOne%2017%20-%20CON1491_1506954898905001IipH.pdf
java.sql278http://cr.openjdk.java.net/~lancea/apidoc/java/sql2/package-summary.html
java.sql278http://cr.openjdk.java.net/~lancea/apidoc/java/sql2/package-summary.html😢 Does not seem to supportReactive Streams (j.u.c.Flow)
Spring Security Reactive79@Beanpublic SecurityWebFilterChainspringWebFilterChain(HttpSecurity http) {return http.authorizeExchange().pathMatchers("/v2/**").hasRole("ADMIN").and().httpBasic().and().build();}@Beanpublic MapUserDetailsRepository userDetailsRepository() {return new MapUserDetailsRepository(...);}
Thymeleaf 3.0 reactive support80• "full mode"• "chunked mode"
=> Progressive rendering. Good for large pages• "data-driven mode" 
=> Rendering template fragments as Server-Sent Events
• https://github.com/thymeleaf/thymeleaf-spring/issues/132• https://github.com/spring-projects/spring-boot/issues/8124• https://speakerdeck.com/dfernandez/o-2017-getting-thymeleaf-ready-for-spring-5-and-reactive?slide=18transfer-encoding: chunked
Data-driven mode81@GetMapping("users")public String hello(Model model) {Flux<User> users = userService.findAll();ReactiveDataDriverContextVariable v= new ReactiveDataDriverContextVariable(users, 1);model.addAttribute("users", v);return "users";}
DEMO
Reactive Application Patterns (as of 2017)83
Reactive Application Patterns (as of 2017)83Spring WebFluxSpring WebFluxFrontend BackendHTTP NoSQLWebClient Spring Data
Reactive Application Patterns (as of 2017)84Spring MVCSpring WebFluxFrontend BackendHTTP RDBJDBCWebClient
Reactive Application Patterns (as of 2017)85Spring MVCSpring MVCFrontend BackendHTTP RDBJDBCWebClient
Spring Cloud Gateway86A Gateway built on Spring Framework 5.0 and SpringBoot 2.0 providing routing and morehttp://cloud.spring.io/spring-cloud-gateway/http://cloud.spring.io/spring-cloud-gateway/2.0.x/single/spring-cloud-gateway.html
Start Spring 5 & Spring Boot 287
Servlet Stack ⇄ Reactive Stack88<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency>Reactive StackServlet Stack
Servlet Stack ⇄ Reactive Stack89<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webflux</artifactId></dependency><dependency><groupId>io.projectreactor.ipc</groupId><artifactId>reactor-netty</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency>Reactive StackServlet Stack+WebClient
Thanks!90Resources• Servlet vs Reactive Stacks in Five Use Cases• Spring Boot 2 0 Web Applications• Spring Framework 5: Themes & Trends• Why Spring ❤ Kotlin

Recommended

PDF
From Spring Boot 2.2 to Spring Boot 2.3 #jsug
PDF
Why PCF is the best platform for Spring Boot
PDF
Spring Boot Actuator 2.0 & Micrometer
PDF
BOSH / CF Deployment in modern ways #cf_tokyo
PDF
Open Service Broker APIとKubernetes Service Catalog #k8sjp
PDF
Spring Cloud Function & Project riff #jsug
PDF
Spring Cloud Servicesの紹介 #pcf_tokyo
PDF
Microservices with Spring and Cloud Foundry
PDF
Serverless with Spring Cloud Function, Knative and riff #SpringOneTour #s1t
PPTX
マイクロサービスに必要な技術要素はすべてSpring Cloudにある #DO07
PDF
Spring Boot 1.3 News #渋谷Java
PDF
Short Lived Tasks in Cloud Foundry #cfdtokyo
PDF
2017年のLINEのマイクロサービスを支えるSpring
PDF
Managing your Docker image continuously with Concourse CI
PDF
Event Driven Microservices with Spring Cloud Stream #jjug_ccc #ccc_ab3
PDF
クラウド時代の Spring Framework (aka Spring Framework in Cloud Era)
PDF
手把手教你如何串接 Log 到各種網路服務
PDF
Spring ❤️ Kotlin #jjug
PDF
Full Stack Reactive with React and Spring WebFlux - SpringOne 2018
PDF
Java Microservices with Spring Boot and Spring Cloud - Denver JUG 2019
PDF
Spring Cloud: Why? How? What?
PDF
Introduction to Cloud Foundry #JJUG
PDF
REST APIs with Spring
PDF
Game of Streams: How to Tame and Get the Most from Your Messaging Platforms
PDF
Spring Boot - Microservice Metrics Monitoring
PDF
Spring Boot & Actuators
PDF
Microservice With Spring Boot and Spring Cloud
PDF
実例で学ぶ、明日から使えるSpring Boot Tips #jsug
PDF
Guide to Spring Reactive Programming using WebFlux
PDF
Reactive programming with spring web flux

More Related Content

PDF
From Spring Boot 2.2 to Spring Boot 2.3 #jsug
PDF
Why PCF is the best platform for Spring Boot
PDF
Spring Boot Actuator 2.0 & Micrometer
PDF
BOSH / CF Deployment in modern ways #cf_tokyo
PDF
Open Service Broker APIとKubernetes Service Catalog #k8sjp
PDF
Spring Cloud Function & Project riff #jsug
PDF
Spring Cloud Servicesの紹介 #pcf_tokyo
PDF
Microservices with Spring and Cloud Foundry
From Spring Boot 2.2 to Spring Boot 2.3 #jsug
Why PCF is the best platform for Spring Boot
Spring Boot Actuator 2.0 & Micrometer
BOSH / CF Deployment in modern ways #cf_tokyo
Open Service Broker APIとKubernetes Service Catalog #k8sjp
Spring Cloud Function & Project riff #jsug
Spring Cloud Servicesの紹介 #pcf_tokyo
Microservices with Spring and Cloud Foundry

What's hot

PDF
Serverless with Spring Cloud Function, Knative and riff #SpringOneTour #s1t
PPTX
マイクロサービスに必要な技術要素はすべてSpring Cloudにある #DO07
PDF
Spring Boot 1.3 News #渋谷Java
PDF
Short Lived Tasks in Cloud Foundry #cfdtokyo
PDF
2017年のLINEのマイクロサービスを支えるSpring
PDF
Managing your Docker image continuously with Concourse CI
PDF
Event Driven Microservices with Spring Cloud Stream #jjug_ccc #ccc_ab3
PDF
クラウド時代の Spring Framework (aka Spring Framework in Cloud Era)
PDF
手把手教你如何串接 Log 到各種網路服務
PDF
Spring ❤️ Kotlin #jjug
PDF
Full Stack Reactive with React and Spring WebFlux - SpringOne 2018
PDF
Java Microservices with Spring Boot and Spring Cloud - Denver JUG 2019
PDF
Spring Cloud: Why? How? What?
PDF
Introduction to Cloud Foundry #JJUG
PDF
REST APIs with Spring
PDF
Game of Streams: How to Tame and Get the Most from Your Messaging Platforms
PDF
Spring Boot - Microservice Metrics Monitoring
PDF
Spring Boot & Actuators
PDF
Microservice With Spring Boot and Spring Cloud
PDF
実例で学ぶ、明日から使えるSpring Boot Tips #jsug
Serverless with Spring Cloud Function, Knative and riff #SpringOneTour #s1t
マイクロサービスに必要な技術要素はすべてSpring Cloudにある #DO07
Spring Boot 1.3 News #渋谷Java
Short Lived Tasks in Cloud Foundry #cfdtokyo
2017年のLINEのマイクロサービスを支えるSpring
Managing your Docker image continuously with Concourse CI
Event Driven Microservices with Spring Cloud Stream #jjug_ccc #ccc_ab3
クラウド時代の Spring Framework (aka Spring Framework in Cloud Era)
手把手教你如何串接 Log 到各種網路服務
Spring ❤️ Kotlin #jjug
Full Stack Reactive with React and Spring WebFlux - SpringOne 2018
Java Microservices with Spring Boot and Spring Cloud - Denver JUG 2019
Spring Cloud: Why? How? What?
Introduction to Cloud Foundry #JJUG
REST APIs with Spring
Game of Streams: How to Tame and Get the Most from Your Messaging Platforms
Spring Boot - Microservice Metrics Monitoring
Spring Boot & Actuators
Microservice With Spring Boot and Spring Cloud
実例で学ぶ、明日から使えるSpring Boot Tips #jsug

Similar to Introduction to Spring WebFlux #jsug #sf_a1

PDF
Guide to Spring Reactive Programming using WebFlux
PDF
Reactive programming with spring web flux
PPTX
Reactive programming by spring webflux - DN Scrum Breakfast - Nov 2018
PDF
Introduction to Spring webflux
PDF
Full-Stack Reactive with Spring WebFlux + Angular - JConf Colombia 2019
PDF
Full-Stack Reactive with Spring WebFlux + Angular - Oracle Code One 2018
PPTX
Reactive programming with Spring Webflux.pptx
PDF
Full Stack Reactive with React and Spring WebFlux Workshop - KCDC 2019
PDF
Full Stack Reactive with React and Spring WebFlux - PWX 2019
PDF
Full Stack Reactive with React and Spring WebFlux - Dublin JUG 2019
PPTX
Spring webflux
PDF
Full Stack Reactive with React and Spring WebFlux - Switzerland JUG 2020
PDF
Building RESTFUL APIs with Spring Webflux
PDF
Reactive&amp;reactor
PDF
Technologies That Make LINE QR Code Login Possible
PPTX
Reactive programming for java developers
PDF
Benefits of Reactive Programming with Reactor and Spring Boot 2 - Violeta Geo...
PDF
Reactive Streams in the Web
PPTX
Reactive solutions using java 9 and spring reactor
PDF
NCUG 2019: Super charge your API’s with Reactive streams
Guide to Spring Reactive Programming using WebFlux
Reactive programming with spring web flux
Reactive programming by spring webflux - DN Scrum Breakfast - Nov 2018
Introduction to Spring webflux
Full-Stack Reactive with Spring WebFlux + Angular - JConf Colombia 2019
Full-Stack Reactive with Spring WebFlux + Angular - Oracle Code One 2018
Reactive programming with Spring Webflux.pptx
Full Stack Reactive with React and Spring WebFlux Workshop - KCDC 2019
Full Stack Reactive with React and Spring WebFlux - PWX 2019
Full Stack Reactive with React and Spring WebFlux - Dublin JUG 2019
Spring webflux
Full Stack Reactive with React and Spring WebFlux - Switzerland JUG 2020
Building RESTFUL APIs with Spring Webflux
Reactive&amp;reactor
Technologies That Make LINE QR Code Login Possible
Reactive programming for java developers
Benefits of Reactive Programming with Reactor and Spring Boot 2 - Violeta Geo...
Reactive Streams in the Web
Reactive solutions using java 9 and spring reactor
NCUG 2019: Super charge your API’s with Reactive streams

More from Toshiaki Maki

PDF
Concourse x Spinnaker #concourse_tokyo
PDF
決済システムの内製化への旅 - SpringとPCFで作るクラウドネイティブなシステム開発 #jsug #sf_h1
PDF
Spring Boot Actuator 2.0 & Micrometer #jjug_ccc #ccc_a1
PDF
Zipkin Components #zipkin_jp
PDF
Spring Framework 5.0による Reactive Web Application #JavaDayTokyo
PDF
Data Microservices with Spring Cloud Stream, Task, and Data Flow #jsug #spri...
PDF
今すぐ始めるCloud Foundry #hackt #hackt_k
PDF
Team Support in Concourse CI 2.0 #concourse_tokyo
PDF
From Zero to Hero with REST and OAuth2 #jjug
PDF
Consumer Driven Contractsで REST API/マイクロサービスをテスト #m3tech
PDF
Implement Service Broker with Spring Boot #cf_tokyo
PDF
#jjug_ccc #ccc_gh5 What's new in Spring Framework 4.3 / Boot 1.4 + Pivotal's ...
PDF
Concourse CI Meetup Demo
PDF
Install Concourse CI with BOSH
PDF
Introduction to Concourse CI #渋谷Java
PDF
Cloud Foundy Java Client V 2.0 #cf_tokyo
Concourse x Spinnaker #concourse_tokyo
決済システムの内製化への旅 - SpringとPCFで作るクラウドネイティブなシステム開発 #jsug #sf_h1
Spring Boot Actuator 2.0 & Micrometer #jjug_ccc #ccc_a1
Zipkin Components #zipkin_jp
Spring Framework 5.0による Reactive Web Application #JavaDayTokyo
Data Microservices with Spring Cloud Stream, Task, and Data Flow #jsug #spri...
今すぐ始めるCloud Foundry #hackt #hackt_k
Team Support in Concourse CI 2.0 #concourse_tokyo
From Zero to Hero with REST and OAuth2 #jjug
Consumer Driven Contractsで REST API/マイクロサービスをテスト #m3tech
Implement Service Broker with Spring Boot #cf_tokyo
#jjug_ccc #ccc_gh5 What's new in Spring Framework 4.3 / Boot 1.4 + Pivotal's ...
Concourse CI Meetup Demo
Install Concourse CI with BOSH
Introduction to Concourse CI #渋谷Java
Cloud Foundy Java Client V 2.0 #cf_tokyo

Recently uploaded

PDF
GPUS and How to Program Them by Manya Bansal
PDF
DIGITAL FORENSICS - Notes for Everything.pdf
PDF
Energy Storage Landscape Clean Energy Ministerial
PPTX
Kanban India 2025 | Daksh Gupta | Modeling the Models, Generative AI & Kanban
PPTX
Building Cyber Resilience for 2026: Best Practices for a Secure, AI-Driven Bu...
PPTX
AI in Cybersecurity: Digital Defense by Yasir Naveed Riaz
PDF
Six Shifts For 2026 (And The Next Six Years)
PPTX
Cloud-and-AI-Platform-FY26-Partner-Playbook.pptx
PDF
Dev Dives: AI that builds with you - UiPath Autopilot for effortless RPA & AP...
PDF
Eredità digitale sugli smartphone: cosa resta di noi nei dispositivi mobili
PDF
Day 3 - Data and Application Security - 2nd Sight Lab Cloud Security Class
PDF
Making Sense of Raster: From Bit Depth to Better Workflows
PDF
Digit Expo 2025 - EICC Edinburgh 27th November
PDF
Session 1 - Solving Semi-Structured Documents with Document Understanding
PDF
Day 2 - Network Security ~ 2nd Sight Lab ~ Cloud Security Class ~ 2020
PDF
Day 5 - Red Team + Blue Team in the Cloud - 2nd Sight Lab Cloud Security Class
PDF
Internet_of_Things_IoT_for_Next_Generation_Smart_Systems_Utilizing.pdf
PDF
Usage Control for Process Discovery through a Trusted Execution Environment
PPTX
wob-report.pptxwob-report.pptxwob-report.pptx
PDF
ElyriaSoftware — Powering the Future with Blockchain Innovation
GPUS and How to Program Them by Manya Bansal
DIGITAL FORENSICS - Notes for Everything.pdf
Energy Storage Landscape Clean Energy Ministerial
Kanban India 2025 | Daksh Gupta | Modeling the Models, Generative AI & Kanban
Building Cyber Resilience for 2026: Best Practices for a Secure, AI-Driven Bu...
AI in Cybersecurity: Digital Defense by Yasir Naveed Riaz
Six Shifts For 2026 (And The Next Six Years)
Cloud-and-AI-Platform-FY26-Partner-Playbook.pptx
Dev Dives: AI that builds with you - UiPath Autopilot for effortless RPA & AP...
Eredità digitale sugli smartphone: cosa resta di noi nei dispositivi mobili
Day 3 - Data and Application Security - 2nd Sight Lab Cloud Security Class
Making Sense of Raster: From Bit Depth to Better Workflows
Digit Expo 2025 - EICC Edinburgh 27th November
Session 1 - Solving Semi-Structured Documents with Document Understanding
Day 2 - Network Security ~ 2nd Sight Lab ~ Cloud Security Class ~ 2020
Day 5 - Red Team + Blue Team in the Cloud - 2nd Sight Lab Cloud Security Class
Internet_of_Things_IoT_for_Next_Generation_Smart_Systems_Utilizing.pdf
Usage Control for Process Discovery through a Trusted Execution Environment
wob-report.pptxwob-report.pptxwob-report.pptx
ElyriaSoftware — Powering the Future with Blockchain Innovation

Introduction to Spring WebFlux #jsug #sf_a1

  • 1.
    1Introduction to SpringWebFlux2017-11-24Toshiaki Maki (@making) #jsug #sf_a1
  • 2.
    Who am I?2Toshiaki Maki (@making) https://blog.ik.amSr. Solutions Architect @Pivotal JapanSpring Framework 💖Cloud Foundry 💖
  • 3.
  • 4.
  • 5.
    ❓How many threadsare this app using?5
  • 6.
    ❓How many threadsare this app using?51. 200 -2. 100 - 2003. 50 - 1004. 10 - 505. 1 - 10
  • 7.
  • 8.
    Web Stacks inSpring 57Servlet ContainerServlet APISpring MVCServlet Stack
  • 9.
    Web Stacks inSpring 57Servlet ContainerServlet APISpring MVCReactive StackNetty, Servlet 3.1+,UndertowReactive StreamsSpring WebFluxServlet Stack
  • 10.
    Web Stacks inSpring 57Servlet ContainerServlet APISpring MVCReactive StackNetty, Servlet 3.1+,UndertowReactive StreamsSpring WebFluxServlet StackBlocking
  • 11.
    Web Stacks inSpring 57Servlet ContainerServlet APISpring MVCReactive StackNetty, Servlet 3.1+,UndertowReactive StreamsSpring WebFluxServlet StackBlocking
  • 12.
    Web Stacks inSpring 57Servlet ContainerServlet APISpring MVCReactive StackNetty, Servlet 3.1+,UndertowReactive StreamsSpring WebFluxServlet StackBlockingNon-Blocking
  • 13.
    Web Stacks inSpring 57Servlet ContainerServlet APISpring MVCReactive StackNetty, Servlet 3.1+,UndertowReactive StreamsSpring WebFluxServlet StackBlockingNon-Blocking
  • 14.
  • 15.
    9Reactive Stack🔄 Thread🔄Thread🔄 Thread🔄 Thread
  • 16.
    ❓Why are weintroducing Spring WebFlux?10
  • 17.
    ❓Why are weintroducing Spring WebFlux?10The goal of Spring WebFlux is to offer Springdevelopers a non-blocking event-loop styleprogramming model similar to node.js.
  • 18.
    the non-blocking asyncprogramming model is moreefficient for latency-sensitive workloads.– Blocking threads consume resources– mobile applications and interconnectedmicroservices❓Why are we introducing Spring WebFlux?10The goal of Spring WebFlux is to offer Springdevelopers a non-blocking event-loop styleprogramming model similar to node.js.
  • 19.
    Web Stacks inSpring 511Servlet ContainerServlet APISpring MVCReactive StackNetty, Servlet 3.1+,UndertowReactive StreamsSpring WebFluxServlet Stack
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
    5Flux<T> is aPublisher<T> for 0..n elements
  • 29.
  • 30.
    7Mono<T> is aPublisher<T> for 0..1 element
  • 31.
    Reactor18Flux<String> s =Flux.just("a", "b", "c", "d").map(String::toUpperCase);s.log("demo").subscribe();
  • 32.
    Reactor18Flux<String> s =Flux.just("a", "b", "c", "d").map(String::toUpperCase);s.log("demo").subscribe();Need to be subscribed!
  • 33.
    Reactor18Flux<String> s =Flux.just("a", "b", "c", "d").map(String::toUpperCase);s.log("demo").subscribe();Need to be subscribed!00:58:33.902 [main] INFO demo - | request(unbounded)00:58:33.903 [main] INFO demo - | onNext(A)00:58:33.903 [main] INFO demo - | onNext(B)00:58:33.903 [main] INFO demo - | onNext(C)00:58:33.903 [main] INFO demo - | onNext(D)00:58:33.903 [main] INFO demo - | onComplete()
  • 34.
    Reactor19Flux<String> s =Flux.just("a", "b", "c", "d").map(String::toUpperCase).delayElements(Duration.ofSeconds(1));s.log("demo").subscribe();
  • 35.
    Reactor19Flux<String> s =Flux.just("a", "b", "c", "d").map(String::toUpperCase).delayElements(Duration.ofSeconds(1));s.log("demo").subscribe();00:59:42.949 [main] INFO demo - request(unbounded)00:59:43.992 [parallel-1] INFO demo - onNext(A)00:59:44.994 [parallel-2] INFO demo - onNext(B)00:59:45.997 [parallel-3] INFO demo - onNext(C)00:59:46.999 [parallel-4] INFO demo - onNext(D)00:59:47.001 [parallel-4] INFO demo - onComplete()
  • 36.
    Reactor (Hot Stream♨)20Flux<String>s = Flux.<String>create(sink ->sink.next(...);});s.log("demo").subscribe();
  • 37.
    Reactor (Hot Stream♨)21twitter4j.TwiterStreamtw = ...;Flux<String> s = Flux.<String>create(sink ->sink.next(status.getText());});s.log("tweet").subscribe();
  • 38.
    Reactor (Hot Stream♨)22twitter4j.TwiterStreamtw = ...;Flux<String> s = Flux.<String>create(sink ->tw.addListener(new StatusAdapter() {public void onStatus(Status status) {sink.next(status.getText()); }public void onException(Exception e) {sink.error(e); }});sink.onCancel(tw::shutdown);tw.sample();});s.log("tweet").subscribe();
  • 39.
    Operators in Reactor23•map / indexed / flatMap / flatMapMany• collectList / collectMap / count• concat / merge / zip / when / combineLatest• repeat / interval• filter / sample / take / skip• window / windowWhile / buffer• ...https://projectreactor.io/docs/core/release/reference/docs/index.html#which-operator
  • 40.
  • 41.
  • 42.
    zip25Flux<GHUser> github =findGitHubUsers("foo", "bar","hoge");
Flux<FBUser> facebook = findFacebookUsers("foo","bar", "hoge");Flux<User> users = Flux.zip(github, facebook).map(tpl -> new User(tpl.getT1(), tpl.getT2()));users.subscribe();
  • 43.
  • 44.
    flatMap27Flux<GHUser> github =findGitHubUsers("foo", "bar","hoge");
Flux<User> users = github.flatMap(g-> findTweets(g).collectList().map(tweets -> new User(g, tweets)));users.subscribe();
  • 45.
    flatMap28Flux<GHUser> github =findGitHubUsers("foo", "bar","hoge");
Flux<User> users = github.map(g-> findTweets(g).collectList().map(tweets -> new User(g, tweets)));users.subscribe();
  • 46.
    flatMap28Flux<GHUser> github =findGitHubUsers("foo", "bar","hoge");
Flux<User> users = github.map(g-> findTweets(g).collectList().map(tweets -> new User(g, tweets)));users.subscribe();⚠ Can be compiled butfindTweets won't besubscribed
  • 47.
    flatMap29Flux<GHUser> github =findGitHubUsers("foo", "bar","hoge");Flux<User> users = github.map(g -> {Mono<User> u = findTweets(g).collectList().map(tweets -> new User(g, tweets));u.subscribe();return u;});users.subscribe();This will work,but use flatMap instead
  • 48.
    Web Stacks inSpring 530Servlet ContainerServlet APISpring MVCReactive StackNetty, Servlet 3.1+,UndertowReactive StreamsSpring WebFluxServlet Stack
  • 49.
    Spring MVC controller31@RestControllerpublicclass HelloController {private final Hello hello;public HelloController(Hello hello) {this.hello = hello;}@GetMapping("hello")public String hello() {return hello.sayHello();}}
  • 50.
    Spring MVC controller31@RestControllerpublicclass HelloController {private final Hello hello;public HelloController(Hello hello) {this.hello = hello;}@GetMapping("hello")public String hello() {return hello.sayHello();}}Blocking / Synchronous
  • 51.
    Spring WebFlux controller32@RestControllerpublicclass HelloController {private final Hello hello;public HelloController(Hello hello) {this.hello = hello;}@GetMapping("hello")public String hello() {return hello.sayHello();}}
  • 52.
    Spring WebFlux controller32@RestControllerpublicclass HelloController {private final Hello hello;public HelloController(Hello hello) {this.hello = hello;}@GetMapping("hello")public String hello() {return hello.sayHello();}}Non-blocking / Synchronous
  • 53.
    Spring WebFlux controller33@RestControllerpublicclass HelloController {private final Hello hello;public HelloController(Hello hello) {this.hello = hello;}@GetMapping("hello")public Mono<String> hello() {return Mono.fromCallable(hello::sayHello);}}
  • 54.
    Spring WebFlux controller33@RestControllerpublicclass HelloController {private final Hello hello;public HelloController(Hello hello) {this.hello = hello;}@GetMapping("hello")public Mono<String> hello() {return Mono.fromCallable(hello::sayHello);}}Non-blocking / Asynchronous
  • 55.
    Spring WebFlux controller33@RestControllerpublicclass HelloController {private final Hello hello;public HelloController(Hello hello) {this.hello = hello;}@GetMapping("hello")public Mono<String> hello() {return Mono.fromCallable(hello::sayHello);}}Non-blocking / AsynchronousYou don't need to subscribe the stream
  • 56.
    Spring WebFlux controller34@RestControllerpublicclass HelloController {private final Hello hello;public HelloController(Hello hello) {this.hello = hello;}@GetMapping("hello")public Mono<String> hello() {return Mono.fromCallable(hello::sayHello).flatMap(foo::abc).map(bar::xyz);}Non-blocking / Asynchronous
  • 57.
    Spring WebFlux controller34@RestControllerpublicclass HelloController {private final Hello hello;public HelloController(Hello hello) {this.hello = hello;}@GetMapping("hello")public Mono<String> hello() {return Mono.fromCallable(hello::sayHello).flatMap(foo::abc).map(bar::xyz);}Non-blocking / AsynchronousCould be executedin the other thread
  • 58.
    Spring MVC controller35@RestControllerpublicclass HelloController {private final Hello hello;public HelloController(Hello hello) {this.hello = hello;}@GetMapping("hello")public Mono<String> hello() {return Mono.fromCallable(hello::sayHello).flatMap(foo::abc).map(bar::xyz);}Blocking / Asynchronous
  • 59.
    Spring MVC controller35@RestControllerpublicclass HelloController {private final Hello hello;public HelloController(Hello hello) {this.hello = hello;}@GetMapping("hello")public Mono<String> hello() {return Mono.fromCallable(hello::sayHello).flatMap(foo::abc).map(bar::xyz);}Blocking / AsynchronousAsync support inServlet 3
  • 60.
    ∞ Stream36@RestControllerpublic classHelloController {@GetMapping("hello")public Flux<String> hello() {Flux<String> hello = Flux.just("hello").delayElement(Duration.ofMillis(100));hello.subscribe();return hello.repeat(); // ∞ Stream}}
  • 61.
  • 62.
    Content Negotiation38Accept: text/event-streamAccept:application/stream+jsonAccept: application/json
  • 63.
    Content Negotiation38Accept: text/event-streamAccept:application/stream+jsonAccept: application/json✅ Backpressure✅ Backpressure❌ Backpressure
  • 64.
    Content Negotiation39@GetMapping("hello")public Flux<Message>hello() {return Mono.just(new Message("a")).repeat().take(100).log("message");}
  • 65.
    40$ curl -v-H "Accept: text/event-stream" localhost:8080/messages> GET /messages HTTP/1.1> Host: localhost:8080> Accept: text/event-stream>< HTTP/1.1 200 OK< transfer-encoding: chunked< Content-Type: text/event-stream<data:{"text":"a"}data:{"text":"a"}data:{"text":"a"}...
  • 66.
    40$ curl -v-H "Accept: text/event-stream" localhost:8080/messages> GET /messages HTTP/1.1> Host: localhost:8080> Accept: text/event-stream>< HTTP/1.1 200 OK< transfer-encoding: chunked< Content-Type: text/event-stream<data:{"text":"a"}data:{"text":"a"}data:{"text":"a"}...INFO 48330 --- [ctor-http-nio-2] message : request(1)INFO 48330 --- [ctor-http-nio-2] message : onNext(a)INFO 48330 --- [ctor-http-nio-2] message : request(31)INFO 48330 --- [ctor-http-nio-2] message : onNext(a)INFO 48330 --- [ctor-http-nio-2] message : onNext(a)... ...INFO 48330 --- [ctor-http-nio-2] message : onNext(a)INFO 48330 --- [ctor-http-nio-2] message : request(24)INFO 48330 --- [ctor-http-nio-2] message : onNext(a)INFO 48330 --- [ctor-http-nio-2] message : onNext(a)... ...INFO 48330 --- [ctor-http-nio-2] message : request(24)INFO 48330 --- [ctor-http-nio-2] message : onNext(a)... ...INFO 48330 --- [ctor-http-nio-2] message : onComplete()
  • 67.
    40$ curl -v-H "Accept: text/event-stream" localhost:8080/messages> GET /messages HTTP/1.1> Host: localhost:8080> Accept: text/event-stream>< HTTP/1.1 200 OK< transfer-encoding: chunked< Content-Type: text/event-stream<data:{"text":"a"}data:{"text":"a"}data:{"text":"a"}...INFO 48330 --- [ctor-http-nio-2] message : request(1)INFO 48330 --- [ctor-http-nio-2] message : onNext(a)INFO 48330 --- [ctor-http-nio-2] message : request(31)INFO 48330 --- [ctor-http-nio-2] message : onNext(a)INFO 48330 --- [ctor-http-nio-2] message : onNext(a)... ...INFO 48330 --- [ctor-http-nio-2] message : onNext(a)INFO 48330 --- [ctor-http-nio-2] message : request(24)INFO 48330 --- [ctor-http-nio-2] message : onNext(a)INFO 48330 --- [ctor-http-nio-2] message : onNext(a)... ...INFO 48330 --- [ctor-http-nio-2] message : request(24)INFO 48330 --- [ctor-http-nio-2] message : onNext(a)... ...INFO 48330 --- [ctor-http-nio-2] message : onComplete()Backpressure
  • 68.
    (FYI) In caseof Servlet Stack41
  • 69.
    (FYI) In caseof Servlet Stack41INFO 61528 --- [nio-8080-exec-1] message : request(1)INFO 61528 --- [nio-8080-exec-1] message : onNext(a)INFO 61528 --- [ MvcAsync1] message : request(1)INFO 61528 --- [ MvcAsync1] message : onNext(a)INFO 61528 --- [ MvcAsync2] message : request(1)... ...INFO 61528 --- [ MvcAsync98] message : request(1)INFO 61528 --- [ MvcAsync98] message : onNext(a)INFO 61528 --- [ MvcAsync99] message : request(1)INFO 61528 --- [ MvcAsync99] message : onNext(a)INFO 61528 --- [ MvcAsync99] message : onComplete()
  • 70.
    (FYI) In caseof Servlet Stack41INFO 61528 --- [nio-8080-exec-1] message : request(1)INFO 61528 --- [nio-8080-exec-1] message : onNext(a)INFO 61528 --- [ MvcAsync1] message : request(1)INFO 61528 --- [ MvcAsync1] message : onNext(a)INFO 61528 --- [ MvcAsync2] message : request(1)... ...INFO 61528 --- [ MvcAsync98] message : request(1)INFO 61528 --- [ MvcAsync98] message : onNext(a)INFO 61528 --- [ MvcAsync99] message : request(1)INFO 61528 --- [ MvcAsync99] message : onNext(a)INFO 61528 --- [ MvcAsync99] message : onComplete()✅ Backpressure
  • 71.
    42$ curl -v-H "Accept: application/stream+json" localhost:8080/messages> GET /messages HTTP/1.1> Host: localhost:8080> Accept: application/stream+json>< HTTP/1.1 200 OK< transfer-encoding: chunked< Content-Type: application/stream+json;charset=UTF-8<{"text":"a"}{"text":"a"}{"text":"a"}...
  • 72.
    42$ curl -v-H "Accept: application/stream+json" localhost:8080/messages> GET /messages HTTP/1.1> Host: localhost:8080> Accept: application/stream+json>< HTTP/1.1 200 OK< transfer-encoding: chunked< Content-Type: application/stream+json;charset=UTF-8<{"text":"a"}{"text":"a"}{"text":"a"}...INFO 48330 --- [ctor-http-nio-4] message : request(1)INFO 48330 --- [ctor-http-nio-4] message : onNext(a)INFO 48330 --- [ctor-http-nio-4] message : request(31)INFO 48330 --- [ctor-http-nio-4] message : onNext(a)INFO 48330 --- [ctor-http-nio-4] message : onNext(a)... ...INFO 48330 --- [ctor-http-nio-4] message : onNext(a)INFO 48330 --- [ctor-http-nio-4] message : request(24)INFO 48330 --- [ctor-http-nio-4] message : onNext(a)INFO 48330 --- [ctor-http-nio-4] message : onNext(a)... ...INFO 48330 --- [ctor-http-nio-4] message : request(24)INFO 48330 --- [ctor-http-nio-4] message : onNext(a)... ...INFO 48330 --- [ctor-http-nio-4] message : onComplete()
  • 73.
    42$ curl -v-H "Accept: application/stream+json" localhost:8080/messages> GET /messages HTTP/1.1> Host: localhost:8080> Accept: application/stream+json>< HTTP/1.1 200 OK< transfer-encoding: chunked< Content-Type: application/stream+json;charset=UTF-8<{"text":"a"}{"text":"a"}{"text":"a"}...INFO 48330 --- [ctor-http-nio-4] message : request(1)INFO 48330 --- [ctor-http-nio-4] message : onNext(a)INFO 48330 --- [ctor-http-nio-4] message : request(31)INFO 48330 --- [ctor-http-nio-4] message : onNext(a)INFO 48330 --- [ctor-http-nio-4] message : onNext(a)... ...INFO 48330 --- [ctor-http-nio-4] message : onNext(a)INFO 48330 --- [ctor-http-nio-4] message : request(24)INFO 48330 --- [ctor-http-nio-4] message : onNext(a)INFO 48330 --- [ctor-http-nio-4] message : onNext(a)... ...INFO 48330 --- [ctor-http-nio-4] message : request(24)INFO 48330 --- [ctor-http-nio-4] message : onNext(a)... ...INFO 48330 --- [ctor-http-nio-4] message : onComplete()✅ Backpressure
  • 74.
    43$ curl -v-H "Accept: application/json" localhost:8080/messages> GET /messages HTTP/1.1> Host: localhost:8080> Accept: application/json>< HTTP/1.1 200 OK< transfer-encoding: chunked< Content-Type: application/json;charset=UTF-8<[{"text":"a"},{"text":"a"},...,{"text":"a"},{"text":"a"},{"text":"a"},{"text":"a"}]
  • 75.
    43$ curl -v-H "Accept: application/json" localhost:8080/messages> GET /messages HTTP/1.1> Host: localhost:8080> Accept: application/json>< HTTP/1.1 200 OK< transfer-encoding: chunked< Content-Type: application/json;charset=UTF-8<[{"text":"a"},{"text":"a"},...,{"text":"a"},{"text":"a"},{"text":"a"},{"text":"a"}]INFO 48330 --- [ctor-http-nio-3] message : request(unbounded)INFO 48330 --- [ctor-http-nio-3] message : onNext(a)INFO 48330 --- [ctor-http-nio-3] message : onNext(a)INFO 48330 --- [ctor-http-nio-3] message : onNext(a)INFO 48330 --- [ctor-http-nio-3] message : onNext(a)... ...INFO 48330 --- [ctor-http-nio-3] message : onNext(a)INFO 48330 --- [ctor-http-nio-3] message : onNext(a)INFO 48330 --- [ctor-http-nio-3] message : onNext(a)INFO 48330 --- [ctor-http-nio-3] message : onNext(a)INFO 48330 --- [ctor-http-nio-3] message : onComplete()
  • 76.
    43$ curl -v-H "Accept: application/json" localhost:8080/messages> GET /messages HTTP/1.1> Host: localhost:8080> Accept: application/json>< HTTP/1.1 200 OK< transfer-encoding: chunked< Content-Type: application/json;charset=UTF-8<[{"text":"a"},{"text":"a"},...,{"text":"a"},{"text":"a"},{"text":"a"},{"text":"a"}]INFO 48330 --- [ctor-http-nio-3] message : request(unbounded)INFO 48330 --- [ctor-http-nio-3] message : onNext(a)INFO 48330 --- [ctor-http-nio-3] message : onNext(a)INFO 48330 --- [ctor-http-nio-3] message : onNext(a)INFO 48330 --- [ctor-http-nio-3] message : onNext(a)... ...INFO 48330 --- [ctor-http-nio-3] message : onNext(a)INFO 48330 --- [ctor-http-nio-3] message : onNext(a)INFO 48330 --- [ctor-http-nio-3] message : onNext(a)INFO 48330 --- [ctor-http-nio-3] message : onNext(a)INFO 48330 --- [ctor-http-nio-3] message : onComplete()❌ Backpressure
  • 77.
    Flux -> Mono44@GetMapping("hello")publicMono<List<Message>> hello() {return Mono.just(new Message("a")).repeat().take(100).collectList().log("message");}
  • 78.
    Flux -> Mono45@GetMapping("hello")publicMono<List<Message>> hello() {return Mono.just(new Message("a")).repeat().take(100).collectList().log("message");}$ curl -v -H "Accept: application/stream+json" localhost:8080/messages> GET /messages HTTP/1.1> Host: localhost:8080> Accept: application/stream+json>< HTTP/1.1 200 OK< transfer-encoding: chunked< Content-Type: application/stream+json;charset=UTF-8<[{"text":"a"},{"text":"a"},...,{"text":"a"},{"text":"a"},{"text":"a"},{"text":"a"}]
  • 79.
    Flux -> Mono45@GetMapping("hello")publicMono<List<Message>> hello() {return Mono.just(new Message("a")).repeat().take(100).collectList().log("message");}$ curl -v -H "Accept: application/stream+json" localhost:8080/messages> GET /messages HTTP/1.1> Host: localhost:8080> Accept: application/stream+json>< HTTP/1.1 200 OK< transfer-encoding: chunked< Content-Type: application/stream+json;charset=UTF-8<[{"text":"a"},{"text":"a"},...,{"text":"a"},{"text":"a"},{"text":"a"},{"text":"a"}]INFO 48330 --- [ctor-http-nio-3] message : | request(1)INFO 48330 --- [ctor-http-nio-3] message : | onNext([a, a, a, a,a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a,a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a,a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a])INFO 48330 --- [ctor-http-nio-3] message : | request(31)INFO 48330 --- [ctor-http-nio-3] message : | onComplete()
  • 80.
    Flux -> Mono45@GetMapping("hello")publicMono<List<Message>> hello() {return Mono.just(new Message("a")).repeat().take(100).collectList().log("message");}$ curl -v -H "Accept: application/stream+json" localhost:8080/messages> GET /messages HTTP/1.1> Host: localhost:8080> Accept: application/stream+json>< HTTP/1.1 200 OK< transfer-encoding: chunked< Content-Type: application/stream+json;charset=UTF-8<[{"text":"a"},{"text":"a"},...,{"text":"a"},{"text":"a"},{"text":"a"},{"text":"a"}]INFO 48330 --- [ctor-http-nio-3] message : | request(1)INFO 48330 --- [ctor-http-nio-3] message : | onNext([a, a, a, a,a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a,a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a,a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a])INFO 48330 --- [ctor-http-nio-3] message : | request(31)INFO 48330 --- [ctor-http-nio-3] message : | onComplete()❌ Backpressure
  • 81.
    Spring WebFlux controller(Receiving Flux)46@RestControllerpublic class HelloController {@PostMapping("upper")public Flux<String>upper(@RequestBody Flux<String> input) {Flux<String> output = input.map(String::toUpperCase);return output;}}
  • 82.
    47$ telnet localhost8080POST /upper HTTP/1.1Host: localhost:8080Transfer-Encoding: chunkedAccept: text/event-streamContent-Type: text/plain3fooHTTP/1.1 200 OKtransfer-encoding: chunkedContent-Type: text/event-stream5data:4FOO14java5data:5JAVA1
  • 83.
    47$ telnet localhost8080POST /upper HTTP/1.1Host: localhost:8080Transfer-Encoding: chunkedAccept: text/event-streamContent-Type: text/plain3fooHTTP/1.1 200 OKtransfer-encoding: chunkedContent-Type: text/event-stream5data:4FOO14java5data:5JAVA1Request Body
  • 84.
    47$ telnet localhost8080POST /upper HTTP/1.1Host: localhost:8080Transfer-Encoding: chunkedAccept: text/event-streamContent-Type: text/plain3fooHTTP/1.1 200 OKtransfer-encoding: chunkedContent-Type: text/event-stream5data:4FOO14java5data:5JAVA1Request BodyResponse Body
  • 85.
    Spring WebFlux controller(Receiving Flux)48@PostMapping("upper")public Flux<String>upper(@RequestBody Flux<String> input) {Flux<String> output = input.map(String::toUpperCase).flatMap(s -> Flux.interval(Duration.ofSeconds(1)).map(i -> s + i).take(3));return output;}
  • 86.
    493fooHTTP/1.1 200 OKtransfer-encoding:chunkedContent-Type: text/event-stream5data:5FOO015data:5FOO115data:5FOO21
  • 87.
  • 88.
    Spring MVC controller(Receiving Flux)51@RestControllerpublic class HelloController {@PostMapping("upper")public Flux<String>hello(@RequestBody Flux<String> input) {Flux<String> output = input.map(String::toUpperCase);return output;}}
  • 89.
    Spring MVC controller(Receiving Flux)51@RestControllerpublic class HelloController {@PostMapping("upper")public Flux<String>hello(@RequestBody Flux<String> input) {Flux<String> output = input.map(String::toUpperCase);return output;}}🙅
  • 90.
    Spring MVC controller(Receiving Flux)51@RestControllerpublic class HelloController {@PostMapping("upper")public Flux<String>hello(@RequestBody Flux<String> input) {Flux<String> output = input.map(String::toUpperCase);return output;}}🙅Reactive type are supported only ascontroller method return values
  • 91.
    Broadcast stream52@RestControllerpublic classHelloController {private final Flux<String> flux;public HelloController() {this.flux = this.createHotStream().share();this.flux.subscribe();}@GetMapping("hello")public Flux<String> hello() {return this.flux;}}
  • 92.
    Broadcast stream52@RestControllerpublic classHelloController {private final Flux<String> flux;public HelloController() {this.flux = this.createHotStream().share();this.flux.subscribe();}@GetMapping("hello")public Flux<String> hello() {return this.flux;}}this stream is shared by all httpclients!
  • 93.
  • 94.
  • 95.
  • 96.
    Web Stacks inSpring 555Servlet ContainerServlet APISpring MVCReactive StackNetty, Servlet 3.1+,UndertowReactive StreamsSpring WebFluxServlet Stack
  • 97.
    Web Stacks inSpring 555Servlet ContainerServlet APISpring MVCReactive StackNetty, Servlet 3.1+,UndertowReactive StreamsSpring WebFluxServlet Stack•Annotated Controller•Function Endpoints (WebFlux.fn)
  • 98.
    @Configurationpublic class RouterConfig{@Beanpublic RouterFunctions<ServerResponse> routes() {return route(GET("hello"), req -> {return ServerReponse.ok().body(Mono.just("hello"), String.class);});}}Spring WebFlux.fn56
  • 99.
    @Configurationpublic class RouterConfig{@Beanpublic RouterFunctions<ServerResponse> routes() {return route(GET("hello"), req -> {return ServerReponse.ok().body(Mono.just("hello"), String.class);});}}lambdaSpring WebFlux.fn56
  • 100.
    Spring WebFlux.fn57@Configurationpublic classRouterConfig {@Beanpublic RouterFunctions<ServerResponse>routes(HelloHandler helloHandler) {return route(GET("hello"), helloHandler::hello);}}
  • 101.
    Spring WebFlux.fn58public RouterFunctions<ServerResponse>routes(PersonHandelerph) {return route(GET("person"), ph::findAll).andRoute(POST("person"), ph::create).andRoute(GET("person/{id}"), ph::findOne).andRoute(PUT("person/{id}"), ph::update).andRoute(DELETE("person/{id}"), ph::delete));}
  • 102.
    Spring WebFlux.fn59public RouterFunctions<ServerResponse>routes(PersonHandelerph) {return nest(path("person"),route(GET("/"), ph::findAll).andRoute(POST("/"), ph::create).andRoute(GET("/{id}"), ph::findOne).andRoute(PUT("/{id}"), ph::update).andRoute(DELETE("/{id}"), ph::delete)));}
  • 103.
    Web Stacks inSpring 560Servlet ContainerServlet APISpring MVCReactive StackNetty, Servlet 3.1+,UndertowReactive StreamsSpring WebFluxServlet Stack
  • 104.
    Web Stacks inSpring 560Servlet ContainerServlet APISpring MVCReactive StackNetty, Servlet 3.1+,UndertowReactive StreamsSpring WebFluxServlet StackReactive Web Client
  • 105.
    Reactive Web Client61@RestControllerpublicclass HelloController {private final WebClient client;public HelloController(WebClinet.Builer b) {this.client = b.build();}@GetMapping("hello")public Flux<String> hello() {return this.client.get().uri("http://blahblah").retrieve().bodyToFlux(String.class);}}
  • 106.
    Reactive Web Client62Flux<User>users = webClient.get().uri("http://user-service").exchange() // => Mono<ClientResponse>.flatMap(r -> r.bodyToFlux(User.class));// short-cutFlux<User> users = webClient.get().uri("http://user-service").retrieve().bodyToFlux(User.class);
  • 107.
    Reactive Web Client63Mono<User>user = webClient.get().uri("http://user-service/{id}", id).header("X-Foo", "foo").retrieve().bodyToMono(User.class);Mono<User> user = ...Mono<Void> created = webClient.post().uri("http://user-service").body(user, User.class).retrieve().bodyToMono(Void.class);
  • 108.
    From RestTemplate toWebClient64@Controllerpublic class UserController {@GetMapping("users")public String hello(Model model) {List<User> users = restTemplate.getForObject("/users", List.class);model.addAttribute("users", users);return "users";}}
  • 109.
    From RestTemplate toWebClient65@Controllerpublic class UserController {@GetMapping("users")public String hello(Model model) {Flux<User> users = client.get().uri("/users").retrieve().bodyToFlux(String.class);model.addAttribute("users", users);return "users";}}
  • 110.
    66<html><body><ul><li data-th-each="user :${user}">[[${user}]]</li></ul></body></html>
  • 111.
    66<html><body><ul><li data-th-each="user :${user}">[[${user}]]</li></ul></body></html>😀 Spring WebFlux
  • 112.
    66<html><body><ul><li data-th-each="user :${user}">[[${user}]]</li></ul></body></html>😀 Spring WebFlux😨 Spring MVC
  • 113.
    From RestTemplate toWebClient (Spring MVC)67@Controllerpublic class UserController {@GetMapping("users")public Mono<String> hello(Model model) {Flux<User> users = client.get().uri("/users").retrieve().bodyToFlux(String.class);users.collectList().map(x -> {model.addAttribute("users", x);return "users";});}
  • 114.
    From RestTemplate toWebClient (Spring MVC)67@Controllerpublic class UserController {@GetMapping("users")public Mono<String> hello(Model model) {Flux<User> users = client.get().uri("/users").retrieve().bodyToFlux(String.class);users.collectList().map(x -> {model.addAttribute("users", x);return "users";});}😀 Spring MVC
  • 115.
    From RestTemplate toWebClient (Spring MVC)68@Controllerpublic class UserController {@GetMapping("users")public Mono<String> hello(Model model) {Flux<User> users = ...;Mono<Sting> foo = ...;return Mono.zip(user.collectList(), foo)   .map(tpl -> {model.addAttribute("users", tpl.getT1());model.addAttribute("foo", tpl.getT2());return "users";});}}
  • 116.
    69String url ="http://httpbin.org/delay/1";@GetMapping("hello")public Mono<String> hello() {Mono<String> mono = Mono.just(restTemplate.getForObject(url));return mono;}
  • 117.
    ⚠ Don't blockthe thread in WebFlux!69String url = "http://httpbin.org/delay/1";@GetMapping("hello")public Mono<String> hello() {Mono<String> mono = Mono.just(restTemplate.getForObject(url));return mono;}
  • 118.
    ⚠ Don't blockthe thread in WebFlux!69String url = "http://httpbin.org/delay/1";@GetMapping("hello")public Mono<String> hello() {Mono<String> mono = Mono.just(restTemplate.getForObject(url));return mono;} 😠 Blocking!
  • 119.
    ⚠ Don't blockthe thread in WebFlux!70String url = "http://httpbin.org/delay/1";@GetMapping("hello")public Mono<String> hello() {Mono<String> mono = Mono.fromCallable(() ->restTemplate.getForObject(url)).subscribeOn(Schedulers.elastic());return mono;}
  • 120.
    ⚠ Don't blockthe thread in WebFlux!70String url = "http://httpbin.org/delay/1";@GetMapping("hello")public Mono<String> hello() {Mono<String> mono = Mono.fromCallable(() ->restTemplate.getForObject(url)).subscribeOn(Schedulers.elastic());return mono;} Switch the execution context
  • 121.
    71ConcurrencyThroughput [trans /sec]Core i7 2.7 GHz4 Core x HT
  • 122.
    Reactive Support inSpring Projects72Spring DataReactive StackNetty, Servlet 3.1+,UndertowReactive StreamsSpring WebFluxSpring SecurityThymeleaf
  • 123.
    Spring Data Kay73Reactivesupport for• Redis• MongoDB• Couchbase• CassandraInfinite streams from the database with @Tailable
  • 124.
    Reactive Non-Blocking DataAccess74public interface ReactiveCrudRepository<ID,T> {Mono<T> findById(ID id);Mono<T> findById(Mono<ID> id);Flux<T> findAll();Mono<Long> count();Mono<T> save(T entity);Mono<T> saveAll(Publisher<T> entityStream);Mono<Void> delete(T entity)// ...}
  • 125.
    Tailable Cursor Supportfor MongoDB75public interface MessageRepositoryextends ReactiveMongoRepository<Message,String>{@TailableFlux<Message>findByUpdatedAtGreaterThan(Instant target);}
  • 126.
    Broadcast updated messagesin MongoDB76@RestControllerpublic class HelloController {private final Flux<Message> messages;public HelloController(MessageRepository repo) {this.messages = repo.findByUpdatedAtGreaterThan(Instant.now()).share();this.messages.subscribe();}@GetMapping("messages")public Flux<Message> messages() {return this.messages;}}
  • 127.
    But, but, JDBCis blocking .... 😭77
  • 128.
    But, but, JDBCis blocking .... 😭77Let's see what will happen in next Java
  • 129.
    But, but, JDBCis blocking .... 😭77Let's see what will happen in next Javahttps://static.rainfocus.com/oracle/oow17/sess/1491948952321001dm4m/PF/JavaOne%2017%20-%20CON1491_1506954898905001IipH.pdf
  • 130.
  • 131.
  • 132.
    Spring Security Reactive79@BeanpublicSecurityWebFilterChainspringWebFilterChain(HttpSecurity http) {return http.authorizeExchange().pathMatchers("/v2/**").hasRole("ADMIN").and().httpBasic().and().build();}@Beanpublic MapUserDetailsRepository userDetailsRepository() {return new MapUserDetailsRepository(...);}
  • 133.
    Thymeleaf 3.0 reactivesupport80• "full mode"• "chunked mode"
=> Progressive rendering. Good for large pages• "data-driven mode" 
=> Rendering template fragments as Server-Sent Events
• https://github.com/thymeleaf/thymeleaf-spring/issues/132• https://github.com/spring-projects/spring-boot/issues/8124• https://speakerdeck.com/dfernandez/o-2017-getting-thymeleaf-ready-for-spring-5-and-reactive?slide=18transfer-encoding: chunked
  • 134.
    Data-driven mode81@GetMapping("users")public Stringhello(Model model) {Flux<User> users = userService.findAll();ReactiveDataDriverContextVariable v= new ReactiveDataDriverContextVariable(users, 1);model.addAttribute("users", v);return "users";}
  • 135.
  • 136.
  • 137.
    Reactive Application Patterns(as of 2017)83Spring WebFluxSpring WebFluxFrontend BackendHTTP NoSQLWebClient Spring Data
  • 138.
    Reactive Application Patterns(as of 2017)84Spring MVCSpring WebFluxFrontend BackendHTTP RDBJDBCWebClient
  • 139.
    Reactive Application Patterns(as of 2017)85Spring MVCSpring MVCFrontend BackendHTTP RDBJDBCWebClient
  • 140.
    Spring Cloud Gateway86AGateway built on Spring Framework 5.0 and SpringBoot 2.0 providing routing and morehttp://cloud.spring.io/spring-cloud-gateway/http://cloud.spring.io/spring-cloud-gateway/2.0.x/single/spring-cloud-gateway.html
  • 141.
    Start Spring 5& Spring Boot 287
  • 142.
    Servlet Stack ⇄Reactive Stack88<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency>Reactive StackServlet Stack
  • 143.
    Servlet Stack ⇄Reactive Stack89<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webflux</artifactId></dependency><dependency><groupId>io.projectreactor.ipc</groupId><artifactId>reactor-netty</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency>Reactive StackServlet Stack+WebClient
  • 144.
    Thanks!90Resources• Servlet vsReactive Stacks in Five Use Cases• Spring Boot 2 0 Web Applications• Spring Framework 5: Themes & Trends• Why Spring ❤ Kotlin

[8]ページ先頭

©2009-2025 Movatter.jp