【Spring 5】响应式Web框架实战(上)

原创 2017年06月30日 00:31:36

引子:被誉为“中国大数据第一人”的涂子沛先生在其成名作《数据之巅》里提到,摩尔定律、社交媒体、数据挖掘是大数据的三大成因。IBM的研究称,整个人类文明所获得的全部数据中,有90%是过去两年内产生的。在此背景下,包括NoSQL,Hadoop, Spark, Storm, Kylin在内的大批新技术应运而生。其中以RxJavaReactor为代表的响应式(Reactive)编程技术针对的就是经典的大数据4V定义(Volume,Variety,Velocity,Value)中的Velocity,即高并发问题,而在即将发布的Spring 5中,也引入了响应式编程的支持。在接下来的几周,我会围绕响应式编程分三期与你分享我的一些学习心得。本篇是第三篇,通过一个简单的Spring 5示例应用,探一探即将于下月底发布的Spring 5的究竟。

前情概要:
- 【Spring 5】响应式Web框架前瞻
- 响应式编程总览

1 回顾

通过前两篇的介绍,相信你对响应式编程和Spring 5已经有了一个初步的了解。下面我将以一个简单的Spring 5应用为例,介绍如何使用Spring 5快速搭建一个响应式Web应用(以下简称RP应用)。

2 实战

2.1 环境准备

首先,从GitHub下载我的这个示例应用,地址是https://github.com/emac/spring5-features-demo

然后,从MongoDB官网下载最新版本的MongoDB,然后在命令行下运行mongod &启动服务。

现在,可以先试着跑一下项目中自带的测试用例。

./gradlew clean build

2.2 依赖介绍

接下来,看一下这个示例应用里的和响应式编程相关的依赖。

compile('org.springframework.boot:spring-boot-starter-webflux')
compile('org.springframework.boot:spring-boot-starter-data-mongodb-reactive')
testCompile('io.projectreactor.addons:reactor-test')
  • spring-boot-starter-webflux: 启用Spring 5的RP(Reactive Programming)支持,这是使用Spring 5开发RP应用的必要条件,就好比spring-boot-starter-web之于传统的Spring MVC应用。
  • spring-boot-starter-data-mongodb-reactive: Spring 5中新引入的针对MongoDB的Reactive Data扩展库,允许通过统一的RP风格的API操作MongoDB。
  • io.projectreactor.addons:reactor-test: Reactor(Spring 5默认使用的RP框架)提供的官方测试工具库。

2.3 示例代码

不知道你是否还记得,在本系列第一篇【Spring 5】响应式Web框架前瞻里提到,Spring 5提供了Spring MVC注解和Router Functions两种方式来编写RP应用。本篇我就先用大家最熟悉的MVC注解来展示如何编写一个最简单的RP Controller。

@RestController
public class RestaurantController {

    /**
     * 扩展ReactiveCrudRepository接口,提供基本的CRUD操作
     */
    private final RestaurantRepository restaurantRepository;

    /**
     * spring-boot-starter-data-mongodb-reactive提供的通用模板
     */
    private final ReactiveMongoTemplate reactiveMongoTemplate;

    public RestaurantController(RestaurantRepository restaurantRepository, ReactiveMongoTemplate reactiveMongoTemplate) {
        this.restaurantRepository = restaurantRepository;
        this.reactiveMongoTemplate = reactiveMongoTemplate;
    }

    @GetMapping("/reactive/restaurants")
    public Flux<Restaurant> findAll() {
        return restaurantRepository.findAll();
    }

    @GetMapping("/reactive/restaurants/{id}")
    public Mono<Restaurant> get(@PathVariable String id) {
        return restaurantRepository.findById(id);
    }

    @PostMapping("/reactive/restaurants")
    public Flux<Restaurant> create(@RequestBody Flux<Restaurant> restaurants) {
        return restaurants
                .buffer(10000)
                .flatMap(rs -> reactiveMongoTemplate.insert(rs, Restaurant.class));
    }

    @DeleteMapping("/reactive/restaurants/{id}")
    public Mono<Void> delete(@PathVariable String id) {
        return restaurantRepository.deleteById(id);
    }
}

可以看到,实现一个RP Controller和一个普通的Controller是非常类似的,最核心的区别是,优先使用RP中最基础的两种数据类型,Flux(对应多值)和Mono(单值),尤其是方法的参数和返回值。即便是空返回值,也应封装为Mono<Void>。这样做的目的是,使得应用能够以一种统一的符合RP规范的方式处理数据,最理想的情况是从最底层的数据库(或者其他系统外部调用),到最上层的Controller层,所有数据都不落地,经由各种FluxMono铺设的“管道”,直供调用端。就像农夫山泉那句著名的广告词,我们不生产水,我们只是大自然的搬运工。

2.4 单元测试

和非RP应用的单元测试相比,RP应用的单元测试主要是使用了一个Spring 5新引入的测试工具类,WebTestClient,专门用于测试RP应用。

@RunWith(SpringRunner.class)
@SpringBootTest
public class RestaurantControllerTests {

