Spring MVC 构建于 Servlet API 之上,使用的是同步阻塞式 I/O 模型,什么是同步阻塞式 I/O 模型呢?就是说,每一个请求对应一个线程去处理。
Spring WebFlux 是一个异步非阻塞式的 Web 框架,它能够充分利用多核 CPU 的硬件资源去处理大量的并发请求。
WebFlux 内部使用的是响应式编程(Reactive Programming),以 Reactor 库为基础, 基于异步和事件驱动,可以让我们在不扩充硬件资源的前提下,提升系统的吞吐量和伸缩性。
看到这里,你是不是以为 WebFlux 能够使程序运行的更快呢?量化一点,比如说我使用 WebFlux 以后,一个接口的请求响应时间是不是就缩短了呢?
抱歉了,答案是否定的! 以下是官方原话:
Reactive and non-blocking generally do not make applications run faster.
WebFlux 并不能使接口的响应时间缩短,它仅仅能够提升吞吐量和伸缩性。
PS: IO 密集型包括:磁盘IO密集型, 网络IO密集型,微服务网关就属于网络 IO 密集型,使用异步非阻塞式编程模型,能够显著地提升网关对下游服务转发的吞吐量。
首先你需要明确一点就是:WebFlux 不是 Spring MVC 的替代方案!,虽然 WebFlux 也可以被运行在 Servlet 容器上(需是 Servlet 3.1+ 以上的容器),但是 WebFlux 主要还是应用在异步非阻塞编程模型,而 Spring MVC 是同步阻塞的,如果你目前在 Spring MVC 框架中大量使用非同步方案,那么,WebFlux 才是你想要的,否则,使用 Spring MVC 才是你的首选。
在微服务架构中,Spring MVC 和 WebFlux 可以混合使用,比如已经提到的,对于那些 IO 密集型服务(如网关),我们就可以使用 WebFlux 来实现。
选 WebFlux 还是 Spring MVC? This is not a problem!
咱不能为了装逼而装逼,为了技术而技术,还要考量到转向非阻塞响应式编程学习曲线是陡峭的,小组成员的学习成本等诸多因素。
总之一句话,在合适的场景中,选型最合适的技术。
注意点
maven依赖包
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webfluxartifactId>
dependency>
下面我们来看一个简单的例子(基于WebFlux环境构建):
// 阻塞5秒钟
private String createStr() {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
}
return "some string";
}
// 普通的SpringMVC方法
@GetMapping("/1")
private String get1() {
log.info("get1 start");
String result = createStr();
log.info("get1 end.");
return result;
}
// WebFlux(返回的是Mono)
@GetMapping("/2")
private Mono<String> get2() {
log.info("get2 start");
Mono<String> result = Mono.fromSupplier(() -> createStr());
log.info("get2 end.");
return result;
}
我们分别来访问一下SpringMVC的接口和WebFlux的接口,看一下有什么区别:
SpringMVC:
WebFlux:
从调用者(浏览器)的角度而言,是感知不到有什么变化的,因为都是得等待5s才返回数据。但是,从服务端的日志我们可以看出,WebFlux是直接返回Mono对象的(而不是像SpringMVC一直同步阻塞5s,线程才返回)。
这正是WebFlux的好处:能够以固定的线程来处理高并发(充分发挥机器的性能)。
WebFlux还支持服务器推送(SSE - >Server Send Event),我们来看个例子:
/**
* Flux : 返回0-n个元素
* 注:需要指定MediaType
* @return
*/
@GetMapping(value = "/3", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
private Flux<String> flux() {
Flux<String> result = Flux
.fromStream(IntStream.range(1, 5).mapToObj(i -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
return "flux data--" + i;
}));
return result;
}
效果就是每秒会给浏览器推送数据: