Spring 5 新增全新的reactive web框架:webflux

Spring 5发布了一个非常重要的模块,名字叫做:spring-webflux。该模块平级的就是spring-webmvc。

具体能做什么呢?自然是mvc不擅长的事情了。自然是人们一直希望实现,但总是比较困难的功能了。

先来看看flux是什么意思?

没错,是“流”的意思:stream, flux, rate, class, blast, grade。

具体做什么事情呢?就是webflux可以让你在web应用下也可以体验tcp长连接传输流数据的快感了。这在过去我们都是通过一些奇技淫巧才能实现的能力。

官方说法就是webflux是一个完全的reactive并且非阻塞的web框架。

什么是响应式编程?简单点说就是非阻塞,异步的而且是事件驱动的,只需要少量的线程,在一个jvm中垂直扩展而已,而不用通过集群的水平扩展方式。

webmvc与webflux

webmvc是servlet stack based,而webflux是reactive stack based。

Spring MVC的大名是响当当的,但是可能让你惊奇的是,居然没有给这个名字实际的项目或独立的分配。相反,它是Spring Framework中的一个模块,叫做spring-webmvc。这真的是一个让人觉得略怪的事情。居然不是一个顶级项目,而是在org.springframework.web.servlet下。好了,不纠结这些细节了。

Spring reative Web框架,是5.0中的新功能,是一个完全的reactive并且非阻塞的web框架。它适合处理那种event-loop 风格的事情,也就是事件驱动的。它支持Servlet容器(Tomcat,Jetty,Servlet 3.1+),也支持非Servlet的运行时(比如:Netty,Undertow),因为它的基础不是Servlet API,而是构建在Reactive Streams和Reactor项目之上的。

在5中,spring-web-reactive模块被改名为spring-webflux 。新模块中的顶级包是org.springframework.web.reactive。

哈哈,既然mvc就没有一个顶级的待遇,现在webflux也一样,都在web下,一个叫servlet,一个叫reactive。

你可以这样理解:就是servlet和reative是内部的真实情况,而webmvc和webflux则是为了迎合和抢占业界的一些主流概念。一个务实一个务虚。

SSE

另外还有一个概念就是SSE。就是Server-sent events的缩写。这是个什么鬼。其实就是个概念。或者是一个标准。就是把数据从web服务端传输到客户端的一种做法。顾名思义:服务端发送给客户端的事件。神奇吧。

你就先这么理解吧。一会我们会上代码,而且会具体演示效果。

实现和演示

铺垫

我们会先通过传统的webmvc的方式来实现reactive效果。然后我们会使用spring 5 的 webflux 再来实现一次reactive效果。

传统的实现方式

先通过spring initializr新建一个普通的spring boot应用。

pom

然后在pom中添加一个web依赖和一个integration-file依赖(稍后会用到):

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.integration</groupId>
   <artifactId>spring-integration-file</artifactId>
</dependency>

代码

/**
 * @author hezhuofan
 */
@SpringBootApplication
@RestController
public class RegularSseApplication {
    private final Map<String, SseEmitter> sses = new ConcurrentHashMap<>();

    @Bean
    public IntegrationFlow inboundFlow(@Value("${input-dir:file://${HOME}/Desktop/in}") File in) {
        System.out.println(in.getAbsolutePath());
        return IntegrationFlows.from(Files.inboundAdapter(in).autoCreateDirectory(true), poller -> poller.poller(spec -> spec.fixedRate(1000L)))
                .transform(File.class, File::getAbsolutePath).handle(String.class, (path, map) -> {
                    sses.forEach((key, sse) -> {
                                try {
                                    sse.send(path);
                                } catch (Exception ex) {
                                    throw new RuntimeException();
                                }
                            }

                    );
                    return null;
                })
                .channel(filesChannel())
                .get();
    }

    @Bean
    SubscribableChannel filesChannel() {
        return MessageChannels.publishSubscribe().get();
    }

    @GetMapping("/files/{name}")
    SseEmitter file(@PathVariable String name) {
        SseEmitter sseEmitter = new SseEmitter(60 * 1000L);
        sses.put(name, sseEmitter);
        return sseEmitter;

    }
        
    public static void main(String[] args) {
        SpringApplication.run(RegularSseApplication.class, args);
    }
}

基本思路

就是我们通过intergration file来把本地的一个指定目录作为一个流绑定到指定的url :/files/${name}。

我们在桌面新建一个in目录,in目录新建文件。这时候启动的web server端就会向客户端返回 该文件的 绝对路径,相当于通知给客户端。

演示

视频内容

左边是服务端,右边是客户端。服务端in目录下新建了文件,服务端检测到后,立马把新建的文件的绝对路径发送给了客户端。

新建文件命令:

touch fileName

客户端发送请求命令:

curl http://localhost:9995/files/spring

webflux 实现

pom

