SlideShare a Scribd company logo
Reactive Streams in the Web
Florian Stefan | eBay Classifieds Group | JUG Saxony 2017
Who am I?
§ Florian Stefan
§ mobile.de (eBay Classifieds Group)
§ https://ebaytech.berlin/
§ fstefan@ebay.com | @f_s_t_e_f_a_n
§ https://github.com/florian-stefan/
Contents
1) Motivation: Distributed Systems
2) Reactive Streams with Reactor
3) Progressive HTML Rendering with Spring 5
It‘s about concepts rather than details!
Reactive Streams in the Web
Why are we here?
Why are we here?
It’s a distributed world!
Why are we here?
Service A Service B
Why are we here?
Service A Service B
Service B
Service B
Service A
Service A
Why are we here?
Service A Service B
Service B
Service B
Service A
Service A
Why are we here?
Service A Service B
BFF
BFF
Service B
Service B
Service A
Service A
Why are we here?
Service A Service B
BFF
BFF
BFF
Service B
Service B
Service A
Service A
Why are we here?
Service A Service B
BFF
How are we doing this?
How are we doing this?
Service A
How are we doing this?
Service A
Database
HTTP
How are we doing this?
Service A
Database
HTTP
How are we doing this?
Service A
Database
Topic
HTTP
How are we doing this?
Service A
Database
Topic
HTTP
How are we doing this?
Service A
Database
Topic Service B Topic
HTTP
How are we doing this?
Service A
Database
Topic Service B Topic
BFF
HTTP
How are we doing this?
Service A
Database
Topic Service B Topic
BFF
Event-Driven Systems
HTTP
How are we doing this?
Service A
Database
Topic Service B Topic
BFF
Event-Driven Systems
Concurrent Remote Calls
HTML Rendering
BFF
Widget Widget
Widget
Widget Widget
Concurrent Remote Calls
HTML Rendering
BFF
Widget Widget
Widget
Widget Widget
Concurrent Remote Calls
HTML Rendering
BFF
Widget Widget
Widget
Widget Widget
Concurrent Remote Calls
HTML Rendering
Server-Side Rendering
BFF
Widget Widget
Widget
Widget Widget
Concurrent Remote Calls
HTML Rendering
Server-Side Rendering
AJAX
BFF
Widget Widget
Widget
Widget Widget
Concurrent Remote Calls
HTML Rendering
HTML Rendering
HTML Rendering
HTML Rendering
Server-Side Rendering
§ Time To First Byte
§ Prerendering / Caching
HTML Rendering
Server-Side Rendering
§ Time To First Byte
§ Prerendering / Caching
HTML Rendering
HTML Rendering
HTML Rendering
HTML Rendering
HTML Rendering
AJAX
§ Head-of-line blocking
§ Multiplexing with HTTP/2
HTML Rendering
AJAX
§ Head-of-line blocking
§ Multiplexing with HTTP/2
HTML Rendering
Reactive Streams in the Web
Can we do better?
Can we do better?
How expensive will it be?
Reactive Streams in the Web
The Reactive Landscape
Distributed
Systems
Concurrent
Remote Calls
Event-Driven
Systems
Distributed
Systems
Concurrent
Remote Calls
Event-Driven
Systems
Distributed
Systems
Reactive
Streams
Concurrent
Remote Calls
Event-Driven
Systems
Distributed
Systems
Reactive
Streams
Backpressure
Concurrent
Remote Calls
Event-Driven
Systems
Distributed
Systems
Reactive
Streams
Reactive
Extensions
Akka Streams
Backpressure
Concurrent
Remote Calls
Event-Driven
Systems
Distributed
Systems
Reactive
Streams
Reactive
Extensions
RxJava
Reactor
Akka Streams
Backpressure
Concurrent
Remote Calls
Event-Driven
Systems
Distributed
Systems
Reactive
Streams
Reactive
Extensions
Marble
Diagrams
RxJava
Reactor
Akka Streams
Backpressure
Concurrent
Remote Calls
Event-Driven
Systems
Distributed
Systems
Reactive
Streams
Reactive
Extensions
Marble
Diagrams
RxJava
Reactor
Akka Streams
Backpressure
Higher-Order
Functions
Concurrent
Remote Calls
Event-Driven
Systems
Distributed
Systems
Reactive
Streams
Reactive
Extensions
Marble
Diagrams
RxJava
Reactor
Akka Streams
Backpressure
Declarative
Programming
Higher-Order
Functions
Concurrent
Remote Calls
Event-Driven
Systems
Distributed
Systems
Reactive
Systems
Reactive
Streams
Reactive
Extensions
Marble
Diagrams
RxJava
Reactor
Akka Streams
Backpressure
Declarative
Programming
Higher-Order
Functions
Concurrent
Remote Calls
Event-Driven
Systems
Distributed
Systems
Reactive
Systems
Reactive
Manifesto
Reactive
Streams
Reactive
Extensions
Marble
Diagrams
RxJava
Reactor
Akka Streams
Backpressure
Declarative
Programming
Higher-Order
Functions
Concurrent
Remote Calls
Event-Driven
Systems
Distributed
Systems
Reactive
Systems
Reactive
Manifesto
Reactive
Streams
Reactive
Extensions
Marble
Diagrams
RxJava
Reactor
Akka Streams
Backpressure
Declarative
Programming
Higher-Order
Functions
Reactive Streams in the Web
The Reactive Manifesto
Responsive
Responsive
Elastic
Responsive
Elastic Resilient
Responsive
Elastic Resilient
Message Driven
Responsive
Elastic Resilient
Message Driven
React to User
Responsive
Elastic Resilient
Message Driven
React to User
React to Load
Responsive
Elastic Resilient
Message Driven
React to User
React to Load React to Failure
Responsive
Elastic Resilient
Message Driven
React to User
React to Load React to Failure
React to Events
Reactive Streams in the Web
Paths to Reactive Systems
Concurrent
Remote Calls
Event-Driven
Systems
Distributed
Systems
Reactive
Systems
Reactive
Manifesto
Reactive
Streams
Reactive
Extensions
Marble
Diagrams
RxJava
Reactor
Akka Streams
Backpressure
Declarative
Programming
Higher-Order
Functions
Reactive Extensions
Reactive Extensions
Service
Reactive Extensions
Service
some
thing
Reactive Extensions
API
Service
some
thing
public interface Somethings {
Something loadById(int id);
}
Reactive Extensions
API
Service
some
thing
Reactive Extensions
API
Service
some
thing
public Something loadById(int id) {
return somethings.loadById(id);
}
Reactive Extensions
API
Service
some
thing
Service
Reactive Extensions
API
Service
some
thing
Service API
Reactive Extensions
API
Service
some
thing
Service API Resource
Reactive Extensions
API
Service
some
thing
Service API Resource
Network Boundary
Reactive Extensions
API
Service
some
thing
Service API Resource
Network Boundary
Reactive Extensions
API
Service
some
thing
Service API Resource
Network Boundary
Reactive Extensions
API
Service
some
thing
Service API Resource
Network Boundary
Reactive Extensions
API
Service
some
thing
Service API Resource
Network Boundary
Reactive Extensions
API
Service
some
thing
Reactive Extensions
API
Service
some
thing
public CompletableFuture<Something> loadById(int id) {
return supplyAsync(() -> somethings.loadById(id), executor);
}
Reactive Extensions
API
Service
some
thing
Network Boundary
ResourceService API
Reactive Extensions
API
Service
some
thing
Network Boundary
ResourceService API
Reactive Extensions
API
Service
some
thing
Network Boundary
ResourceService API
Reactive Extensions
API
Service
some
thing
Network Boundary
ResourceService API
Reactive Extensions
API
Service
some
thing
public interface Somethings {
void loadById(int id, BiConsumer<Exception, Something> callback);
}
Reactive Extensions
API
Service
some
thing
public CompletableFuture<Something> loadById(int id) {
CompletableFuture<Something> eventualSomething =
new CompletableFuture<>();
somethings.loadById(id, (error, something) -> {
if (error != null) {
eventualSomething.completeExceptionally(error);
} else {
eventualSomething.complete(something);
}
});
return eventualSomething;
}
Reactive Extensions
API
Service
some
thing
Network Boundary
ResourceService API
Reactive Extensions
API
Service
some
thing
Network Boundary
ResourceService API
Reactive Extensions
API
Service
some
thing
Network Boundary
ResourceService API
Reactive Extensions
API
Service
some
thing
Network Boundary
ResourceService API
Reactive Extensions
API
Service
some
thing
Reactive Extensions
Reactive Extensions
CompletableFuture<T>
represents an already
scheduled task
completes only once
Reactive Extensions
CompletableFuture<T>
represents an already
scheduled task
completes only once
Reactive Extensions
CompletableFuture<T>
represents an already
scheduled task
completes only once
Mono<T>
represents a building plan
(caller has to subscribe)
emits at most one value
Reactive Extensions
CompletableFuture<T>
represents an already
scheduled task
completes only once
Flux<T>
represents a building plan
(caller has to subscribe)
emits many values
Mono<T>
represents a building plan
(caller has to subscribe)
emits at most one value
Reactive Extensions
CompletableFuture<T>
represents an already
scheduled task
completes only once
Flux<T>
represents a building plan
(caller has to subscribe)
emits many values
Mono<T>
represents a building plan
(caller has to subscribe)
emits at most one value
Reactive Extensions
CompletableFuture<T>
represents an already
scheduled task
completes only once
Flux<T>
represents a building plan
(caller has to subscribe)
emits many values
Mono<T>
represents a building plan
(caller has to subscribe)
emits at most one value
subscribe { , }✘
Reactive Extensions
CompletableFuture<T>
represents an already
scheduled task
completes only once
Flux<T>
represents a building plan
(caller has to subscribe)
emits many values
Mono<T>
represents a building plan
(caller has to subscribe)
emits at most one value
subscribe { , }✘
Reactive Extensions
CompletableFuture<T>
represents an already
scheduled task
completes only once
Flux<T>
represents a building plan
(caller has to subscribe)
emits many values
Mono<T>
represents a building plan
(caller has to subscribe)
emits at most one value
subscribe { , }✘
Reactive Extensions
CompletableFuture<T>
represents an already
scheduled task
completes only once
Flux<T>
represents a building plan
(caller has to subscribe)
emits many values
Mono<T>
represents a building plan
(caller has to subscribe)
emits at most one value
subscribe { , }✘
Reactive Extensions
CompletableFuture<T>
represents an already
scheduled task
completes only once
Flux<T>
represents a building plan
(caller has to subscribe)
emits many values
Mono<T>
represents a building plan
(caller has to subscribe)
emits at most one value
subscribe { , }✘
Reactive Extensions
CompletableFuture<T>
represents an already
scheduled task
completes only once
Flux<T>
represents a building plan
(caller has to subscribe)
emits many values
Mono<T>
represents a building plan
(caller has to subscribe)
emits at most one value
subscribe { , }✘
Reactive Extensions
CompletableFuture<T>
represents an already
scheduled task
completes only once
Flux<T>
represents a building plan
(caller has to subscribe)
emits many values
Mono<T>
represents a building plan
(caller has to subscribe)
emits at most one value
subscribe { , }✘
Reactive Extensions
CompletableFuture<T>
represents an already
scheduled task
completes only once
Flux<T>
represents a building plan
(caller has to subscribe)
emits many values
Mono<T>
represents a building plan
(caller has to subscribe)
emits at most one value
subscribe { , }✘
Reactive Extensions
Publisher
Reactive Extensions
fromArray { }
[ , , ]
Operator
Publisher
Reactive Extensions
map { }
fromArray { }
[ , , ]
Operator
Subscriber
Publisher
Reactive Extensions
map { }
subscribe { }
fromArray { }
[ , , ]
Operator
Subscriber
Publisher
Reactive Extensions
map { }
subscribe { }
fromArray { }
[ , , ]
wraps
Operator
Subscriber
Publisher
Reactive Extensions
map { }
subscribe { }
fromArray { }
[ , , ]
wraps
Operator
Subscriber
Publisher
Reactive Extensions
map { }
subscribe { }
fromArray { }
[ , , ]
wraps
subscribes to
Reactive Extensions
Reactive Extensions
Circle[] circles = { Circle.of(BLUE), ... };
Reactive Extensions
Circle[] circles = { Circle.of(BLUE), ... };
Flux.fromArray(circles)
Reactive Extensions
Circle[] circles = { Circle.of(BLUE), ... };
Flux.fromArray(circles)
.map(circle -> Square.of(circle.getColor()))
Reactive Extensions
Circle[] circles = { Circle.of(BLUE), ... };
Flux.fromArray(circles)
.map(circle -> Square.of(circle.getColor()))
.log()
Reactive Extensions
Circle[] circles = { Circle.of(BLUE), ... };
Flux.fromArray(circles)
.map(circle -> Square.of(circle.getColor()))
.subscribe(...);
.log()
Reactive Extensions
Circle[] circles = { Circle.of(BLUE), ... };
Flux.fromArray(circles)
.map(circle -> Square.of(circle.getColor()))
.subscribe(...);
.log()
[main] INFO - | onSubscribe()
Reactive Extensions
Circle[] circles = { Circle.of(BLUE), ... };
Flux.fromArray(circles)
.map(circle -> Square.of(circle.getColor()))
.subscribe(...);
.log()
[main] INFO - | onSubscribe()
[main] INFO - | ?
Reactive Extensions
Circle[] circles = { Circle.of(BLUE), ... };
Flux.fromArray(circles)
.map(circle -> Square.of(circle.getColor()))
.subscribe(...);
.log()
[main] INFO - | onSubscribe()
[main] INFO - | ?
[main] INFO - | onNext(Square(BLUE))
Reactive Extensions
Circle[] circles = { Circle.of(BLUE), ... };
Flux.fromArray(circles)
.map(circle -> Square.of(circle.getColor()))
.subscribe(...);
.log()
[main] INFO - | onSubscribe()
[main] INFO - | ?
[main] INFO - | onNext(Square(BLUE))
[main] INFO - | onNext(Square(YELLOW))
Reactive Extensions
Circle[] circles = { Circle.of(BLUE), ... };
Flux.fromArray(circles)
.map(circle -> Square.of(circle.getColor()))
.subscribe(...);
.log()
[main] INFO - | onSubscribe()
[main] INFO - | ?
[main] INFO - | onNext(Square(BLUE))
[main] INFO - | onNext(Square(YELLOW))
[main] INFO - | onNext(Square(GREEN))
Reactive Extensions
Circle[] circles = { Circle.of(BLUE), ... };
Flux.fromArray(circles)
.map(circle -> Square.of(circle.getColor()))
.subscribe(...);
.log()
[main] INFO - | onSubscribe()
[main] INFO - | ?
[main] INFO - | onNext(Square(BLUE))
[main] INFO - | onNext(Square(YELLOW))
[main] INFO - | onNext(Square(GREEN))
[main] INFO - | onComplete()
Reactive Streams in the Web
Concurrency
Concurrent
Remote Calls
Event-Driven
Systems
Distributed
Systems
Reactive
Systems
Reactive
Manifesto
Reactive
Streams
Reactive
Extensions
Marble
Diagrams
RxJava
Reactor
Akka Streams
Backpressure
Declarative
Programming
Higher-Order
Functions
Concurrency Calling Thread
map { }
Concurrency
subscribe { }
Calling Thread
map { }
Concurrency
subscribe { }
Calling Thread
map { }
Concurrency
subscribe { }
Calling Thread
map { }
Concurrency
subscribe { }
Calling Thread
map { }
Concurrency
subscribe { }
Calling Thread
map { }
Concurrency
subscribe { }
Calling Thread
map { }
Concurrency
subscribe { }
Calling Thread
map { }
Concurrency Calling Thread
map { }
Concurrency Calling Thread Scheduler
subscribeOn { }
map { }
Concurrency
subscribe { }
Calling Thread Scheduler
subscribeOn { }
map { }
Concurrency
subscribe { }
Calling Thread Scheduler
subscribeOn { }
map { }
Concurrency
subscribe { }
Calling Thread Scheduler
subscribeOn { }
map { }
Concurrency
subscribe { }
Calling Thread Scheduler
subscribeOn { }
map { }
Concurrency
subscribe { }
Calling Thread Scheduler
subscribeOn { }
map { }
Concurrency
subscribe { }
Calling Thread Scheduler
subscribeOn { }
map { }
Concurrency
subscribe { }
Calling Thread Scheduler
subscribeOn { }
map { }
Concurrency Calling Thread
flatMap { }
Concurrency Calling Thread Scheduler
flatMap { }
subscribeOn { }
Concurrency
subscribe { }
Calling Thread Scheduler
flatMap { }
subscribeOn { }
Concurrency
subscribe { }
Calling Thread Scheduler
flatMap { }
subscribeOn { }
Concurrency
subscribe { }
Calling Thread Scheduler
flatMap { }
subscribeOn { }
Concurrency
subscribe { }
Calling Thread Scheduler
flatMap { }
subscribeOn { }
Concurrency
subscribe { }
Calling Thread Scheduler
flatMap { }
subscribeOn { }
Concurrency
subscribe { }
Calling Thread Scheduler
flatMap { }
subscribeOn { }
Concurrency
subscribe { }
Calling Thread Scheduler
flatMap { }
subscribeOn { }
Concurrency
Concurrency
API
Service
some
thing
Concurrency
API
Service
some
thing
Scheduler scheduler =
Schedulers.newParallel("pool", 8);
Concurrency
Flux.fromIterable(ids)
API
Service
some
thing
Scheduler scheduler =
Schedulers.newParallel("pool", 8);
Concurrency
Flux.fromIterable(ids)
API
Service
some
thing
Scheduler scheduler =
Schedulers.newParallel("pool", 8);
.flatMap(id -> Mono.just(id)
.map(somethings::loadById)
.subscribeOn(scheduler))
Concurrency
Flux.fromIterable(ids)
API
Service
some
thing
Scheduler scheduler =
Schedulers.newParallel("pool", 8);
.flatMap(id -> Mono.just(id)
.map(somethings::loadById)
.subscribeOn(scheduler))
.log()
Concurrency
Flux.fromIterable(ids)
API
Service
some
thing
Scheduler scheduler =
Schedulers.newParallel("pool", 8);
.flatMap(id -> Mono.just(id)
.map(somethings::loadById)
.subscribeOn(scheduler))
.log()
.subscribe();
Concurrency
Flux.fromIterable(ids)
API
Service
some
thing
Scheduler scheduler =
Schedulers.newParallel("pool", 8);
[main] INFO - onSubscribe()
.flatMap(id -> Mono.just(id)
.map(somethings::loadById)
.subscribeOn(scheduler))
.log()
.subscribe();
Concurrency
Flux.fromIterable(ids)
API
Service
some
thing
Scheduler scheduler =
Schedulers.newParallel("pool", 8);
[main] INFO - onSubscribe()
.flatMap(id -> Mono.just(id)
.map(somethings::loadById)
.subscribeOn(scheduler))
.log()
.subscribe();
[main] INFO - ?
Concurrency
Flux.fromIterable(ids)
API
Service
some
thing
Scheduler scheduler =
Schedulers.newParallel("pool", 8);
[main] INFO - onSubscribe()
.flatMap(id -> Mono.just(id)
.map(somethings::loadById)
.subscribeOn(scheduler))
.log()
.subscribe();
[main] INFO - ?
[pool-1] INFO - onNext(Something(id=1))
Concurrency
Flux.fromIterable(ids)
API
Service
some
thing
Scheduler scheduler =
Schedulers.newParallel("pool", 8);
[main] INFO - onSubscribe()
.flatMap(id -> Mono.just(id)
.map(somethings::loadById)
.subscribeOn(scheduler))
.log()
.subscribe();
[main] INFO - ?
[pool-1] INFO - onNext(Something(id=1))
[pool-2] INFO - onNext(Something(id=2))
Concurrency
Flux.fromIterable(ids)
API
Service
some
thing
Scheduler scheduler =
Schedulers.newParallel("pool", 8);
[main] INFO - onSubscribe()
.flatMap(id -> Mono.just(id)
.map(somethings::loadById)
.subscribeOn(scheduler))
.log()
.subscribe();
[main] INFO - ?
[pool-1] INFO - onNext(Something(id=1))
[pool-2] INFO - onNext(Something(id=2))
[pool-3] INFO - onNext(Something(id=3))
Concurrency
Flux.fromIterable(ids)
API
Service
some
thing
Scheduler scheduler =
Schedulers.newParallel("pool", 8);
[main] INFO - onSubscribe()
.flatMap(id -> Mono.just(id)
.map(somethings::loadById)
.subscribeOn(scheduler))
.log()
.subscribe();
[main] INFO - ?
[pool-1] INFO - onNext(Something(id=1))
[pool-2] INFO - onNext(Something(id=2))
[pool-3] INFO - onNext(Something(id=3))
[pool-3] INFO - onComplete()
Concurrent
Remote Calls
Event-Driven
Systems
Distributed
Systems
Reactive
Systems
Reactive
Manifesto
Reactive
Streams
Reactive
Extensions
Marble
Diagrams
RxJava
Reactor
Akka Streams
Backpressure
Declarative
Programming
Higher-Order
Functions
Backpressure
Backpressure
Publisher
Backpressure
Publisher Subscriber
Backpressure
Publisher Subscriber
subscribe
Backpressure
Publisher Subscriber
Backpressure
Publisher Subscriber
1onNext( )
Backpressure
Publisher Subscriber
onNext( )2
Backpressure
Publisher Subscriber
onNext( )3
Backpressure
Publisher Subscriber
onNext( )4
Backpressure
Publisher Subscriber
onNext( )5
5
Backpressure
Publisher Subscriber
onNext( )
5
Backpressure
Publisher Subscriber
onNext( )
ExecutorService executorService =
new ThreadPoolExecutor(
nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(capacity)
);
5
Backpressure
Publisher Subscriber
onNext( )
ExecutorService executorService =
new ThreadPoolExecutor(
nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(capacity)
);
ids.stream()
.map(id -> executorService
.submit(() -> somethings.loadById(id)))
.collect(toList())
.stream()
.map(waitForSomething())
.forEach(handleSomething());
5
Backpressure
Publisher Subscriber
onNext( )
ExecutorService executorService =
new ThreadPoolExecutor(
nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(capacity)
);
ids.stream()
.map(id -> executorService
.submit(() -> somethings.loadById(id)))
.collect(toList())
.stream()
.map(waitForSomething())
.forEach(handleSomething());
java.util.concurrent.RejectedExecutionException:
Task rejected from ThreadPoolExecutor [
Running, pool size = 2, active threads = 2,
queued tasks = 4, completed tasks = 0
]
Backpressure
Backpressure
Publisher
Backpressure
Publisher Subscriber
Backpressure
Publisher Subscriber
subscribe
Backpressure
Publisher Subscriber
Backpressure
Publisher Subscriber
request(3)
Backpressure
Publisher Subscriber
Backpressure
Publisher Subscriber
1onNext( )
Backpressure
Publisher Subscriber
onNext( )2
Backpressure
Publisher Subscriber
onNext( )3
Backpressure
Publisher Subscriber
Backpressure
Publisher Subscriber
request(2)
Backpressure
Publisher Subscriber
Backpressure
Publisher Subscriber
onNext( )4
Backpressure
Publisher Subscriber
onNext( )5
5
Backpressure
Publisher Subscriber
onNext( )
5
Backpressure
Publisher Subscriber
onNext( )
ExecutorService executorService =
new ThreadPoolExecutor(
nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(capacity)
);
5
Backpressure
Publisher Subscriber
onNext( )
ExecutorService executorService =
new ThreadPoolExecutor(
nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(capacity)
);
Scheduler scheduler = Schedulers
.fromExecutorService(executorService);
5
Backpressure
Publisher Subscriber
onNext( )
ExecutorService executorService =
new ThreadPoolExecutor(
nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(capacity)
);
Flux.fromIterable(ids)
.log()
.flatMap(id -> Mono.just(id)
.map(somethings::loadById)
.subscribeOn(scheduler), 3)
.doOnNext(handleSomething())
.subscribe();
Scheduler scheduler = Schedulers
.fromExecutorService(executorService);
5
Backpressure
Publisher Subscriber
onNext( )
ExecutorService executorService =
new ThreadPoolExecutor(
nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(capacity)
);
Flux.fromIterable(ids)
.log()
.flatMap(id -> Mono.just(id)
.map(somethings::loadById)
.subscribeOn(scheduler), 3)
.doOnNext(handleSomething())
.subscribe();
Scheduler scheduler = Schedulers
.fromExecutorService(executorService);
[main] INFO - | onSubscribe()
[main] INFO - | ?
5
Backpressure
Publisher Subscriber
onNext( )
ExecutorService executorService =
new ThreadPoolExecutor(
nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(capacity)
);
Flux.fromIterable(ids)
.log()
.flatMap(id -> Mono.just(id)
.map(somethings::loadById)
.subscribeOn(scheduler), 3)
.doOnNext(handleSomething())
.subscribe();
Scheduler scheduler = Schedulers
.fromExecutorService(executorService);
[main] INFO - | onSubscribe()
[main] INFO - | ?
5
Backpressure
Publisher Subscriber
onNext( )
ExecutorService executorService =
new ThreadPoolExecutor(
nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(capacity)
);
Flux.fromIterable(ids)
.log()
.flatMap(id -> Mono.just(id)
.map(somethings::loadById)
.subscribeOn(scheduler), 3)
.doOnNext(handleSomething())
.subscribe();
Scheduler scheduler = Schedulers
.fromExecutorService(executorService);
[main] INFO - | onSubscribe()
[main] INFO - | request(3)
5
Backpressure
Publisher Subscriber
onNext( )
ExecutorService executorService =
new ThreadPoolExecutor(
nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(capacity)
);
Flux.fromIterable(ids)
.log()
.flatMap(id -> Mono.just(id)
.map(somethings::loadById)
.subscribeOn(scheduler), 3)
.doOnNext(handleSomething())
.subscribe();
Scheduler scheduler = Schedulers
.fromExecutorService(executorService);
[main] INFO - | onSubscribe()
[main] INFO - | request(3)
5
Backpressure
Publisher Subscriber
onNext( )
ExecutorService executorService =
new ThreadPoolExecutor(
nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(capacity)
);
Flux.fromIterable(ids)
.log()
.flatMap(id -> Mono.just(id)
.map(somethings::loadById)
.subscribeOn(scheduler), 3)
.doOnNext(handleSomething())
.subscribe();
Scheduler scheduler = Schedulers
.fromExecutorService(executorService);
[main] INFO - | onSubscribe()
[main] INFO - | request(3)
[main] INFO - | onNext(1)
[main] INFO - | onNext(2)
[main] INFO - | onNext(3)
5
Backpressure
Publisher Subscriber
onNext( )
ExecutorService executorService =
new ThreadPoolExecutor(
nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(capacity)
);
Flux.fromIterable(ids)
.log()
.flatMap(id -> Mono.just(id)
.map(somethings::loadById)
.subscribeOn(scheduler), 3)
.doOnNext(handleSomething())
.subscribe();
Scheduler scheduler = Schedulers
.fromExecutorService(executorService);
[main] INFO - | onSubscribe()
[main] INFO - | request(3)
[main] INFO - | onNext(1)
[main] INFO - | onNext(2)
[main] INFO - | onNext(3)
[pool-1] INFO - | request(1)
5
Backpressure
Publisher Subscriber
onNext( )
ExecutorService executorService =
new ThreadPoolExecutor(
nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(capacity)
);
Flux.fromIterable(ids)
.log()
.flatMap(id -> Mono.just(id)
.map(somethings::loadById)
.subscribeOn(scheduler), 3)
.doOnNext(handleSomething())
.subscribe();
Scheduler scheduler = Schedulers
.fromExecutorService(executorService);
[main] INFO - | onSubscribe()
[main] INFO - | request(3)
[main] INFO - | onNext(1)
[main] INFO - | onNext(2)
[main] INFO - | onNext(3)
[pool-1] INFO - | request(1)
[pool-1] INFO - | onNext(4)
5
Backpressure
Publisher Subscriber
onNext( )
ExecutorService executorService =
new ThreadPoolExecutor(
nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(capacity)
);
Flux.fromIterable(ids)
.log()
.flatMap(id -> Mono.just(id)
.map(somethings::loadById)
.subscribeOn(scheduler), 3)
.doOnNext(handleSomething())
.subscribe();
Scheduler scheduler = Schedulers
.fromExecutorService(executorService);
[main] INFO - | onSubscribe()
[main] INFO - | request(3)
[main] INFO - | onNext(1)
[main] INFO - | onNext(2)
[main] INFO - | onNext(3)
[pool-1] INFO - | request(1)
[pool-1] INFO - | onNext(4)
[pool-2] INFO - | request(1)
5
Backpressure
Publisher Subscriber
onNext( )
ExecutorService executorService =
new ThreadPoolExecutor(
nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(capacity)
);
Flux.fromIterable(ids)
.log()
.flatMap(id -> Mono.just(id)
.map(somethings::loadById)
.subscribeOn(scheduler), 3)
.doOnNext(handleSomething())
.subscribe();
Scheduler scheduler = Schedulers
.fromExecutorService(executorService);
[main] INFO - | onSubscribe()
[main] INFO - | request(3)
[main] INFO - | onNext(1)
[main] INFO - | onNext(2)
[main] INFO - | onNext(3)
[pool-1] INFO - | request(1)
[pool-1] INFO - | onNext(4)
[pool-2] INFO - | request(1)
[pool-2] INFO - | onNext(5)
5
Backpressure
Publisher Subscriber
onNext( )
ExecutorService executorService =
new ThreadPoolExecutor(
nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(capacity)
);
Flux.fromIterable(ids)
.log()
.flatMap(id -> Mono.just(id)
.map(somethings::loadById)
.subscribeOn(scheduler), 3)
.doOnNext(handleSomething())
.subscribe();
Scheduler scheduler = Schedulers
.fromExecutorService(executorService);
[main] INFO - | onSubscribe()
[main] INFO - | request(3)
[main] INFO - | onNext(1)
[main] INFO - | onNext(2)
[main] INFO - | onNext(3)
[pool-1] INFO - | request(1)
[pool-1] INFO - | onNext(4)
[pool-2] INFO - | request(1)
[pool-2] INFO - | onNext(5)
[pool-2] INFO - | onComplete()
Backpressure
HTTP
Service A
Database
Topic Service B Topic
BFF
Backpressure
HTTP
Service A
Database
Topic Service B Topic
BFF
HTTP
Service A
Database
Topic Service B Topic
BFF
Reactive Streams in the Web
Progressive HTML Rendering
Progressive HTML RenderingProgressive
Progressive HTML Rendering
BFF
Widget Widget
Widget
Widget Widget
Concurrent Remote Calls
Progressive HTML Rendering
Transfer-Encoding: chunked
BFF
Widget Widget
Widget
Widget Widget
Concurrent Remote Calls
Progressive HTML Rendering
Progressive HTML Rendering
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Progressive HTML Rendering
<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>
Progressive HTML Rendering
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Progressive HTML Rendering
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@RestController
@GetMapping(value = "hello")
public Flux<String> sayHello() {
return Flux.interval(Duration.ofMillis(500)).map(tick -> "Hellon");
}
Progressive HTML Rendering
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
curl -i -N localhost:8080/hello
@RestController
@GetMapping(value = "hello")
public Flux<String> sayHello() {
return Flux.interval(Duration.ofMillis(500)).map(tick -> "Hellon");
}
Progressive HTML Rendering
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
curl -i -N localhost:8080/hello
@RestController
@GetMapping(value = "hello")
public Flux<String> sayHello() {
return Flux.interval(Duration.ofMillis(500)).map(tick -> "Hellon");
}
HTTP/1.1 200
Content-Type: text/plain
Transfer-Encoding: chunked
Progressive HTML Rendering
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
curl -i -N localhost:8080/hello
@RestController
@GetMapping(value = "hello")
public Flux<String> sayHello() {
return Flux.interval(Duration.ofMillis(500)).map(tick -> "Hellon");
}
HTTP/1.1 200
Content-Type: text/plain
Transfer-Encoding: chunked
Hello
Progressive HTML Rendering
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
curl -i -N localhost:8080/hello
@RestController
@GetMapping(value = "hello")
public Flux<String> sayHello() {
return Flux.interval(Duration.ofMillis(500)).map(tick -> "Hellon");
}
HTTP/1.1 200
Content-Type: text/plain
Transfer-Encoding: chunked
Hello
Hello
Progressive HTML Rendering
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
curl -i -N localhost:8080/hello
@RestController
@GetMapping(value = "hello")
public Flux<String> sayHello() {
return Flux.interval(Duration.ofMillis(500)).map(tick -> "Hellon");
}
HTTP/1.1 200
Content-Type: text/plain
Transfer-Encoding: chunked
Hello
Hello
Hello
Progressive HTML Rendering
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
curl -i -N localhost:8080/hello
@RestController
@GetMapping(value = "hello")
public Flux<String> sayHello() {
return Flux.interval(Duration.ofMillis(500)).map(tick -> "Hellon");
}
HTTP/1.1 200
Content-Type: text/plain
Transfer-Encoding: chunked
Hello
Hello
Hello
Hello
Reactive Streams in the Web
Baking an HTML stream
Spring 5 WebClient
Spring 5 WebClient
WidgetService
Spring 5 WebClient
WidgetService WebClient
Spring 5 WebClient
WidgetServerWidgetService WebClient
Spring 5 WebClient Network Boundary
WidgetServerWidgetService WebClient
Spring 5 WebClient Network Boundary
WidgetServerWidgetService WebClient
Spring 5 WebClient Network Boundary
WidgetServerWidgetService WebClient
WebClient.create()
Spring 5 WebClient Network Boundary
WidgetServerWidgetService WebClient
WebClient.create()
.get()
Spring 5 WebClient Network Boundary
WidgetServerWidgetService WebClient
WebClient.create()
.get()
.uri("http://widget-server")
Spring 5 WebClient Network Boundary
WidgetServerWidgetService WebClient
WebClient.create()
.get()
.uri("http://widget-server")
.retrieve()
Spring 5 WebClient Network Boundary
WidgetServerWidgetService WebClient
WebClient.create()
.get()
.uri("http://widget-server")
.retrieve()
.bodyToFlux(String.class)
Spring 5 WebClient Network Boundary
WidgetServerWidgetService WebClient
WebClient.create()
.get()
.uri("http://widget-server")
.retrieve()
.bodyToFlux(String.class)
.subscribe(this::onNext);
Spring 5 WebClient Network Boundary
WidgetServerWidgetService WebClient
WebClient.create()
.get()
.uri("http://widget-server")
.retrieve()
.bodyToFlux(String.class)
.subscribe(this::onNext);
Spring 5 WebClient Network Boundary
WidgetServerWidgetService WebClient
WebClient.create()
.get()
.uri("http://widget-server")
.retrieve()
.bodyToFlux(String.class)
.subscribe(this::onNext);
Spring 5 WebClient Network Boundary
WidgetServerWidgetService WebClient
WebClient.create()
.get()
.uri("http://widget-server")
.retrieve()
.bodyToFlux(String.class)
.subscribe(this::onNext);
Spring 5 WebClient Network Boundary
WidgetServerWidgetService WebClient
WebClient.create()
.get()
.uri("http://widget-server")
.retrieve()
.bodyToFlux(String.class)
.subscribe(this::onNext);
HTML Rendering
HTML Rendering
HTTP Request
HTML Rendering
HTTP Request
Accepter Thread
HTML Rendering
HTTP Request
Accepter Thread Connection Queue
HTML Rendering
HTTP Request
Accepter Thread Connection Queue Request Thread
HTML Rendering
HTTP Request
Accepter Thread Connection Queue Request Thread
Model
HTML Rendering
HTTP Request
Accepter Thread Connection Queue Request Thread
Model
TemplateHttpResponse.getWriter()
Output Buffer
HTML Rendering
HTTP Request
Accepter Thread
HTTP Response
Connection Queue Request Thread
Model
TemplateHttpResponse.getWriter()
Output Buffer
Model
Reactive-Friendly Template Engine
Template
Model
Reactive-Friendly Template Engine
Flux<T>
Template
th:each="..."
Model
Reactive-Friendly Template Engine
Flux<T>
Template
th:each="..."
Model
Reactive-Friendly Template Engine
Flux<T>
Template
th:each="..."
Reactive
Web Server
Model
Reactive-Friendly Template Engine
Flux<T>
Template
th:each="..."
Reactive
Web Server
subscribes
Model
Reactive-Friendly Template Engine
Flux<T>
Template
th:each="..."
requests
DataBuffer
Reactive
Web Server
subscribes
Processor<T, DataBuffer>
Model
Reactive-Friendly Template Engine
Flux<T>
Template
th:each="..."
requests
DataBuffer
Reactive
Web Server
subscribes
Processor<T, DataBuffer>
Model
Reactive-Friendly Template Engine
Flux<T>
Template
th:each="..."
requests
DataBuffer
Reactive
Web Server
subscribes
HTML rendered as values are published
Progressive HTML Rendering
Progressive HTML Rendering
Processor
Subscriber
Publisher
Progressive HTML Rendering
Processor
Subscriber
Publisher Spring 5 WebClient
Progressive HTML Rendering
Processor
Subscriber
Publisher Spring 5 WebClient
Reactive-Friendly Thymeleaf View
Progressive HTML Rendering
Processor
Subscriber
Publisher Spring 5 WebClient
Reactive-Friendly Thymeleaf View
Reactive Web Server
Progressive HTML Rendering
Publisher Spring 5 WebClient
URI uri = ...
String name = ...
Mono<Pagelet> pageletMono = webClient.get().uri(uri).exchange()
.flatMap(res -> res.bodyToMono(String.class))
.map(body -> new Pagelet(name, body));
List<Mono<Pagelet>> pageletMonos = ...;
... = Flux.merge(pageletMonos);
Progressive HTML Rendering
Publisher Spring 5 WebClient
Progressive HTML Rendering
Processor Reactive Thymeleaf View
@GetMapping(value = "/widgets")
public String widgets(Model model) {
List<String> pageletNames = widgetService.getPageletNames();
Flux<Pagelet> pagelets = widgetService.loadPagelets();
model.addAttribute("pageletNames", pageletNames);
model.addAttribute("pagelets", new ReactiveDataDriverContextVariable(pagelets, 1));
return "widgets";
}
Progressive HTML Rendering
Processor Reactive Thymeleaf View
@GetMapping(value = "/widgets")
public String widgets(Model model) {
List<String> pageletNames = widgetService.getPageletNames();
Flux<Pagelet> pagelets = widgetService.loadPagelets();
model.addAttribute("pageletNames", pageletNames);
model.addAttribute("pagelets", new ReactiveDataDriverContextVariable(pagelets, 1));
return "widgets";
}
Progressive HTML Rendering
Processor Reactive Thymeleaf View
ReactiveDataDriverContextVariable
<article th:each="pagelet: ${pagelets}" class="...">
<section class="...">
<div class="...">
<div th:utext="${pagelet.content}" class="..."></div>
</div>
</section>
</article>
Progressive HTML Rendering
Processor Reactive Thymeleaf View
<article th:each="pagelet: ${pagelets}" class="...">
<section class="...">
<div class="...">
<div th:utext="${pagelet.content}" class="..."></div>
</div>
</section>
</article>
Progressive HTML Rendering
Processor Reactive Thymeleaf View
List<Mono<Pagelet>> pageletMonos = ...;
... = Flux.merge(pageletMonos);
<article th:each="pagelet: ${pagelets}" class="...">
<section class="...">
<div class="...">
<div th:utext="${pagelet.content}" class="..."></div>
</div>
</section>
</article>
Progressive HTML Rendering
Processor Reactive Thymeleaf View
List<Mono<Pagelet>> pageletMonos = ...;
... = Flux.merge(pageletMonos);Flux.merge(pageletMonos)
<article th:each="pageletName: ${pageletNames}" class="...">
<section class="...">
<div th:id="${pageletName}" class="...">
<div class="...">...</div>
</div>
</section>
</article>
Progressive HTML Rendering
Processor Reactive Thymeleaf View
<script th-inline="javascript" th:each="pagelet : ${pagelets}">
(function() {
var element = document.getElementById("[[${pagelet.name}]]");
if(element) {
element.innerHTML = "[(${pagelet.content})]";
}
})();
</script>
Progressive HTML Rendering
Processor Reactive Thymeleaf View
Reactive Streams in the Web
Does it work?
Progressive HTML Rendering
Progressive HTML Rendering
Progressive HTML Rendering
Progressive HTML Rendering
Progressive HTML Rendering
What‘s more?
§ Cold Streams vs. Hot Streams
§ Reactive Database Drivers
§ Backpressure Strategies
§ Threaded vs. Evented Server Model
§ WebSockets and Server-Sent Events
Thank you!
Questions?

