Browse Source

Document WebFlux concurrency model

Issue: SPR-16538
pull/1752/merge
Rossen Stoyanchev 7 years ago
parent
commit
19875d8e3f
  1. BIN
      src/docs/asciidoc/images/spring-mvc-and-webflux-venn.png
  2. 159
      src/docs/asciidoc/web/webflux.adoc

BIN
src/docs/asciidoc/images/spring-mvc-and-webflux-venn.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

159
src/docs/asciidoc/web/webflux.adoc

@ -23,7 +23,9 @@ e.g. Spring MVC controllers with the reactive `WebClient`. @@ -23,7 +23,9 @@ e.g. Spring MVC controllers with the reactive `WebClient`.
[[webflux-new-framework]]
=== Why a new web framework?
=== Motivation
Why was Spring WebFlux created?
Part of the answer is the need for a non-blocking web stack to handle concurrency with a
small number of threads and scale with less hardware resources. Servlet 3.1 did provide
@ -44,9 +46,9 @@ WebFlux to offer functional web endpoints alongside with annotated controllers. @@ -44,9 +46,9 @@ WebFlux to offer functional web endpoints alongside with annotated controllers.
[[webflux-why-reactive]]
=== Reactive: what and why?
=== Define "reactive"
We touched on non-blocking and functional but why reactive and what do we mean?
We touched on non-blocking and functional but what does reactive mean?
The term "reactive" refers to programming models that are built around reacting to change --
network component reacting to I/O events, UI controller reacting to mouse events, etc.
@ -131,82 +133,86 @@ annotations and being called back. @@ -131,82 +133,86 @@ annotations and being called back.
[[webflux-framework-choice]]
=== Choosing a web framework
=== Applicability
Spring MVC or WebFlux?
A natural question to ask but one that sets up an unsound dichotomy. It's actually both
working together to expand the range of available options. The two are designed for
continuity and consistency with each other, they are available side by side, and feedback
from each side benefits both sides. The diagram below shows how the two relate, what they
have in common, and what each supports uniquely:
image::images/spring-mvc-and-webflux-venn.png[]
Should you use Spring MVC or WebFlux? Let's cover a few different perspectives.
Below are some specific points to consider:
If you have a Spring MVC application that works fine, there is no need to change.
* If you have a Spring MVC application that works fine, there is no need to change.
Imperative programming is the easiest way to write, understand, and debug code.
You have maximum choice of libraries since historically most are blocking.
If you are already shopping for a non-blocking web stack, Spring WebFlux offers the same
* If you are already shopping for a non-blocking web stack, Spring WebFlux offers the same
execution model benefits as others in this space and also provides a choice of servers --
Netty, Tomcat, Jetty, Undertow, Servlet 3.1+ containers, a choice of programming models --
annotated controllers and functional web endpoints, and a choice of reactive libraries --
Reactor, RxJava, or other.
If you are interested in a lightweight, functional web framework for use with Java 8 lambdas
* If you are interested in a lightweight, functional web framework for use with Java 8 lambdas
or Kotlin then use the Spring WebFlux functional web endpoints. That can also be a good choice
for smaller applications or microservices with less complex requirements that can benefit
from greater transparency and control.
In a microservice architecture you can have a mix of applications with either Spring MVC
* In a microservice architecture you can have a mix of applications with either Spring MVC
or Spring WebFlux controllers, or with Spring WebFlux functional endpoints. Having support
for the same annotation-based programming model in both frameworks makes it easier to
re-use knowledge while also selecting the right tool for the right job.
A simple way to evaluate an application is to check its dependencies. If you have blocking
* A simple way to evaluate an application is to check its dependencies. If you have blocking
persistence APIs (JPA, JDBC), or networking APIs to use, then Spring MVC is the best choice
for common architectures at least. It is technically feasible with both Reactor and
RxJava to perform blocking calls on a separate thread but you wouldn't be making the
most of a non-blocking web stack.
If you have a Spring MVC application with calls to remote services, try the reactive `WebClient`.
* If you have a Spring MVC application with calls to remote services, try the reactive `WebClient`.
You can return reactive types (Reactor, RxJava, <<webflux-reactive-libraries,or other>>)
directly from Spring MVC controller methods. The greater the latency per call, or the
interdependency among calls, the more dramatic the benefits. Spring MVC controllers
can call other reactive components too.
If you have a large team, keep in mind the steep learning curve in the shift to non-blocking,
* If you have a large team, keep in mind the steep learning curve in the shift to non-blocking,
functional, and declarative programming. A practical way to start without a full switch
is to use the reactive `WebClient`. Beyond that start small and measure the benefits.
We expect that for a wide range of applications the shift is unnecessary.
If you are unsure what benefits to look for, start by learning about how non-blocking I/O
works (e.g. concurrency on single-threaded Node.js is not an oxymoron) and its effects.
The tag line is "scale with less hardware" but that effect is not guaranteed, not without
some network I/O that can be slow or unpredictable. This Netflix
https://medium.com/netflix-techblog/zuul-2-the-netflix-journey-to-asynchronous-non-blocking-systems-45947377fb5c[blog post]
is a good resource.
We expect that for a wide range of applications the shift is unnecessary. If you are
unsure what benefits to look for, start by learning about how non-blocking I/O works
(e.g. concurrency on single-threaded Node.js) and its effects.
[[webflux-server-choice]]
=== Choosing a server
=== Servers
Spring WebFlux is supported on Netty, Undertow, Tomcat, Jetty, and Servlet 3.1+ containers.
Each server is adapted to a common Reactive Streams API. The Spring WebFlux programming
models are built on that common API.
Spring WebFlux is supported on Tomcat, Jetty, Servlet 3.1+ containers, as well as on
non-Servlet runtimes such as Netty and Undertow. All servers are adapted to a low-level,
<<webflux-httphandler,common API>> so that higher level
<<webflux-programming-models,programming models>> can be supported across servers.
[NOTE]
====
*Common question: how can Tomcat and Jetty be used in both stacks?* +
Tomcat and Jetty are non-blocking at their core. It's the Servlet API that adds a
blocking facade. Starting in version 3.1 the Servlet API adds a choice for non-blocking I/O.
However its use requires care to avoid other synchronous and blocking parts. For this
reason Spring's reactive web stack has a low-level Servlet adapter to bridge to Reactive
Streams but the Servlet API is otherwise not exposed for direct use.
====
Spring WebFlux does not have built-in support to start or stop a server. However it is
easy to <<webflux-web-handler-api,assemble>> an application from Spring configuration, and
<<webflux-config,WebFlux infrastructure>>, and <<webflux-httphandler,run it>> with a few
lines of code.
Spring Boot 2 uses Netty by default with WebFlux because Netty is more widely used in the
async, non-blocking space and also provides both client and server that can share resources.
By comparison Servlet 3.1 non-blocking I/O hasn't seen much use because the bar to use it
is so high. Spring WebFlux opens one practical path to adoption.
Spring Boot has a WebFlux starter that automates these steps. By default the starter uses
Netty but it is easy to switch to Tomcat, Jetty, or Undertow simply by changing your
Maven or Gradle dependencies. Spring Boot defaults to Netty because it is more widely
used in the async, non-blocking space, and provides a client and a server share resources.
The default server choice in Spring Boot is mainly about the out-of-the-box experience.
Applications can still choose any of the other supported servers which are also highly
optimized for performance, fully non-blocking, and adapted to Reactive Streams back
pressure. In Spring Boot it is trivial to make the switch.
Tomcat and Jetty can be used with both Spring MVC and WebFlux. Keep in mind however that
the way they're used is very differently. Spring MVC relies on Servlet blocking I/O and
allows applications to use the Servlet API directly if they need to. Spring WebFlux
relies on Servlet 3.1 non-blocking I/O and uses the Servlet API behind a low-level
adapter and not exposed for direct use.
For Undertow, Spring WebFlux uses Undertow APIs directly without the Servlet API.
@ -227,6 +233,77 @@ dramatic. @@ -227,6 +233,77 @@ dramatic.
[[webflux-concurrency-model]]
=== Concurrency Model
Both Spring MVC and Spring WebFlux support annotated controllers, but there is a key
difference in the concurrency model and default assumptions for blocking and threads.
In Spring MVC, and servlet applications in general, it is assumed that applications _may
block_ the current thread, e.g. for remote calls, and for this reason servlet containers
use a large thread pool, to absorb potential blocking during request handling.
In Spring WebFlux, and non-blocking servers in general, it is assumed that applications
_will not block_, and therefore non-blocking servers use a small, fixed-size thread pool
(event loop workers) to handle requests.
[TIP]
====
To "scale" and "small number of threads" may sound contradictory but to never block the
current thread, and rely on callbacks instead, means you don't need extra threads as
there are no blocking calls to absorb.
====
_Invoking a Blocking API_
What if you do need to use a blocking library? Both Reactor and RxJava provide the
`publishOn` operator to continue processing on a different thread. That means there is an
easy escape latch. Keep in mind however that blocking APIs are not a good fit for
this concurrency model.
_Mutable State_
In Reactor and RxJava, logic is declared through operators, and at runtime, a reactive
pipeline is formed where data is processed sequentially, in distinct stages. A key benefit
of that is that it frees applications from having to protect mutable state because
application code within that pipeline is never invoked concurrently.
_Threading Model_
What threads should you expect to see on a server running with Spring WebFlux?
* On a "vanilla" Spring WebFlux server (e.g. no data access, nor other optional
dependencies), you can expect one thread for the server, and several others for request
processing (typically as many as the number of CPU cores). Servlet containers, however,
may start with more threads (e.g. 10 on Tomcat), in support of both servlet, blocking I/O
and servlet 3.1, non-blocking I/O usage.
* The reactive `WebClient` operates in event loop style. So you'll see a small, fixed
number of processing threads related to that, e.g. "reactor-http-nio-" with the Reactor
Netty connector. However if Reactor Netty is used for both client and server, the two
will share event loop resources by default.
* Reactor and RxJava provide thread pool abstractions, called Schedulers, to use with the
`publishOn` operator that is used to switch processing to a different thread pool.
The schedulers have names that suggest a specific concurrency strategy, e.g. "parallel"
for CPU-bound work with a limited number of threads, or "elastic" for I/O-bound work with
a large number of threads. If you see such threads it means some code is using a
specific thread pool `Scheduler` strategy.
* Data access libraries and other 3rd party dependencies may also create and use threads
of their own.
_Configuring_
The Spring Framework does not provide support for starting and stopping
<<webflux-server-choice,servers>>. To configure the threading model for a server, you'll
need to use server-specific config APIs, or if using Spring Boot, check the Spring
Boot configuration options for each server. The WebClient
<<web-reactive.adoc#webflux-client-builder,can be configured>> directly. For all other
libraries, refer to their respective documentation.
[[webflux-reactive-spring-web]]
== Reactive Spring Web

Loading…
Cancel
Save