Reactive Streams 是 JVM 中面向流的库标准和规范:
处理可能无限数量的元素按顺序处理
组件之间异步传递
强制性非阻塞背压(Backpressure)
Backpressure(背压):背压是一种常用策略,使得发布者拥有无限制的缓冲区存储元素,用于确保发布者发布元素太快时,不会去压制订阅者。
Reactive Streams(响应式流)一般由以下组成:
发布者:发布元素到订阅者
订阅者:消费元素
订阅:在发布者中,订阅被创建时,将与订阅者共享
处理器:发布者与订阅者之间处理数据
响应式编程
有了 Reactive Streams 这种标准和规范,利用规范可以进行响应式编程。那再了解下什么是 Reactive programming 响应式编程。响应式编程是基于异步和事件驱动的非阻塞程序,只是垂直通过在 JVM 内启动少量线程扩展,而不是水平通过集群扩展。这就是一个编程范例,具体项目中如何体现呢?
响应式项目编程实战中,通过基于 Reactive Streams 规范实现的框架 Reactor 去实战。Reactor 一般提供两种响应式 API :
Mono:实现发布者,并返回 0 或 1 个元素
Flux:实现发布者,并返回 N 个元素
Spring Webflux
Spring Boot Webflux 就是基于 Reactor 实现的。Spring Boot 2.0 包括一个新的 spring-webflux 模块。该模块包含对响应式 HTTP 和 WebSocket 客户端的支持,以及对 REST,HTML 和 WebSocket 交互等程序的支持。一般来说,Spring MVC 用于同步处理,Spring Webflux 用于异步处理。
Spring Boot Webflux 有两种编程模型实现,一种类似 Spring MVC 注解方式,另一种是使用其功能性端点方式。注解的会在下面快速入门用 Spring Webflux 功能性方式实现。
Spring Boot 2.0 WebFlux 特性
常用的 Spring Boot 2.0 WebFlux 生产的特性如下:
响应式 API
编程模型
适用性
内嵌容器
Starter 组件
还有对日志、Web、消息、测试及扩展等支持。
响应式 API:
Reactor 框架是 Spring Boot Webflux 响应库依赖,通过 Reactive Streams 并与其他响应库交互。提供了 两种响应式 API:Mono 和 Flux。一般是将 Publisher 作为输入,在框架内部转换成 Reactor 类型并处理逻辑,然后返回 Flux 或 Mono 作为输出。
适用性:
MVC 能满足场景的,就不需要更改为 WebFlux。
要注意容器的支持,可以看看下面内嵌容器的支持。
微服务体系结构,WebFlux 和 MVC 可以混合使用。尤其开发 IO 密集型服务的时候,选择 WebFlux 去实现。
编程模型:
Spring 5 web 模块包含了 Spring WebFlux 的 HTTP 抽象。类似 Servlet API , WebFlux 提供了 WebHandler API 去定义非阻塞 API 抽象接口。可以选择以下两种编程模型实现:
注解控制层。和 MVC 保持一致,WebFlux 也支持响应性 @RequestBody 注解。
功能性端点。基于 lambda 轻量级编程模型,用来路由和处理请求的小工具。和上面最大的区别就是,这种模型,全程控制了请求 - 响应的生命流程
跟 Spring Boot 大框架一样启动应用,但 WebFlux 默认是通过 Netty 启动,并且自动设置了默认端口为 8080。另外还提供了对 Jetty、Undertow 等容器的支持。开发者自行在添加对应的容器 Starter 组件依赖,即可配置并使用对应内嵌容器实例。
但是要注意,必须是 Servlet 3.1+ 容器,如 Tomcat、Jetty;或者非 Servlet 容器,如 Netty 和 Undertow。
Starter 组件:
跟 Spring Boot 大框架一样,Spring Boot Webflux 提供了很多 “开箱即用” 的 Starter 组件。Starter 组件是可被加载在应用中的 Maven 依赖项。只需要在 Maven 配置中添加对应的依赖配置,即可使用对应的 Starter 组件。例如,添加 spring-boot-starter-webflux 依赖,就可用于构建响应式 API 服务,其包含了 Web Flux 和 Tomcat 内嵌容器等。
开发中,很多功能是通过添加 Starter 组件的方式来进行实现。那么,Spring Boot 2.x 常用的 Starter 组件有哪些呢?
Spring Boot 2.0 WebFlux 组件:
Spring Boot WebFlux 官方提供了很多 Starter 组件,每个模块会有多种技术实现选型支持,来实现各种复杂的业务需求:
Web:Spring WebFlux
模板引擎:Thymeleaf
存储:Redis、MongoDB、Cassandra,不支持 MySQL
内嵌容器:Tomcat、Jetty、Undertow
以上的都是一些概念,下面开始webflux的hello world小demo:
一,创建webflux项目
适用IDEA创建一个spring boot项目,引入spring-boot-starter-webflux包,如下图:
pom.xml里面的配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.jack</groupId>
<artifactId>webflux1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>webflux1</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
spring-boot-starter-webflux 依赖,是我们核心需要学习 webflux 的包,里面默认包含了 spring-boot-starter-reactor-netty 、spring 5 webflux 包,也就是说默认是通过 netty 启动的。
reactor-test、spring-boot-starter-test 两个依赖搭配是用于单元测试。
spring-boot-maven-plugin 是 Spring Boot Maven 插件,可以运行、编译等调用。
创建成功以后,启动成功后的输出如下:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.1.RELEASE)
2018-04-21 22:25:25.294 INFO 16920 --- [ main] com.jack.webflux1.Webflux1Application : Starting Webflux1Application on wj with PID 16920 (E:\webworkspace\IdeaProjects\springcloud4\webflux1\target\classes started by Administrator in E:\webworkspace\IdeaProjects\springcloud4)
2018-04-21 22:25:25.299 INFO 16920 --- [ main] com.jack.webflux1.Webflux1Application : No active profile set, falling back to default profiles: default
2018-04-21 22:25:25.342 INFO 16920 --- [ main] onfigReactiveWebServerApplicationContext : Refreshing org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext@1198b989: startup date [Sat Apr 21 22:25:25 GMT+08:00 2018]; root of context hierarchy
2018-04-21 22:25:26.354 INFO 16920 --- [ main] o.s.w.r.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.reactive.resource.ResourceWebHandler]
2018-04-21 22:25:26.354 INFO 16920 --- [ main] o.s.w.r.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.reactive.resource.ResourceWebHandler]
2018-04-21 22:25:26.425 INFO 16920 --- [ main] o.s.w.r.r.m.a.ControllerMethodResolver : Looking for @ControllerAdvice: org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext@1198b989: startup date [Sat Apr 21 22:25:25 GMT+08:00 2018]; root of context hierarchy
2018-04-21 22:25:26.756 INFO 16920 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2018-04-21 22:25:27.272 INFO 16920 --- [ctor-http-nio-1] r.ipc.netty.tcp.BlockingNettyContext : Started HttpServer on /0:0:0:0:0:0:0:0:8080
2018-04-21 22:25:27.272 INFO 16920 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 8080
2018-04-21 22:25:27.275 INFO 16920 --- [ main] com.jack.webflux1.Webflux1Application : Started Webflux1Application in 2.324
看输出,可以看到,默认使用netty作为容器启动,默认端口为8080
二,代码编写
1,编写处理器类handler
package com.jack.webflux1.handler;
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;
/**
* create by jack 2018/4/21
*/
@Component
public class HelloWorldHandler {
/**
* ServerResponse 是对响应的封装,可以设置响应状态、响应头、响应正文。
* 比如 ok 代表的是 200 响应码、MediaType 枚举是代表这文本内容类型、返回的是 String 的对象。
这里用 Mono 作为返回对象,是因为返回包含了一个 ServerResponse 对象,而不是多个元素
* @param request
* @return
*/
public Mono<ServerResponse> helloWorld(ServerRequest request) {
return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN)
.body(BodyInserters.fromObject("Hello, World Flux!"));
}
}
2,编写router
2,编写router路由器
package com.jack.webflux1.router;
import com.jack.webflux1.handler.HelloWorldHandler;
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;
/**
* create by jack 2018/4/21
* 配置路由
*/
@Configuration
public class HelloWorldRouter {
/**
* RouterFunctions 对请求路由处理类,即将请求路由到处理器,这里将一个 GET 请求 /helloWorld 路由到处理器
* cityHandler 的 helloCity 方法上。跟 Spring MVC 模式下的 HandleMapping 的作用类似。
RouterFunctions.route(RequestPredicate, HandlerFunction) 方法,
对应的入参是请求参数和处理函数,如果请求匹配,就调用对应的处理器函数。
* @param helloWorldHandler
* @return
*/
@Bean
public RouterFunction<ServerResponse> routeHelloWorld(HelloWorldHandler helloWorldHandler) {
return RouterFunctions
.route(RequestPredicates.GET("/helloWorld")
.and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),
helloWorldHandler::helloWorld);
}
}
上面的代码就编写完成了,进行请求,返回Hello, World Flux!
三,测试
启动应用,使用postman进行测试,url为:http://localhost:8080/helloWorld
返回结果如下图:
源码地址:https://github.com/wj903829182/springcloud5/tree/master/webflux1
学习来源参考:http://gitbook.cn/gitchat/column/5acda6f6d7966c5ae1086f2b/topic/5acda9d9d7966c5ae1087053
欢迎加群:331227121,一起学习交流