More Related Content

PDF
ISTA 2019 - Migrating data-intensive microservices from Python to Go
Nikolay Stoitsev
 
PDF
Designing for Distributed Systems with Reactor and Reactive Streams
Stéphane Maldini
 
ODP
Intro to Muon - How to build Polyglot Message and Event Microservices
David Dawson
 
PPTX
Real-World Pulsar Architectural Patterns
Devin Bost
 
PPTX
Vertica the convertro way
Zvika Gutkin
 
PPTX
Scylla Summit 2017: Managing 10,000 Node Storage Clusters at Twitter
ScyllaDB
 
PDF
Buildinga billionuserloadbalancer may2015-sre-con15europe-shuff
Patrick Shuff
 
ISTA 2019 - Migrating data-intensive microservices from Python to Go
Nikolay Stoitsev
 
Designing for Distributed Systems with Reactor and Reactive Streams
Stéphane Maldini
 
Intro to Muon - How to build Polyglot Message and Event Microservices
David Dawson
 
Real-World Pulsar Architectural Patterns
Devin Bost
 
Vertica the convertro way
Zvika Gutkin
 
Scylla Summit 2017: Managing 10,000 Node Storage Clusters at Twitter
ScyllaDB
 
Buildinga billionuserloadbalancer may2015-sre-con15europe-shuff
Patrick Shuff
 

