参考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 示例程序
spring-boot-starter-parent
org.springframework.boot
2.3.1.RELEASE
8
8
1.8
UTF-8
UTF-8
true
2.3.1.RELEASE
org.springframework.boot
spring-boot-starter-webflux
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 hello(@PathVariable String id){
log.info("spring-webflux 接口(方式1)-注解-start,id:{}",id);
Mono 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 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 hello(ServerRequest request){
String id = request.pathVariable("id");
log.info("spring-webflux 接口(方式2)-声明式-start,id:{}",id);
Mono 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 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 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