之前是web,现在换成了webflux。其他没变。

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.integration</groupId>
   <artifactId>spring-integration-file</artifactId>
</dependency>

值得注意的是,我们使用的是spring boot 2.0.0-SNAPSHOT版。因为flux是spring 5的内容。

<parent>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-parent</artifactId>
   <version>2.0.0.BUILD-SNAPSHOT</version>
   <relativePath/> <!-- lookup parent from repository -->
</parent>

代码

@SpringBootApplication
@RestController
public class SpringSamplesWebfluxApplication {

    @GetMapping(value="files/{name}",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    Flux<String> files(@PathVariable String name) {
        return Flux.create((FluxSink<String> sink) -> {
            FluxSink<String> serialize = sink.serialize();
            MessageHandler handler = msg -> serialize.next(String.class.cast(msg.getPayload()));
            serialize.setCancellation(() -> filesChannel().unsubscribe(handler));
            filesChannel().subscribe(handler);
        });
    }

    @Bean
    public IntegrationFlow inboundFlow(@Value("${input-dir:file://${HOME}/Desktop/in}") File in) {
        System.out.println(in.getAbsolutePath());
        return IntegrationFlows.from(Files.inboundAdapter(in).autoCreateDirectory(true), poller -> poller.poller(spec -> spec.fixedRate(1000L)))
                .transform(File.class, File::getAbsolutePath)
                .channel(filesChannel())
                .get();
    }

    @Bean
    SubscribableChannel filesChannel() {
        return MessageChannels.publishSubscribe().get();
    }

    public static void main(String[] args) {
        SpringApplication.run(SpringSamplesWebfluxApplication.class, args);
    }
}

演示

为了区分是不同的实现,我们使用了不同的端口。

视频内容

左边是服务端,右边是客户端。服务端in目录下新建了文件,服务端检测到后,立马把新建的文件的绝对路径发送给了客户端。

新建文件命令:

touch fileName

客户端发送请求命令:

curl http://localhost:8765/files/spring

总结

以上只是简单的介绍了下来龙去脉,并且对基于web的sse实现做了演示,以及对基于flux实现的演示。webflux是一个全新的reactive非阻塞web框架。与webmvc在同一个层次。对于微服务下的IO密集型的service来说,webflux也许是一个不错的尝试或选择吧。

原文发布于微信公众号 - ImportSource(importsource)

原文发表时间:2017-04-01

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏编程

Java开发必须要知道的知识体系

Java是超高人气编程语言,拥有跨平台、面向对象、泛型编程等特性。在TIOBE编程语言排行榜中,连续夺得第一宝座,而且国内各大知名互联网公司,后端开发首选语言:...

25590
来自专栏LeoXu的博客

Tapestry 教程(一) 原

这篇教程帮助人们来创建基于 Tapestry 的 web 应用程序。你是否有过使用 Tapestry 早期版本或者其它 web 框架的经历,这一点并不重要。事实...

17930
来自专栏流柯技术学院

性能测试之----瓶颈分析方法

内存分析需要使用的计数器:Memory类别和Physical Disk类别的计数器。内存分析的主要方法和步骤:

12420
来自专栏张善友的专栏

REST 入门介绍

dudu的 HttpClient + ASP.NET Web API, WCF之外的另一个选择 讨论的人很多,说明RESTful API也开始在.NET 社区中...

23880
来自专栏Java进阶干货

一说项目就spring,你真的懂spring么?

因此,你会发现,造一辆车需要层层嵌套零部件的生产逻辑而成,使得这家工厂需要面面俱到,而且要级级紧扣,每一个步骤都需要等待前一个步骤完成,这就造成这个家工厂效率低...

10910
来自专栏Java进阶架构师

这么说吧,Netty很简单,其实就是个Jar包,是作为通讯组件用的

极简教程,五分钟快速入门之netty,搭配后面netty实战以及netty源码分析

27820
来自专栏一个会写诗的程序员的博客

第1章 Spring Boot史前简述小结参考资料

大约20年前,程序员们使用“企业级Java Bean”(EJB)开发企业应用,需要配置复杂的XML。

13040
来自专栏Albert陈凯

2018-02-08 JAVA程序员必用JAR包

缺少经验的程序员往往可能想到自己去写个工具类来处理,这个想法当然是没有错的,但我们应尽可能去利用那些成熟的第三方库,来提高我们开发效率的同时保证代码性能与稳定!...

35550
来自专栏程序猿DD

Netflix Zuul与Nginx的性能对比

这是一篇翻译,关于大家经常质疑的一个问题:API网关Zuul的性能。 作者:STANISLAV MIKLIK 原文:NETFLIX ZUUL VS NGINX ...

58950
来自专栏java一日一条

调查:Java程序员最亲睐的Web框架

只有少数几种语言像Java一样提供了各种各样的web框架,上面的统计图就是一个证据。下面是其他开发者所使用web框架列表:

15910

扫码关注云+社区

领取腾讯云代金券