此次文章介绍如何用Spring WebFlux来构建响应式REST API,在了解这个之前,必须先对系统的开发、传统REST的常见问题和API的普遍需求有一定的了解基础,我在网上找到了关于介绍传统应用和现代应用特点的分类图:
从图中可以看出,现代应用更偏向于分布式应用、云原生、高可用性和可扩展性,所以这就在系统有效资源利用上需要特别关注。
传统REST API请求处理的工作原理图:
从上面的工作原理可以分析得到:
- 无背压支持。传统的REST API是很难支持从客户端或服务器位置施加背压的,所以通常会造成负载过大的问题影响运行。这就意味着它不能支持短时间的大量请求,容易造成服务器崩溃,进而无法访问。
- 阻塞和同步。一般情况下,请求的线路释放会在I/O阻塞结束后,最后将响应返回给调用方。
- 单个请求的线程数。web容器会用到基于请求的线程模型,这个模型是存在影响API性能的,因为该模型会限制发请求的数量,让其成为一种“排队”的效果。
- 限制高并发用户的处理。正是因为上段提到的模型限制, 所以对于高并发量时无法处理。
- 对系统资源的浪费。I/O会造成线路阻塞,这就会让web容器没办法再处理更多的需求,对于系统资源的处理处于浪费阶段。
为什么响应式的优势可以解决上述问题:
- 异步无阻塞。响应式编程极大的为写异步和无阻塞应用程序提升了灵活性。
- 事件、消息驱动。系统可以对应活动产生相呼应的事件或者消息。打个比方,来自数据库的就会被看作事件流。
- 支持背压。通过背压,我们可以避免拒绝服务的出现,因为它可以很方便的处理掉一个系统到另一个系统的压力。
- 应用响应的时间可以预测。由于线程属于异步非阻塞,所以对于负载下应用的响应时间可以做到比较详细的预测。
- 系统资源利用率高。它可以支持更多的用户请求,因为其线路异步且非阻塞,因此各种线程不会被I/O所占用,其系统资源利用率就更高。
- 基于负载的扩容方式。
- 脱离基于请求的线程。借助于异步线程,基于请求的线程模型限制就可以得到脱离,因为模型会和服务器同时创建事件,并通过请求线程,处理其他请求。
了解了具体的优势,那么到底响应式编程流程是怎么样的?我在网上找到的图片解释的非常详细,如果应用数据调用了数据源获取数据的操作,就回立刻有一个线程返回,这是让自来该数据源的数据作为数据流出现。当数据流完成的时候,就是onComplete事件触发的时候。
当过程中出现了异常情况,则会出发onError事见。
有一些特殊的例子,比如删除一个来自数据库中的内容时,oncomplete/onError事件就会立即被触发,因为它本身没有数据可以返回来,所以调用onNext事件并不现实。
对上面部分了解之后,我们可以接着对背压进行了解并考虑如何将其应用到响应流。举个例子,假如说我们的客户端在向另一个服务请求数据,假设这个服务发布的速率可以达到1000TPS,但是客户端处理该应用的事件却很低。正是为了应对这种情况的产生,客户端需要利用缓冲来处理,也正因为如此,在缓冲过程中对于内存的占用会更大。这就造成了级联效应,造成其他应用程序出问题,为了避免此类情况,可以调用服务在末尾缓冲,然后让用客户端应用去推送事件,这就是背压。
之前看过的一篇关于响应流的文章写的很好,我此次简单介绍一下。先从发布者和订阅者开始说明:
发布者:拥有无限数量顺序元素的提供者称之为发布者,它可以根据订阅者要求发布。
订阅者:无限数量顺序元素相对应的使用者。
订阅:订阅者向发布者订阅的某个一对一的周期。
处理器:订阅者和发布者之间根据约定进行的处理阶段。
明确了上述的定义后,在说回响应规范流,比如常见的Project Reactor是其中的一种,它的优点在于可以实现无阻塞、高效的请求管理,也可以提供响应式可组合的API。这可组合的API广泛实现了响应式的拓展,而且提供了非阻塞的背压式网络引擎、TCP和UDP给HTTP,这对架构微服务是非常有效果的。
由于Reactor实施时大多数时候都需要涉及Spring 5.x,根据这个可以尝试通过Spring servlet栈的命令式编程来构架REST API。
在这里举例一个响应式REST API应用,在应用中使用到了带有WebFlux的Spring Boot、具有响应式支持的Spring数据和Cassandra数据库。
在构建响应式API的时候,我们可以不使用RestController,改为使用功能性样式编程模型来构建API,当然,你需要拥有router和handler组件。到这个地步,对公布响应式REST API已经了解了不少。