Similar to Reactive Streams in the Web (20)

PPT
Microsoft Active Directory Fundament.ppt
anishmohd123
 
PDF
HTMX: Web 1.0 with the benefits of Web 2.0 without the grift of Web 3.0
Martijn Dashorst
 
PDF
HBaseCon 2012 | Storing and Manipulating Graphs in HBase
Cloudera, Inc.
 
PDF
Storing and manipulating graphs in HBase
Dan Lynn
 
PDF
Using Websockets with Play!
Andrew Conner
 
KEY
CakePHP - The Path to 2.0
Graham Weldon
 
PPTX
Interactive Realtime Dashboards on Data Streams using Kafka, Druid and Superset
Hortonworks
 
PDF
Devoxx London 2017 - Rethinking Services With Stateful Streams
Ben Stopford
 
PDF
DSR microservices
Steve Upton
 
PPTX
Real world functional reactive programming
Eric Polerecky
 
PDF
Software Architecture Anti-Patterns
Eduards Sizovs
 
PDF
Free The Enterprise With Ruby & Master Your Own Domain
Ken Collins
 
PDF
iSAQB Software Architecture Gathering 2023: How Process Orchestration Increas...
Bernd Ruecker
 
PDF
Jeff Lindsay: Building Public Infrastructure with Autosustainable Services
it-people
 
