★ Dos métodos de desarrollo de Spring WebFlux
1. 采用类似于Spring MVC的注解的方式来开发。
此时开发时感觉Spring MVC差异不大,但底层依然是反应式API。
2. 使用函数式编程来开发
★ Desarrollar Spring WebFlux basado en anotaciones
开发上变化并不大,主要是处理方法的返回值可使用Mono或Flux,但并不强制使用Mono或Flux
WebFlux的变化主要是两点:
- 彻底抛弃Servlet API;
- 基于订阅-发布的异步机制。
这两点的区别主要体现在底层服务器能以较小的线程池处理更高的并发,从而提高应用的可伸缩性
WebFlux支持基于背压(back press)的反应式流。
Qué es la contrapresión:
este es el concepto de reactiva: cuando la capacidad de consumo del suscriptor es mucho menor que la del editor, el suscriptor (es decir, el consumidor) tiene un mecanismo para notificar al editor que cancele o finalice la producción de datos. Este mecanismo se puede llamar "contrapresión".
Para decirlo sin rodeos, cuando los consumidores tienen un retraso en el consumo, le dicen al productor que empuja a la inversa: "Ya no necesito que produzcas más, por favor, reduce la velocidad", lo que se llama contrapresión.
Por ejemplo, este método .onBackpressionDrop() se utiliza para habilitar la función de procesamiento de contrapresión para el método. El mecanismo es que cuando el editor envía demasiados mensajes a los suscriptores y los suscriptores no pueden manejarlos, algunos datos se eliminarán para garantizar que el programa no fallará.
Demostración de código:
Utilice spring webFlux para demostrar una función de contrapresión que springmvc no puede realizar, es decir, el editor de mensajes puede enviar mensajes continuamente a los suscriptores de mensajes. Simplemente siga enviando mensajes al cliente.
Requisitos: enviar mensajes al cliente cada 5 segundos.
Al crear el proyecto, anteriormente verifiqué Spring Web, que se basa en Spring MVC, ahora quiero verificar Spring Reactive Web, que se basa en reactividad.
Como se muestra en la figura:
se puede ver que Spring WebFlux está integrado con el marco Reactor/basado en el marco Reactor.
Spring WebFlux y Reactor usan Netty como servidor web de forma predeterminada.
Spring MVC usa Tomcat como servidor web.
Simplemente escriba un proceso para consultar libros por identificación y la base de datos se reemplazará por una colección de mapas:
Escriba un método para agregar libros, tipo de envío postMapping y pruébelo con cartero.
Escriba aquí los datos del objeto de un libro y envíelo en formato json.
El backend utiliza el objeto modificado con la anotación @RequestBody para recibir los datos.
libro público addBook(@RequestBody libro libro){}
El caso es que este método, que refleja contrapresión, consiste en enviar datos al cliente todo el tiempo.
Simplifique el código:
Algunas notas:
el siguiente código para ver todos los libros.
Implementación de funciones:
Requisitos: enviar mensajes al cliente cada 5 segundos.
Cuando el proyecto se está ejecutando, inserte un dato del libro, se puede ver que la consulta se ejecuta cada 5 segundos y luego los datos se envían al cliente.
Esta es la función de contrapresión que Spring MVC no puede lograr.
Si cambia el proyecto a spring mvc, Flux no estará disponible.
通过依赖把 <artifactId>spring-boot-starter-webflux</artifactId>
改成 <artifactId>spring-boot-starter-web</artifactId>
就是把 spring webflux 改成 spring mvc 框架
código completo
dependencias de pom:
<!-- 表明使用 WebFlux , 此时是反应式 Web 应用,默认使用 Reactor netty 作为服务器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
dominio
package cn.ljh.my_annotation_flux.domain;
import lombok.Data;
@Data
public class Book
{
private Integer id;
private String name;
private double price;
private String author;
public Book(Integer id, String name, double price, String author)
{
this.id = id;
this.name = name;
this.price = price;
this.author = author;
}
}
Controlador de libro
package cn.ljh.my_annotation_flux.controller;
import cn.ljh.my_annotation_flux.domain.Book;
import cn.ljh.my_annotation_flux.service.BookService;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import java.time.Duration;
import java.util.Collection;
@RestController
@RequestMapping("/books")
public class BookController
{
private BookService bookService;
//有参构造器完成依赖注入
public BookController(BookService bookService)
{
this.bookService = bookService;
}
@GetMapping("/{id}")
public Book viewBooks(@PathVariable Integer id)
{
Book book = bookService.getBook(id);
return book;
}
//restful的方式提交请求 ,
// @RequestBody主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的);
@PostMapping("/addBook")
public Book addBook(@RequestBody Book book)
{
bookService.addBook(book);
return book;
}
// 开发基于 背压的 WebFlux 反应式流,来看 spring mvc 做不到的事
// WebFlux支持基于背压(back press)的反应式流。
// 背压:很多种处理方式,消息的发布者可以不断的向消息的订阅者推送消息。消息过多后的一些处理方式。
//这个获取图书的方法,使用到了背压的技术,相当于是一个数据流、消息发布者,会不断的向客户端生成数据,
// 因此需要指定响应的数据类型: 数据是stream数据流,数据流里面的数据格式是json
@GetMapping(value = "/viewBooks",produces = "application/stream+json")
//需要直接用 Reactor 特性的时候,可以让处理方法返回 Mono 或 Flux
public Flux<Book> viewBooks()
{
Flux<Collection<Book>> map =
//周期性的向客户端推送数据,每隔5秒推送一次, interval:间隔 , Duration:持续 , Seconds:秒
//Flux.interval(Duration.ofSeconds(5)) 属于上游发送消息的发送者
Flux.interval(Duration.ofSeconds(5))
//onBackpressureDrop作用:实现处理背压的功能
.onBackpressureDrop()
.map((i) -> bookService.getAllBooks());
//将 Collection 转换 Flux (相当于把同步数据集 转换成 反应式的数据发布者)。
//flatMapIterable 方法中的Lambda 表达式负责将 Coollection 中的元素转成 Flux 中的元素。
Flux<Book> bookFlux = map.flatMapIterable(book -> book);
return bookFlux;
}
}
Servicio de libros
package cn.ljh.my_annotation_flux.service;
import cn.ljh.my_annotation_flux.domain.Book;
import java.util.Collection;
public interface BookService
{
Book getBook(Integer id);
Integer addBook(Book book);
Collection<Book> getAllBooks();
}
LibroServicioImpl
package cn.ljh.my_annotation_flux.service.impl;
import cn.ljh.my_annotation_flux.domain.Book;
import cn.ljh.my_annotation_flux.service.BookService;
import org.springframework.stereotype.Service;
import java.util.*;
//添加这个@Service注解,springboot就可以自动扫描这个Service组件的实现类,然后把这个类部署成容器中的bean。
@Service
public class BookServiceImpl implements BookService
{
//添加一个 Map 集合,假设为数据库
public static final Map<Integer, Book> bookDB = new LinkedHashMap<>();
//创建一个自增id
static int nextId = 4;
//初始化这个数据库
static
{
bookDB.put(1, new Book(1, "火影忍者", 100.0, "岸本"));
bookDB.put(2, new Book(2, "家庭教师", 110.0, "天野明"));
bookDB.put(3, new Book(3, "七龙珠Z", 120.0, "鸟山明"));
}
//查看图书
@Override
public Book getBook(Integer id)
{
Book book = bookDB.get(id);
if (book == null){
throw new RuntimeException("没有此图书信息!");
}
return book;
}
//添加图书
@Override
public Integer addBook(Book book)
{
book.setId(nextId);
bookDB.put(nextId,book);
//返回id,先返回在自增。
return nextId++;
}
//查看所有的图书
@Override
public Collection<Book> getAllBooks()
{
//获取集合中的所有元素
Collection<Book> values = bookDB.values();
return values;
}
}
Función .onBackpressionDrop()
Check.onBackpressionDrop() La función de este método: