1. 什么是响应式编程
在计算机中, 响应式编程或反应式编程 (英语: Reactive programming) 是一种面向数据流和变化传播的编程范式. 这意味着可以在编程语言中很方便地表达静态或动态的数据流, 而相关的计算模型会自动将变化的值通过数据流进行传播.
例如, 在命令式编程环境中, a=b+c 表示将表达式的结果赋给 a, 而之后改变 b 或 c 的值不会影响 a . 但在响应式编程中, a 的值会随着 b 或 c 的更新而更新.
响应式编程是基于异步和事件驱动的非阻塞程序, 只需要在程序内启动少量线程扩展, 而不是水平通过集群扩展.
设想一个场景, 从底层数据库驱动, 经过持久层, 服务层, MVC 层中的 model, 到用户的前端界面的元素, 全部都采用声明式的编程范式, 从而搭建一条能够传递变化的管道, 这样我们只要更新一下数据库中的数据, 用户的界面上就相应的发生变化, 从而无需前端轮询才能获取到最新的数据.
简单来讲, 我们以前写的程序是阻塞式的, 当一个请求任务过来时, 线程会阻塞, 等到这个任务完成后再返回出去. 而响应式编程则是一个请求任务过来时, 会有其他的线程去做处理, 当任务执行结束后再异步的通知回去.
2. 为什么要使用响应式编程
在如今互联网时代的大背景下, web 应用通常要面对高并发, 海量数据的挑战, 性能从来都是必须要考量的核心因素.
阻塞便是性能杀手之一.
多数人不认为阻塞是一个比较大的问题, 至少觉得除了网络 I/O 之外, 读写文件和数据库还是很快的, 许多人也一直在写阻塞的代码.
那么 I/O 操作具体有多慢?
2.1 CPU 眼中的时间
以下内容来源 https://blog.csdn.net/get_set/article/details/79466402
CPU 绝对称得上是 "闪电侠", 因为它们做事都有自己的一套时钟. 我们故事的主人公是一个主频为 2.5GHz 的 CPU, 如果它的世界也有 "秒" 的概念, 并且它的时钟跳一下为一秒, 那么在 CPU(CPU 的一个核心)眼中的时间概念是啥样的呢?
CPU 先生所在的组是硬件部计算组. 对它来说, 与其一起紧密合作的几个小伙伴还能跟的上它的节奏:
CPU 先生很利索, 只需要一秒就可以完成一个指令, 复杂的动作可能需要多个指令.
好在 "贴身秘书" 一级缓存反应比较快, 能够秒懂 CPU 先生的意思.
来自 "秘书组" 的二级缓存虽然要十几秒才能 "get" 到 CPU 先生的点, 但也不算太迟钝.
和内存组的合作已经习以为常了, 跟内存请求的数据通常要 4-5 分钟才能找到(内存寻址), 不过也还好啦, 毕竟一级缓存那里能拿到 80% 想要的数据, 其余的二级缓存也能搞定一大部分, 不怎么耽误事儿.
CPU 先生是典型的工作狂, 任务多的时候, 通宵达旦也毫无怨言, 但是有什么事情让它等, 那简直要他命了. 恰恰一起共事的其他组 (尤其是 I/O 组的磁盘和网卡) 相对来说那效率是低的离谱啊:
关于 I/O 组的同事, CPU 先生已经抱怨很久了, 每次找 SSD 要东西, 都要花费 4-5 天才能找到(寻址), 等到数据传送过来, 几周都过去了. 机械磁盘更是过分地离谱, 跟他要个数据, 竟然要平均花费 10 个月才能找到, 如果要读取 1M 的数据, 竟然要 20 个月! 这种员工怎么还不下岗?!
关于网卡, CPU 先生知道它们也尽力了, 毕竟万兆网络成本颇高. 与机房内的其他小伙伴们用千兆网络互相沟通也算顺畅, 给另一台机器的 CPU 朋友发送 1K 的书信, 最快七八个小时就可以送过去了. 但是 1K 的书信经过层层包裹, 实际也写不了多少话. 更要命的是, 网卡们的沟通手续繁杂, 每次网络沟通前的 "你好能听到我吗?-- 我能听到, 你那边能听到我吗?-- 我也能听到你, 那我们开始吧!" 这样的握手确认都要花掉很长的时间, 不过不能当面沟通, 也只能这样了. 这还好, 最恐怖的是与其他城市的小伙伴沟通, 有时候传递消息要花费好几年呢!
由此可见, 对于 CPU 先生来说, 想要让工作充实起来实在不容易, 不过多亏了内存组的小伙伴帮忙分批缓存往返于 I/O 组的数据, 矛盾才有所缓解.
这个图只能明显看出涉及 I/O 的时间条, 我们转换成对数刻度的图看一下:
这个图并不是直观的比例, 横轴上每个刻度是一个数量级, 可见 I/O 的速度与 CPU 和内存相比是要差几个数量级的. 由此可见, 对于大型高并发场景下的 Web 应用, 缓存有多重要, 更高的缓存命中率就意味着性能.
并行化: 使用更多的线程和硬件资源;
异步化: 基于现有的资源来提高执行效率.
3. 基础概念
在介绍主题之前先普及几个概念:
3.1 Backpressure(背压)
背压是一种常用策略, 使得发布者拥有无限制的缓冲区存储元素, 用于确保发布者发布元素太快时, 不会去压制订阅者.
3.2 Reactive Streams(响应式流)
一般由以下组成:
发布者: 发布元素到订阅者
订阅者: 消费元素
订阅: 在发布者中, 订阅被创建时, 将与订阅者共享
处理器: 发布者与订阅者之间处理数据
3.3 Mono 和 Flux
Mono: 实现发布者, 并返回 0 或 1 个元素
Flux: 实现发布者, 并返回 N 个元素
4. 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 注解方式, 另一种是使用其功能性端点方式.
4.1 适用性
一图就很明确了, WebFlux 和 MVC 有交集. 但是注意:
MVC 能满足场景的, 就不需要更改为 WebFlux.
要注意容器的支持, 可以看看下面内嵌容器的支持.
微服务体系结构, WebFlux 和 MVC 可以混合使用. 尤其开发 IO 密集型服务的时候, 选择 WebFlux 去实现.
4.2 内嵌容器
跟 Spring Boot 大框架一样启动应用, 但 WebFlux 默认是通过 Netty 启动, 并且自动设置了默认端口为 8080. 另外还提供了对 Jetty,Undertow 等容器的支持. 开发者自行在添加对应的容器 Starter 组件依赖, 即可配置并使用对应内嵌容器实例.
但是要注意, 必须是 Servlet 3.1+ 容器, 如 Tomcat,Jetty; 或者非 Servlet 容器, 如 Netty 和 Undertow.
4.3 数据库
支持 reactive 编程的数据库只有 MongoDB , Redis , Cassandra , Couchbase .
4.4 快速上手
工程依赖
代码清单: spring-boot-webflux/pom.xml
- ***
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-webflux</artifactId>
- </dependency>
Service 类
代码清单: spring-boot-webflux/src/main/java/com/springboot/springbootwebflux/service/impl/UserServiceImpl.java
- ***
- @Service
- public class UserServiceImpl implements UserSerivice {
- private static Map<Long, User> map = new HashMap<>();
- static {
- map.put(1L, new User(1L, "www.geekdigging.com", 18));
- map.put(2L, new User(2L, "极客挖掘机", 28));
- }
- @Override
- public Mono<User> getUserById(Long id) {
- return Mono.just(map.get(id));
- }
- }
Controller 类
代码清单: spring-boot-webflux/src/main/java/com/springboot/springbootwebflux/controller/UserController.java
- ***
- @RestController
- public class UserController {
- @Autowired
- UserSerivice userSerivice;
- @GetMapping("/getUserById/{id}")
- public Mono<User> getUserById(@PathVariable Long id) {
- return userSerivice.getUserById(id);
- }
- }
通过上面的示例可以发现, 开发模式和之前 Spring MVC 的模式差别不是很大, 只是在方法的返回值上有所区别.
5. 示例代码
示例代码 - GitHub
示例代码 - Gitee
6. 参考
- https://blog.csdn.net/get_set/article/details/79466402
- http://www.ityouknow.com/springboot/2019/02/12/spring-boot-webflux.html
- https://www.cnblogs.com/limuma/p/9315442.html
来源: https://www.cnblogs.com/babycomeon/p/11683324.html