PPTX
Exactly-Once Made Easy: Transactional Messaging in Apache Pulsar - Pulsar Sum...
StreamNative
 
PDF
A Microservices Journey - Susanne Kaiser
Thoughtworks
 
PPT
KaTe RESTful adapter for SAP Process Integration: Introduction
Kate_RESTful
 
PDF
The API (R) Evolution
Rakuten Group, Inc.
 
PDF
F# and SignalR for a FastWeb
Riccardo Terrell
 
PDF
The Ember.js Framework - Everything You Need To Know
All Things Open
 
Microsoft Active Directory Fundament.ppt
anishmohd123
 
HTMX: Web 1.0 with the benefits of Web 2.0 without the grift of Web 3.0
Martijn Dashorst
 
HBaseCon 2012 | Storing and Manipulating Graphs in HBase
Cloudera, Inc.
 
Storing and manipulating graphs in HBase
Dan Lynn
 
Using Websockets with Play!
Andrew Conner
 
CakePHP - The Path to 2.0
Graham Weldon
 
Interactive Realtime Dashboards on Data Streams using Kafka, Druid and Superset
Hortonworks
 
Devoxx London 2017 - Rethinking Services With Stateful Streams
Ben Stopford
 
DSR microservices
Steve Upton
 
Real world functional reactive programming
Eric Polerecky
 
