preface
Related concepts of reactive programming model were introduced above, and the core API of Spring Reactor was briefly summarized. This article will introduce Spring 5 WebFlux, including Servlet 3.1 +, Router Functions, WebFlux and Reactive Streams, etc. And how to configure HTTP request processing in Spring Boot 2.0 as global functional routing and MVC controller, respectively.
The body of the
Introduction to Spring 5 WebFlux
For WebFlux responsive programming in Spring 5, the following figure shows the traditional Spring Web MVC structure and the new Spring WebFlux framework based on Reactive Streams added to Spring 5. You can use the webFlux module to build asynchronous, non-clogged, event-driven services that scale very well.
As shown in the figure, the WebFlux module consists of Router Functions, WebFlux, and Reactive Streams from top to bottom.
Servlet 3.1+ API introduction
The WebFlux module needs to run on a container that implements the Servlet 3.1+ specification. Support for asynchronous processing was added to the Servlet 3.1 specification, where Servlet threads do not have to block until business processing is complete.
In Servlet 3.1, the threading model for request processing is roughly as follows:
-
When a Servlet thread receives a new request, it does not need to wait for the business process to complete before output the result, but delegates the request to another thread (the business thread) to complete it.
-
The Servlet thread returns the delegate to the container to receive the new request.
The Servlet 3.1 specification is particularly suitable for scenarios where business processing is time consuming and can reduce server resource usage and increase concurrent processing speed, with little benefit for those scenarios where rapid response is possible.
Therefore, WebFlux supports synchronous containers such as Tomcat and Jetty, as well as asynchronous containers such as Netty and Undertow. In the container, Spring WebFlux ADAPTS the input stream to Mono or Flux format for unified processing.
Spring WebFlux function module
Each WebFlux module in the figure above is introduced below:
1. Router Functions
Spring MVC annotations to standard @Controller, @RequestMapping, etc. provide a functional style API for creating routers, handlers, and filters.
2. WebFlux
Core components that coordinate upstream and downstream components to provide responsive programming support.
3. Reactive Streams
It is an asynchronous data stream processing standard that supports Backpressure. The mainstream implementation includes RxJava and Reactor, and Spring WebFlux integrates Reactor.
Flux
Flux and Mono are event publishers that act like producers and provide subscription interfaces to consumers. When an event occurs, Flux or Mono calls back to the corresponding method to notify the consumer of the event.
Here’s a working flow chart for Flux:
Flux can emit many items, which can go through Operators before being subscribed. Here is an example of using Flux:
Flux.fromIterable(getSomeLongList())
.mergeWith(Flux.interval(100))
.doOnNext(serviceA::someObserver)
.map(d -> d * 2)
.take(3)
.onErrorResumeWith(errorHandler::fallback)
.doAfterTerminate(serviceM::incrementTerminate)
.subscribe(System.out::println);
Copy the code
Mono
The following picture is the processing process of Mono, which can intuitively see the difference between Mono and Flux:
Mono can emit only one item. Here is an example using Mono:
Mono.fromCallable(System::currentTimeMillis)
.flatMap(time -> Mono.first(serviceA.findRecent(time), serviceB.findRecent(time)))
.timeout(Duration.ofSeconds(3), errorHandler::fallback)
.doOnSuccess(r -> serviceM.incrementSuccess())
.subscribe(System.out::println);
Copy the code
Spring Boot 2.0 Reactive Stack
Spring Boot Webflux is implemented based on Reactor. Spring Boot 2.0 includes a new Spring-WebFlux module. This module contains support for responsive HTTP and WebSocket clients, as well as for REST, HTML, and WebSocket interaction programs. In general, Spring MVC is used for synchronous processing and Spring Webflux is used for asynchronous processing.
As you can see above, Spring Boot 2.0 provides both synchronous blocking and asynchronous non-blocking API stacks, from the Web presentation layer to data access to containers.
Compare the differences between the two from top to bottom:
API Stack | Sevlet Stack | Reactive Stack |
---|---|---|
Web control layer | Spring MVC | Spring WebFlux |
Security authentication layer | Spring Security | Spring Security |
Data access layer | Spring Data Repositories | Spring Data Reactive Repositories |
Container API | Servlet API | Reactive Streams Adapters |
The built-in container | Servlet Containers | Netty, 3.1 + Servlet Containers |
Applicable scenario
Once the control layer uses Spring WebFlux, its security authentication layer and data access layer must use Reactive APIS. Second, Spring Data Reactive Repositories currently only supports several NOSQL that do not support transaction management, such as MongoDB, Redis and Couchbase. These disadvantages and risks must be weighed when selecting a technology, such as:
-
There is no need to change to Spring WebFlux if Spring MVC meets the scenario.
-
To pay attention to container support, look at the support for the underlying embedded containers.
-
Microservice architecture, Spring WebFlux and Spring MVC can be mixed. Especially when developing IO intensive services, you can choose Spring WebFlux to implement it.
Programming model
The Spring 5 Web module contains the HTTP abstraction for Spring WebFlux. Similar to the Servlet API, WebFlux provides a WebHandler API to define non-blocking API abstractions. The following two programming model implementations can be selected:
-
Annotation Control layer: In keeping with MVC, WebFlux also supports responsive @requestBody annotations.
-
Functional endpoints: Tools for establishing routing and processing requests based on the Lambda lightweight programming model. The biggest difference is that this model controls the request-response lifecycle
The built-in container
Just like the Spring Boot framework, Spring WebFlux starts applications via Netty by default and automatically sets the default port to 8080. There is also support for Jetty, Undertow, and other containers. By adding the corresponding container Starter component dependency, the developer can configure and use the corresponding embedded container instance.
Note: Must be Servlet 3.1+ container, such as Tomcat, Jetty; Or non-servlet containers, such as Netty and Undertow.
The Starter kit
Like the Spring Boot Macro framework, Spring Boot Webflux provides a number of Starter components out of the box. Adding spring-boot-starter-webflux dependencies can be used to build responsive API services, including WebFlux and Tomcat embedded containers, etc.
Quick start
Spring Initializr builds the skeleton of the project
Use Spring Initializer to quickly generate Spring Boot applications, configure project information, and set dependencies.
Configuring Maven dependencies
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>. 2.0.0 RELEASE</version>
<relativePath/> <! -- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
Copy the code
Spring Boot starts the class
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); }}Copy the code
Configuring entity Classes
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Message {
String body;
}
Copy the code
1. MVC controller approach
1.1. Write the controller
@RestController
@RequestMapping
public class MessageController {
@GetMapping
public Flux<Message> allMessages(a){
return Flux.just(
Message.builder().body("hello Spring 5").build(),
Message.builder().body("hello Spring Boot 2").build() ); }}Copy the code
1.2. Write test classes
@RunWith(SpringRunner.class)
@WebFluxTest(controllers = MessageController.class)
public class DemoApplicationTests {
@Autowired
WebTestClient client;
@Test
public void getAllMessagesShouldBeOk(a) {
client.get().uri("/").exchange().expectStatus().isOk(); }}Copy the code
1.3. View startup logs
The 2018-05-27 17:37:23. 67749-550 the INFO [main] S.W.R.R.M.A.R equestMappingHandlerMapping: Mapped"{[],methods=[GET]}"onto reactor.core.publisher.Flux<com.example.demo.Message> com.example.demo.MessageController.allMessages() 2018-05-27 17:37:23. 67749-998 the INFO [ctor - HTTP - nio - 1] r.ipc.net ty. TCP. BlockingNettyContext: Started HttpServer on / 0:0:0:0:0:0:0:0:0:0:8080 2018-05-27 17:37:23.999 INFO 67749 -- [main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 8080 the 2018-05-27 17:37:24. 67749-003 the INFO [main] com. Example. Demo. DemoApplication: Started DemoApplicationin1.6 seconds (JVM is runningfor 2.274)
Copy the code
It can be seen from the log:
- At the start
WebFlux
usingMVC
The nativeRequestMappingHandlerMapping
Put the one in the controllerRequest path 和MVC
In theThe processorBind. Spring WebFlux
By default theNetty
As aThe built-in containerAnd the default boot port is8080
.
Visit http://localhost:8080 and the following result is returned:
2. Global Router API mode
2.1. Configure the global Router Bean
@Configuration
public class DemoRouterConfig {
@Bean
public RouterFunction<ServerResponse> routes(a) {
return route(GET("/"), (ServerRequest req)-> ok()
.body(
BodyInserters.fromObject(
Arrays.asList(
Message.builder().body("hello Spring 5").build(),
Message.builder().body("hello Spring Boot 2").build() ) ) ) ); }}Copy the code
2.2. Write test classes
@RunWith(SpringRunner.class)
@WebFluxTest
public class DemoApplicationTests {
@Autowired
WebTestClient client;
@Test
public void getAllMessagesShouldBeOk(a) {
client.get().uri("/").exchange().expectStatus().isOk(); }}Copy the code
2.3. View startup logs
Run the Spring Boot Boot entry class, and the Boot log looks like this (unimportant omitted) :
The 2018-05-27 17:20:28. 67696-870 the INFO [the main] O.S.W.R.F.S.S.R outerFunctionMapping: Mapped (GET && /) -> com.example.demo.DemoRouterConfig$$LambdaThe $213/1561745898@3381b4fcThe 2018-05-27 17:20:28. 67696-931 the INFO [main] O.S.W.R.R.M.A.C ontrollerMethodResolver: &for@ControllerAdvice: org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext@1460a8c0: startup date [Sun May 27 17:20:27 CST 2018]; The root of the context hierarchy 17:20:29 2018-05-27. 67696-311 the INFO [ctor - HTTP - nio - 1] r.ipc.net ty. TCP. BlockingNettyContext : Started HttpServer on / 0:0:0:0:0:0:0:0:0:0:0:8080 2018-05-27 17:20:29.312 INFO 67696 -- [main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 8080 the 2018-05-27 17:20:29. 67696-316 the INFO [main] com. Example. Demo. DemoApplication: Started DemoApplicationin2.137 seconds (JVM is runningfor 3.169)
Copy the code
According to the logs, WebFlux uses RouterFunctionMapping to map and bind global paths and request processing in RouterFunction during startup.
Visit http://localhost:8080 and the following result is returned:
As you can see, both Fucntional Router and MVC Controller can have the same effect!
Development run environment
-
JDK 1.8 + : Spring Boot 2.x requires JDK 1.8 or later. In addition, Spring Boot 2.x is only compatible with Spring Framework 5.0 and later versions.
-
Maven 3.2 + : The dependency build tool for Spring Boot 2.x is Maven. Version 3.2 or later is required. Gradle requires version 1.12 or later. Maven and Gradle just pick your favorite.
summary
This paper first introduces Spring 5 WebFlux separately, including Servlet 3.1 +, Router Functions, WebFlux and Reactive Streams, etc. Then, the differences between Reactive Stack and Servlet Stack are introduced in detail in Spring Boot 2.0, and the configuration and use cases of WebFlux based on global functional routing and controller are given respectively.
Welcome to pay attention to the technical public number: Zero one Technology Stack
This account will continue to share learning materials and articles on back-end technologies, including virtual machine basics, multithreaded programming, high-performance frameworks, asynchronous, caching and messaging middleware, distributed and microservices, architecture learning and progression.