Class Experiment03_Flow
Flow Experiments
Flow
is Java's default Reactive Streams
framework that addresses the
Producer–Consumer Problem
by restricting how many messages the Producer (Publisher) can send the Consumer (Subscriber), such that
the Producer will block when the Consumer cannot consume any more messages, using a technique called
Back Pressure.
Why Loom
As we have learned before, using Java Parallel Streams with Transactional Tasks is problematic, where Virtual Threads handle Transactional Tasks better than Platform Threads. So, in terms of a classic Producer/Consumer application, where the Producers and/or the Consumers are Transactional, using Virtual Threads can be an effective improvement over regular thread pools.
Why Flow
If Project Loom solves several problems that were solved by Reactive Programming methods, why do we want to continue using Reactive Programming methods such as Reactive Streams?
- Given there is an existing and growing ecosystem of Reactive Solutions, we will have to go with the momentum for a while.
- It is expected that most Reactive Frameworks will refactor their code to use Project Loom capabilities to exploit the obvious advantages, such as support in the JVM.
- Backpressure is a really valuable technique for some applications, and that is not a feature of Project Loom.
Increasingly many APIs are providing both Synchronous and Asynchronous capabilities to better handle the
reality of asynchronous and concurrent programming practices. In some case, an API will return a Future
or CompletableFuture
as a result, and it is up to the API User to best decide how to consume that.
Increasingly, however, many APIs are now returning a Flow.Publisher
, or taking
one as an argument to the API; similarly for Flow.Subscriber
. Such patterns
are often useful when the data sets are large, and/or dynamically generated. For example:
var myPublisher = new MyPublisher(); // produces an HTTP Request Body var mySubscriber = new MySubscriber(); // consumes an HTTP Response Body var request = HttpRequest.newBuilder() .uri(URI.create("https://www.boredapi.com/api/activity")) .POST(myPublisher) .build(); client .sendAsync(request, info -> HttpResponse.BodySubscribers.ofPublisher()) .thenAccept(action -> {action.body().subscribe(mySubscriber);}) .join(); // or CompletableFuture<HttpResponse<Flow.Publisher<List<ByteBuffer>>>> response = client.sendAsync(request, info -> HttpResponse.BodySubscribers.ofPublisher()); response .thenAccept(action -> {action.body().subscribe(mySubscriber);}) .join();
where myPublisher will produce the HTTP Request Body and mySubscriber will consume the HTTP Response Body. Note: I wanted to reveal how complex the result type of response would be. Yes, these are rather elaborate ways to send and receive HTTP bodies, but sometimes these kinds of data transport are non-trivial. Whether it be Request Body Production, or Response Body Consumption, we may want to maximize/optimize throughput and ideally both Flow and Virtual Threads can help.
In the world of HTTP/2 and HTTP/3, these protocols allow for the transport of very large payloads, and even dynamic streaming, where using Reactive Streams such as Flow can better produce, process, and consume such data. But, will Project Loom improve the throughput of these applications? However, we will explore this more in future experiments related to HTTP, because I just wanted to introduce one of the leading rationales for using Flow.
Publisher/Subscriber
However, Flow
is also a
Publish-Subscribe Pattern
-
Constructor Summary
-
Method Summary
-
Constructor Details
-
Experiment03_Flow
public Experiment03_Flow()
-
-
Method Details
-
main
-