★ Spring WebFluxの2つの開発方法
1. 采用类似于Spring MVC的注解的方式来开发。
此时开发时感觉Spring MVC差异不大,但底层依然是反应式API。
2. 使用函数式编程来开发
★ アノテーションに基づいて Spring WebFlux を開発する
开发上变化并不大,主要是处理方法的返回值可使用Mono或Flux,但并不强制使用Mono或Flux
WebFlux的变化主要是两点:
- 彻底抛弃Servlet API;
- 基于订阅-发布的异步机制。
这两点的区别主要体现在底层服务器能以较小的线程池处理更高的并发,从而提高应用的可伸缩性
WebFlux支持基于背压(back press)的反应式流。
バックプレッシャーとは:
リアクティブの概念であり、サブスクライバーの消費能力がパブリッシャーの消費能力よりもはるかに低い場合、サブスクライバー (つまりコンシューマー) はキャンセルを通知したり、パブリッシャーの本番データを停止したりするメカニズムを備えています。このような仕組みを「背圧」と呼ぶことができます。
平たく言えば、消費者が消費に滞りがあると、プッシュ型の生産者に対して「もう生産しなくていいから、ゆっくりしてください」と逆に言うのですが、これをバックプレッシャーといいます。
たとえば、この .onBackpressureDrop() メソッドは、メソッドのバック プレッシャー処理機能を有効にするために使用されます。このメカニズムは、パブリッシャがサブスクライバに送信するメッセージが多すぎて、サブスクライバがそれを処理できない場合に、一部のデータが破棄されて確実に実行されるというものです。プログラムはクラッシュしません。
コードデモ:
spring webFlux を使用して、springmvc では実行できないバック プレッシャー機能をデモンストレーションします。つまり、メッセージのパブリッシャーがメッセージのサブスクライバーにメッセージを継続的にプッシュできます。それは、クライアントに常にメッセージを送信することです。
要件: 5 秒ごとにメッセージをクライアントにプッシュします。
プロジェクトを作成する際、以前は Spring MVC ベースの Spring Web をチェックしていましたが、今回はリアクティブベースの Spring Reactive Web をチェックしてみます。
図に示すように:
Spring WebFlux は Reactor フレームワークと統合されているか、Reactor フレームワークに基づいていることがわかります。Spring
WebFlux と Reactor の最下層はデフォルトで Web サーバーとして Netty を使用します。Spring
MVC は Web として Tomcat を使用します。サーバ。
ID で書籍をクエリするプロセスを記述するだけで、データベースが Map コレクションに置き換えられます。
本を追加するメソッド、postMapping 送信タイプを記述し、postman を使用してテストします。
ここに本のオブジェクト データを書き込み、json 形式で送信し、
バックエンドで @RequestBody アノテーションを使用してデータを受信します。
public Book addBook(@RequestBody Book book){}
重要なのは、このメソッドはクライアントにデータを送信し続けるというバック プレッシャーを反映しているということです。
コードをより単純化します。
いくつかのメモ:
すべての書籍のコードは後で参照してください。
関数の実装:
要件: 5 秒ごとにメッセージをクライアントにプッシュします。
プロジェクトの実行中に書籍データを挿入すると、実際にクエリが 5 秒ごとに実行され、データがクライアントにプッシュされることがわかります。
これは、Spring MVC では実装できないバックプレッシャー関数です。
プロジェクトを spring mvc に変更すると、Flux は使用できなくなります。
通过依赖把 <artifactId>spring-boot-starter-webflux</artifactId>
改成 <artifactId>spring-boot-starter-web</artifactId>
就是把 spring webflux 改成 spring mvc 框架
完全なコード
pom の依存関係:
<!-- 表明使用 WebFlux , 此时是反应式 Web 应用,默认使用 Reactor netty 作为服务器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
ドメイン
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;
}
}
ブックコントローラー
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;
}
}
ブックサービス
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();
}
BookServiceImpl
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;
}
}
.onBackpressureDrop() 関数
.onBackpressureDrop() メソッドの機能を確認します。