Software Architecture Anti-Patterns
Eduards Sizovs
 
Free The Enterprise With Ruby & Master Your Own Domain
Ken Collins
 
iSAQB Software Architecture Gathering 2023: How Process Orchestration Increas...
Bernd Ruecker
 
Jeff Lindsay: Building Public Infrastructure with Autosustainable Services
it-people
 
Exactly-Once Made Easy: Transactional Messaging in Apache Pulsar - Pulsar Sum...
StreamNative
 
A Microservices Journey - Susanne Kaiser
Thoughtworks
 
KaTe RESTful adapter for SAP Process Integration: Introduction
Kate_RESTful
 
The API (R) Evolution
Rakuten Group, Inc.
 
F# and SignalR for a FastWeb
Riccardo Terrell
 
The Ember.js Framework - Everything You Need To Know
All Things Open
 
Ad

Recently uploaded (20)

PDF
Research-Fundamentals-and-Topic-Development.pdf
ayesha butalia
 
PDF
Automating ArcGIS Content Discovery with FME: A Real World Use Case
Safe Software
 
PDF
The Evolution of KM Roles (Presented at Knowledge Summit Dublin 2025)
Enterprise Knowledge
 
PPTX
What-is-the-World-Wide-Web -- Introduction
tonifi9488
 