    @Test
    public void testNormal() throws InterruptedException {
        // start from scratch
        restaurantRepository.deleteAll().block();

        // prepare
        WebTestClient webClient = WebTestClient.bindToController(new RestaurantController(restaurantRepository, reactiveMongoTemplate)).build();
        Restaurant[] restaurants = IntStream.range(0, 100)
                .mapToObj(String::valueOf)
                .map(s -> new Restaurant(s, s, s))
                .toArray(Restaurant[]::new);

        // create
        webClient.post().uri("/reactive/restaurants")
                .accept(MediaType.APPLICATION_JSON_UTF8)
                .syncBody(restaurants)
                .exchange()
                .expectStatus().isOk()
                .expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8)
                .expectBodyList(Restaurant.class)
                .hasSize(100)
                .consumeWith(rs -> Flux.fromIterable(rs)
                        .log()
                        .subscribe(r1 -> {
                            // get
                            webClient.get()
                                    .uri("/reactive/restaurants/{id}", r1.getId())
                                    .accept(MediaType.APPLICATION_JSON_UTF8)
                                    .exchange()
                                    .expectStatus().isOk()
                                    .expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8)
                                    .expectBody(Restaurant.class)
                                    .consumeWith(r2 -> Assert.assertEquals(r1, r2));
                        })
                );
    }
}

创建WebTestClient实例时,首先要绑定一下待测试的RP Controller。可以看到,和业务类一样,编写RP应用的单元测试,同样也是数据不落地的流式风格。

在示例应用中可以找到更多的单元测试。

3 小结

以上就是Spring 5里第一种,相信也将会是最常用的编写RP应用的实现方式。介于篇幅原因,这篇就先到这里。下篇我将详细介绍第二种方式,Router Functions。欢迎你到我的留言板分享,和大家一起过过招。

4 参考

版权声明:本文为博主原创文章,未经博主允许不得转载。

【Spring 5】响应式Web框架实战(下)

可以看到,使用Router Functions实现RP应用时,你需要自己创建和管理容器,也就是说Spring 5并没有针对Router Functions提供IoC支持,这是Router Functi...
  • eMac
  • eMac
  • 2017年07月18日 01:23
  • 3571

【Spring 5】响应式Web框架前瞻

引子:被誉为“中国大数据第一人”的涂子沛先生在其成名作《数据之巅》里提到,摩尔定律、社交媒体、数据挖掘是大数据的三大成因。IBM的研究称,整个人类文明所获得的全部数据中,有90%是过去两年内产生的。在...
  • eMac
  • eMac
  • 2017年05月30日 23:43
  • 7771

Spring Framework 5.0的响应式微服务

Spring Framework 5.0的响应式微服务作者:chszs,未经博主允许不得转载。经许可的转载需注明作者和博客主页:http://blog.csdn.net/chszsSpring团队已经...
  • chszs
  • chszs
  • 2017年02月21日 17:05
  • 5010

为什么响应式编程并非一时之势?

本文作者为 David Buschman,文章从程序架构与系统的发展历程出发,逐步论证了为什么响应式编程并非一时之势,而是能带来更快处理速度,更高硬件利用率的未来选择。文章系国内 ITOM 管理平台 ...
  • wangpeng198688
  • wangpeng198688
  • 2016年06月08日 11:33
  • 1272

教你写响应式框架(一)

在真正开始编写自己的响应式框架之前,我们先来从观察者模式说起。已经对观察者模式很熟悉的可以直接掠过。基本概念观察者模式属于对象行为模式之一,也可叫做发布——订阅模式。它定义了一种以对多的依赖关系,让多...
  • dd864140130
  • dd864140130
  • 2016年03月13日 12:24
  • 8925

响应式框架推荐

最近有涉及到响应式布局相关的问题,在此希望能给大家提供一些参考的框架供大家参考。 对于用习惯了Bootstrap开发页面的程序员来说,很多成都上bootstrap就已经足够满足需求了,响应式Web设...
  • limm33
  • limm33
  • 2016年09月19日 12:00
  • 1636

25款最佳响应式HTML5前端开发框架

25款最佳响应式HTML5前端开发框架,供你挑选,一个好的界面不是梦。
  • qq_36305327
  • qq_36305327
  • 2017年05月21日 17:05
  • 1336

响应式WebUI框架整理

响应式Web框架整理汇总
  • qq_30051139
  • qq_30051139
  • 2017年06月21日 19:24
  • 280

是什么让spring 5放弃了使用Guava Cache?

一路走来,Spring社区从刚开始的核心模块一直发展到现在,最近Sping5也完成了M5的发布, 相信不久之后第一个RELEASE版本也会发布。里面有很多特性是和即将要发布的JAVA 9息息相关的。今...
  • qq_38398479
  • qq_38398479
  • 2017年04月24日 12:05
  • 854

Spring 5.0响应式编程

Mono 和 Flux对象,这些对象是实现反应式流规范中的 Publisher 接口的反应式类型。它们的职责是处理数据流。Mono 对象处理一个仅含 1 个元素的流,而 Flux 表示一个包含 N 个...
  • qq_28089993
  • qq_28089993
  • 2017年12月21日 20:04
  • 76
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:【Spring 5】响应式Web框架实战(上)
举报原因:
原因补充:

(最多只允许输入30个字)