Spring WebFlux
1.SpringWebFLux介绍
是Spring Framework提供的一种用户构建响应式Web应用程序的模块。它基于Reactive Streams标准,并使用了Reactor库来实现非阻塞、异步的编程模型
与传统的Spring MVC相比,Spring WebFlux采用了一种基于事件驱动的架构,可以处理更高的并发请求和负载。它不再依赖于Servlet容器,而是使用自己的服务器,例如Netty或Undertow,以实现异步、高效的请求处理。
Spring WebFlux提供了以下主要特性:
- 响应式编程模型:通过使用Reactive Streams和Reactor库,可以实现异步、非阻塞的编程方式,从而提高系统的吞吐量和性能。
- 注解驱动的编程模式:类似于Spring MVC,Spring WebFlux也支持基于注解的编程,通过注解控制器和路由函数,可以方便地定义请求映射、参数绑定和响应结果。
- 函数式端点定义:除了使用注解定义控制器,Spring WebFlux还支持使用函数式编程的方式定义端点。可以通过Router Functions来构建路由和处理请求,使代码更为简洁和灵活。
- 响应式数据流处理:Spring WebFlux中引入了Flux和Mono两个反应式类型,用于处理数据流。Flux表示一个包含0到N个元素的响应式序列,而Mono表示一个包含0或1个元素的响应式序列。这些类型可以用于处理异步数据流和流式操作。
1.1 什么是异步非阻塞
-
异步和同步:
异步和同步针对调用者,调用者发送请求,如果等待对方回应之后才继续执行去做其他事情就是同步,发送请求成功之后不等待对方回应直接去做其他事情就是非阻塞
-
阻塞和非阻塞
阻塞和非阻塞针对被调用者,调用者收到请求后,做完任务请求才给出反馈就是阻塞,收到请求之后马上给出反馈就是非阻塞
2.响应式编程
2.1 什么是响应式编程
响应式变成是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。电子表格程序就是响应式编程的一个例子。单元格可以包含字面或类似“B1+C1”的公司,而包含公式的值会依据其他单元格的值变化而变化;
响应式编程(Reactive Programming)是一种编程范式,旨在处理异步数据流和事件,并支持高效、可靠、可扩展的程序开发。它强调数据流的变化和传播,以及对这些变化作出相应的能力。
在响应式编程中,程序被看作是一组数据流和数据流之间的关系。数据流可以是来自用户输入、传感器、网络请求等的事件流或数据序列。响应式编程通过使用观察者模式(Observer Pattern)和函数式编程的概念,使得开发者可以定义对这些数据流进行监听和处理的逻辑。
响应式编程具有以下特点:
- 异步和非阻塞:响应式编程鼓励使用非阻塞的方式处理数据流,以避免线程阻塞和资源浪费。它通过使用回调函数、Promise、Future等机制来处理异步操作。
- 数据流的变换和组合:响应式编程提供了丰富的操作符和转换函数,可以对数据流进行过滤、映射、聚合等各种操作。这些操作可以组合在一起,形成复杂的数据流处理逻辑。
- 响应式和自动更新:响应式编程使得程序能够自动地对数据流的变化作出相应。当输入数据流发生变化时,相关的处理逻辑会自动更新和重新执行。
- 错误处理和容错性:响应式编程提供了错误处理机制,可以方便地捕获和处理异常情况。它也支持容错性,通过使用重试、超时等策略来处理潜在的故障。
响应式编程有许多应用场景,特别是在需要处理大量实时数据、事件驱动的系统和用户界面开发中。它能够简化异步编程和事件处理的复杂性,并提供了一种可靠和高效的方式来处理数据流和事件流。
public class ObserverDemo extends Observable {
public static void main(String[] args) {
ObserverDemo observer =new ObserverDemo();
observer.addObserver((o,arg)->{
System.out.println("发送变化");
});
observer.addObserver((o,arg)->{
System.out.println("手动通知观察者通知,准备改变");
});
observer.setChanged();//数据变化
observer.notifyObservers();//通知
}
}
2.2 Reactor实现
-
Reactor 是一个基于 Java 8+ 的响应式编程框架,它提供了一种基于事件驱动、非阻塞的编程模型,旨在简化异步编程和处理流式数据。Reactor 的核心是 Reactor 类库,它实现了 Reactive Streams 规范,并提供了一系列用于构建响应式应用的工具和组件。
-
Reactor有两个核心类,Mono和Flux,这两个类实现接口Publisher,提供丰富的操作符。Flux对象实现发布者,返回N个元素;Mono实现发布者,返回0或者1个元素;
-
Flux 和 Mono:Flux 表示包含零个或多个元素的异步序列,而 Mono 表示包含零个或一个元素的异步序列。Flux 和 Mono 是 Reactor 中最基本的数据流表示,您可以对它们进行各种操作,比如映射、过滤、合并等。
-
Flux和Mono都可以发送三种信号:
元素值,错误信号,完成信号,错误信号和完成信号都代表终止信号,错误信号终止数据流同时把错误信息传递给订阅者
代码演示Flux和Mono:
/**
* @Description: 创建Flux Mono方法
* @author: scott
* @date: 2023年10月30日 18:54
*/
public class TestReactor {
public static void main(String[] args) {
//just 直接声明
Flux.just(1, 2, 3, 4).subscribe(System.out::println);
Mono.just(1);
//其他方法
Integer[] array = {1, 2, 3, 4};
Flux.fromArray(array);
List<Integer> list = Arrays.asList(1, 2, 3, 5);
Flux.fromIterable(list);
}
}
- 错误信号和完成信号都是终止信号,不能共存
- 如果没有发送任何元素中,而是直接发送错误或者完成信号,则表示空数据流
- 如果没有错误和完成信号,则表示无限数据流
- 调用just或者其他方法只是声明数据流,数据流并没有发出,只有进行订阅之后才会触发数据流,不订阅subscribe什么都不会发生的
-
常用操作符
map: 元素映射为新元素
Flux<Integer> numbers = Flux.just(1, 2, 3, 4, 5); Flux<String> mappedNumbers = numbers.map(number -> "Number: " + number);
**Filter 操作符:**根据指定的条件筛选元素
Flux<Integer> numbers = Flux.just(1, 2, 3, 4, 5); Flux<Integer> evenNumbers = numbers.filter(number -> number % 2 == 0);
**FlatMap 操作符:**将流的每个元素转换为新流,然后将这些流平铺合并成单一流。
Flux<String> letters = Flux.just("A", "B", "C"); Flux<String> combinedLetters = letters.flatMap(letter -> Flux.just(letter.toLowerCase(), letter.toUpperCase()));
**Merge 操作符:**将多个流合并为单一流。
Flux<Integer> numbers1 = Flux.just(1, 2, 3); Flux<Integer> numbers2 = Flux.just(4, 5, 6); Flux<Integer> mergedNumbers = Flux.merge(numbers1, numbers2);
其他操作符等:
reduce:对流中的元素进行累积计算,返回一个单个的结果。
take:从流中取出指定数量的元素。
zip:将多个流的对应位置的元素进行配对。
onErrorResume:在遇到错误时,返回一个备用的流。
retry:在遇到错误时,重新尝试执行操作。 timeout:设置一个超时时间,在指定时间内未完成操作,则抛出超时异常。
3. SpringWebFulx执行流程和核心API
SpringWebflux 处理请求流程图:
SpringMvc处理请求流程图:
Spring Webflux 执行过程和Spring mvc相似,SpringWebflux核心控制器DispatchHandler,实现接口WebHanlder
接口WebHanlder 有一个方法
public interface WebHandler {
/**
* Handle the web server exchange.
* @param exchange the current server exchange
* @return {@code Mono<Void>} to indicate when request handling is complete
*/
Mono<Void> handle(ServerWebExchange exchange);
}
@Override
public Mono<Void> handle(ServerWebExchange exchange) {// 放http请求响应信息
if (this.handlerMappings == null) {
return createNotFoundError();
}
return Flux.fromIterable(this.handlerMappings)
.concatMap(mapping -> mapping.getHandler(exchange)) //根据请求地址获取对应mapping
.next()
.switchIfEmpty(createNotFoundError())
.flatMap(handler -> invokeHandler(exchange, handler))// 调用具体的业务方法
.flatMap(result -> handleResult(exchange, result)); //处理结果返回
}
4.SpringWebflux (基于注解编程模型实现)
4.1导入包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
4.2实体类
package com.xq.webflux_test.entity;
/**
* @Description: TODO
* @author: scott
* @date: 2023年11月14日 16:54
*/
public class User {
private String name;
private String gendre;
private Integer age;
public User(String name, String gendre, Integer age) {
this.name = name;
this.gendre = gendre;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGendre() {
return gendre;
}
public void setGendre(String gendre) {
this.gendre = gendre;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
4.3 Service类
package com.xq.webflux_test.service;
import com.xq.webflux_test.entity.User;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.List;
public interface UserService {
//根据id查询用户
Mono<User> getUserById(int id);
//查询所有的用户
Flux<User> getAllUser();
//添加用户
Mono<Void> addUser(Mono<User> user);
}
package com.xq.webflux_test.service.impl;
import com.xq.webflux_test.entity.User;
import com.xq.webflux_test.service.UserService;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Map;
/**
* @Description: user接口实现
* @author: xy
* @date: 2023年11月14日 17:01
*/
@Service
public class UserServiceImpl implements UserService {
private Map<Integer, User> users = new HashMap<>();
public UserServiceImpl() {
users.put(1, new User("xy", "男", 18));
users.put(2, new User("Jones", "男", 28));
users.put(3, new User("jack", "女", 8));
}
@Override
public Mono<User> getUserById(int id) {
return Mono.justOrEmpty(this.users.get(id));
}
@Override
public Flux<User> getAllUser() {
return Flux.fromIterable(this.users.values());
}
@Override
public Mono<Void> addUser(Mono<User> userMono) {
return userMono.doOnNext(user -> {
//向map集合中放值
int id = this.users.size() + 1;
this.users.put(id, user);
}).thenEmpty(Mono.empty());
}
}
4.4 Controller
package com.xq.webflux_test.controller;
import com.xq.webflux_test.entity.User;
import com.xq.webflux_test.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
* @Description:
* @author: xy
* @date: 2023年11月14日 17:10
*/
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/user/{id}")
public Mono<User> getUserById(@PathVariable("id") Integer id) {
return userService.getUserById(id);
}
@GetMapping("/user/list")
public Flux<User> getUserById() {
return userService.getAllUser();
}
@PostMapping("user")
public Mono<Void> addUser(@RequestBody User user) {
return userService.addUser(Mono.just(user));
}
}
4.5 测试结果
5.基于函数式编程实现
5.1 创建UserHandler
package com.xq.webflux_test2.handler;
import com.xq.webflux_test.entity.User;
import com.xq.webflux_test.service.UserService;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
* @Description:
* @author: xq0136
* @date: 2023年11月15日 17:17
*/
public class UserHandler {
private final UserService userService;
public UserHandler(UserService userService) {
this.userService = userService;
}
public Mono<ServerResponse> getUserById(ServerRequest request){
int userId = Integer.valueOf(request.pathVariable("id"));
Mono<User> userMono = this.userService.getUserById(userId);
Mono<ServerResponse> notFount = ServerResponse.notFound().build();
//把userMono进行转换返回
//使用reactor 操作符flatMap
return userMono.flatMap(user -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(user))
.switchIfEmpty(notFount);
}
//查询所有
public Mono<ServerResponse> getAllUsers(ServerRequest request){
Flux<User> allUser = this.userService.getAllUser();
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(allUser,User.class);
}
public Mono<ServerResponse> saveUser(ServerRequest request){
//得到user对象
Mono<User> userMono = request.bodyToMono(User.class);
return ServerResponse.ok().build(this.userService.addUser(userMono));
}
}
5.2 测试
/**
* @Description:
* @author: xy
* @date: 2024年01月12日 17:16
*/
public class Server {
public static void main(String[] args) throws Exception {
Server server = new Server();
server.createReactorServer();
System.out.println("enter to exit");
System.in.read();
}
//1 创建Router路由
public RouterFunction<ServerResponse> routingFunction() {
//创建hanler对象
UserService userService = new UserServiceImpl();
UserHandler handler = new UserHandler(userService);
//设置路由
return RouterFunctions.route(
GET("/users/{id}").and(accept(APPLICATION_JSON)),handler::getUserById)
.andRoute(GET("/users").and(accept(APPLICATION_JSON)),handler::getAllUsers);
}
//2 创建服务器完成适配
public void createReactorServer() {
//路由和handler适配
RouterFunction<ServerResponse> route = routingFunction();
//将路由转为Handler
HttpHandler httpHandler = RouterFunctions.toHttpHandler(route);
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
//创建服务器
HttpServer httpServer = HttpServer.create();
httpServer.handle(adapter).bindNow();
}
}