PDF
Make GenAI investments go further with the Dell AI Factory
Principled Technologies
 
PDF
Cloud-Migration-Best-Practices-A-Practical-Guide-to-AWS-Azure-and-Google-Clou...
Artjoker Software Development Company
 
PPTX
Applied-Statistics-Mastering-Data-Driven-Decisions.pptx
parmaryashparmaryash
 
PDF
MASTERDECK GRAPHSUMMIT SYDNEY (Public).pdf
Neo4j
 
PDF
Software Development Methodologies in 2025
KodekX
 
PDF
CIFDAQ's Market Wrap : Bears Back in Control?
CIFDAQ
 
PDF
OFFOFFBOX™ – A New Era for African Film | Startup Presentation
ambaicciwalkerbrian
 
PDF
REPORT: Heating appliances market in Poland 2024
SPIUG
 
PDF
The Future of Mobile Is Context-Aware—Are You Ready?
iProgrammer Solutions Private Limited
 
PDF
Economic Impact of Data Centres to the Malaysian Economy
flintglobalapac
 
PPTX
New ThousandEyes Product Innovations: Cisco Live June 2025
ThousandEyes
 
PDF
Trying to figure out MCP by actually building an app from scratch with open s...
Julien SIMON
 
PDF
SparkLabs Primer on Artificial Intelligence 2025
SparkLabs Group
 
