使用 Spring Webflux 进行异步非阻塞编程

Spring webflux,是在将要发布的Spring 5和Spring boot 2中提供的,结合非阻塞IO,Reactive 风格编程的异步非阻塞开发框架。

之前有一篇文章介绍了Vert.x,初次之外Java中还有Ratpack等以异步非阻塞编程为目标的项目。然而就目前来看,Spring Webflux将会是API设计最良好,最方便使用的一个。

Spring Webflux 介绍

Spring Webflux 是一个基于事件驱动的非阻塞实现,底层可以使用:

  1. Netty。
  2. 支持Servlet3.1 Non-Blocking Servlet标准的Web容器。具体的有Tomcat,Undertow,Jetty等。

默认使用的是Netty。毕竟Servlet整个生态都是针对阻塞IO的实现的,Async Servlet和Non-Blocking Servlet就是在Servlet标准中打的奇怪的格格不入的补丁。在性能上,Netty也有着不小的优势。

Spring Webflux 使用的Reactive System实现是Reactor,但是也支持使用RxJava,还有Java8 CompletableFuture。Reactor和RxJava2.0的实现接口基本一致,然而Reactor是基于Java8实现的,可以利用Java8中的许多既有实现(比如CompletableFuture,Stream等)。Reactor中最常使用的是Publisher的两个实现,Mono和Flux,Mono表示0或1,对应于RxJava中的MayBe,Completable,和Single;Flux表示1+数量,对应于RxJava中的Observable。

Spring Webflux 还提供了一个Netty实现的非阻塞WebClient,用来做Http 请求。

Spring Webflux 实例

我们这里完成一个和之前Vert.x一样功能的简单程序,使用HTTP请求网易新闻头条内容,然后抽取其中的文章标题,并以Json格式返回给客户端。项目使用Spring Boot开发。

@RestController
public class TopLinesHandler {

    @Resource
    private ObjectMapper mapper;

    @GetMapping("/top_lines")
    public Mono<Object> handleGetUserById() {
        return getTopLines().map(this::extractTitles);
    }

    private Mono<String> getTopLines() {
        WebClient webClient = WebClient.create("http://c.m.163.com");
        return webClient.get().uri("/nc/article/headline/T1348647853363/0-20.html")
                .accept(MediaType.APPLICATION_JSON)
                .exchange()
                .flatMap(resp -> resp.bodyToMono(String.class));
    }

    private List<String> extractTitles(String jsonStr) {
        JsonNode jsonNode;
        try {
            jsonNode = mapper.readTree(jsonStr);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
        JsonNode articles = jsonNode.get("T1348647853363");
        List<String> list = new ArrayList<>(articles.size());
        for (int i = 0; i < articles.size(); i++) {
            JsonNode article = articles.get(i);
            String title = article.get("title").textValue();
            list.add(title);
        }
        return list;
    }
}

可以看到,在webflux中,也可以使用SpringMVC中定义的注解,这大大简化了路由,response处理等工作。

然后我们需要启动一个Spring Webflux应用程序:

@SpringBootApplication
public class WebfluxApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder()
                .bannerMode(Banner.Mode.OFF)
                .sources(WebfluxApplication.class)
                .run(args);
    }
}

还是熟悉的配方,还是熟悉的味道。

当然,对于JDBC这种只有同步阻塞实现的,还是需要wrap到额外的线程池,以避免阻塞EventLoop,目前Spring Webflux还没有对这些做封装,需要的话只能自己动手了。

现在已经可以使用start.spring.io/方便的创建自己的Spring Webflux项目,注意SpringBoot要选2.0版本,Dependencies里加上Webflux。