Spring Framework 5包含一个新 spring-webflux 模块。该模块包含对反应式HTTP和WebSocket客户端的支持以及反应式服务器Web应用程序(包括REST,HTML浏览器和WebSocket样式交互)
spring web-flux 支持2种不同的编程模型
- 支持Spring MVC @Controller 这种注解,用法大同小异
- 函数式 Java 8 lambda 风格的路由函数处理请求
Spring WebFlux 是 Spring Framework 5.0中引入的新的响应式web框架。与Spring MVC不同,它不需要Servlet API,是完全异步且非阻塞的,并且通过Reactor项目实现了Reactive Streams规范。
Spring WebFlux 用于创建基于事件循环执行模型的完全异步且非阻塞的应用程序。
(PS:异步非阻塞是针对服务端而言的,服务端可以充分利用CPU资源去做更多事情,与客户端无关,客户端该怎么请求还是怎么请求。)
Reactive Streams是一套用于构建高吞吐量、低延迟应用的规范。而Reactor项目是基于这套规范的实现,它是一个完全非阻塞的基础,且支持背压。Spring WebFlux基于Reactor实现了完全异步非阻塞的一套web框架,是一套响应式堆栈。
【spring-webmvc + Servlet + Tomcat】命令式的、同步阻塞的
【spring-webflux + Reactor + Netty】响应式的、异步非阻塞的
参考
Spring WebFlux 入门
外行人都能看得懂的WebFlux,错过了血亏!
WebFlux 示例程序
代码
pom
<!--基于Springboot-->
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.3.1.RELEASE</version>
<relativePath/>
</parent>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<skipTests>true</skipTests>
<spring-boot.version>2.3.1.RELEASE</spring-boot.version>
</properties>
<dependencies>
<!-- 异步非阻塞并不会使程序运行得更快。WebFlux 并不能使接口的响应时间缩短,它仅仅能够提升吞吐量和伸缩性。-->
<!-- Spring WebFlux 是一个异步非阻塞的 Web 框架,所以,它特别适合应用在 IO 密集型的服务中,比如微服务网关这样的应用中。-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
</dependencies>
yml
server:
port: 8998
logging:
file:
path: ./logs
name: spring-logs-demo.log
业务测试代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public class BusService {
private static final Logger log = LoggerFactory.getLogger(BusService.class);
public String implement(String type){
log.info("执行耗时业务,{}",type);
try {
TimeUnit.SECONDS.sleep(2L);
} catch (InterruptedException e) {
log.info("睡眠异常");
}
return "hello.";
}
}
接口-注解方式
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.stream.IntStream;
@RestController
@RequestMapping("/root")
public class RootController {
private static final Logger log = LoggerFactory.getLogger(RootController.class);
private BusService busService;
@Autowired
public void setBusService(BusService busService) {
this.busService = busService;
}
@GetMapping("{id}")
public Mono<String> hello(@PathVariable String id){
log.info("spring-webflux 接口(方式1)-注解-start,id:{}",id);
Mono<String> result = Mono.fromSupplier(()-> busService.implement("api1"));
log.info("spring-webflux 接口(方式1)-注解-end");
return result;
}
@GetMapping(value = "flux",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> flux(){
log.info("spring-webflux 接口(方式1)-服务器推送");
return Flux.fromStream(IntStream.range(1,5).mapToObj(i-> busService.implement("flux") + i));
}
}
接口-路由方式
接口
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
@Component
public class RootHandler {
private static final Logger log = LoggerFactory.getLogger(RootHandler.class);
public Mono<ServerResponse> hello(ServerRequest request){
String id = request.pathVariable("id");
log.info("spring-webflux 接口(方式2)-声明式-start,id:{}",id);
Mono<ServerResponse> mono = ServerResponse.ok().contentType(MediaType.TEXT_PLAIN)
.body(BodyInserters.fromValue("Hello."));
log.info("spring-webflux 接口(方式2)-声明式-end");
return mono;
}
}
路由配置
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
@Configuration
public class RootRouter {
private static final Logger log = LoggerFactory.getLogger(RootRouter.class);
@Bean
public RouterFunction<ServerResponse> router(RootHandler handler){
log.info("spring-webflux 接口(方式2)-声明式-注入接口,{}",handler.getClass());
return RouterFunctions.route(RequestPredicates.GET("/handler/hello/{id}")
.and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),handler::hello);
}
}
测试
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
public class RootWebClient {
private final WebClient client = WebClient.create("http://localhost:8998");
private final Mono<ClientResponse> result = client.get()
.uri("/handler/hello/66")
.accept(MediaType.TEXT_PLAIN).exchange();
public String res(){
return result.flatMap( r -> r.bodyToMono(String.class)).block();
}
}
启动类
import cn.demo.flux.action.RootWebClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class WebFluxDemoApplication {
private static final Logger log = LoggerFactory.getLogger(WebFluxDemoApplication.class);
public static void main(String[] args) {
log.info("start--->");
SpringApplication.run(WebFluxDemoApplication.class,args);
log.info("end--->");
String res = new RootWebClient().res();
log.info("send--->,{}",res);
}
}
测试
代码测试
请求测试-1
请求测试2
测试推送