PDF
BLW VOCATIONAL TRAINING SUMMER INTERNSHIP REPORT
codernjn73
 
PDF
Event Presentation Google Cloud Next Extended 2025
minhtrietgect
 
PDF
Get More from Fiori Automation - What’s New, What Works, and What’s Next.pdf
Precisely
 
Research-Fundamentals-and-Topic-Development.pdf
ayesha butalia
 
Automating ArcGIS Content Discovery with FME: A Real World Use Case
Safe Software
 
The Evolution of KM Roles (Presented at Knowledge Summit Dublin 2025)
Enterprise Knowledge
 
What-is-the-World-Wide-Web -- Introduction
tonifi9488
 
Make GenAI investments go further with the Dell AI Factory
Principled Technologies
 
Cloud-Migration-Best-Practices-A-Practical-Guide-to-AWS-Azure-and-Google-Clou...
Artjoker Software Development Company
 
Applied-Statistics-Mastering-Data-Driven-Decisions.pptx
parmaryashparmaryash
 
MASTERDECK GRAPHSUMMIT SYDNEY (Public).pdf
Neo4j
 
Software Development Methodologies in 2025
KodekX
 
CIFDAQ's Market Wrap : Bears Back in Control?
CIFDAQ
 
OFFOFFBOX™ – A New Era for African Film | Startup Presentation
ambaicciwalkerbrian
 
REPORT: Heating appliances market in Poland 2024
SPIUG
 
The Future of Mobile Is Context-Aware—Are You Ready?
iProgrammer Solutions Private Limited
 
Economic Impact of Data Centres to the Malaysian Economy
flintglobalapac
 
New ThousandEyes Product Innovations: Cisco Live June 2025
ThousandEyes
 
Trying to figure out MCP by actually building an app from scratch with open s...
Julien SIMON
 
SparkLabs Primer on Artificial Intelligence 2025
SparkLabs Group
 
BLW VOCATIONAL TRAINING SUMMER INTERNSHIP REPORT
codernjn73
 
Event Presentation Google Cloud Next Extended 2025
minhtrietgect
 
Get More from Fiori Automation - What’s New, What Works, and What’s Next.pdf
Precisely
 
Ad

Reactive Streams in the Web