学习WebFlux时常见的问题

2022-10-12,,

前言

只有光头才能变强。

文本已收录至我的github精选文章,欢迎star:https://github.com/zhongfucheng3y/3y

回顾一下上篇我对webflux的入门,如果没读过的同学建议读一下再来看本篇文章,上一篇文章花了我很多的心血~~

  • 外行人都能看懂的webflux,错过了血亏

开局再来一张图,内容全靠编:

这篇主要写写我初学时对webflux的一些疑问,不知道大家在看上一篇文章的时候有没有相应的问题呢?

这次学webflux主要的动力是公司组内分享,写了一个ppt,有需要的同学在我的公众号(java3y)下回复“ppt”即可获取。

一、本来就能实现异步非阻塞,为啥要用webflux?

相信有过相关了解的同学都知道,servlet 3.1就已经支持异步非阻塞了。

我们可以以自维护线程池的方式实现异步

  • 说白了就是tomcat的线程处理请求,然后把这个请求分发到自维护的线程处理,tomcat的请求线程返回
@webservlet(value = "/nonblockingthreadpoolasync", asyncsupported = true)
public class nonblockingasynchelloservlet extends httpservlet {

    private static threadpoolexecutor executor = new threadpoolexecutor(100, 200, 50000l, timeunit.milliseconds, new arrayblockingqueue<>(100));

    protected void doget(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception {

        asynccontext asynccontext = request.startasync();

        servletinputstream inputstream = request.getinputstream();

        inputstream.setreadlistener(new readlistener() {
            @override
            public void ondataavailable() throws ioexception {

            }
            @override
            public void onalldataread() throws ioexception {
                executor.execute(() -> {
                    new longrunningprocess().run();

                    try {
                        asynccontext.getresponse().getwriter().write("hello world!");
                    } catch (ioexception e) {
                        e.printstacktrace();
                    }
                    asynccontext.complete();

                });
            }

            @override
            public void onerror(throwable t) {
                asynccontext.complete();
            }
        });


    }

}

流程图如下:

上面的例子来源:

简单的方式,我们还可以使用jdk 8 提供的completablefuture类,这个类可以方便的处理异步调用。

protected void doget(httpservletrequest request,
                     httpservletresponse response) throws servletexception, ioexception {
    long t1 = system.currenttimemillis();

    // 开启异步
    asynccontext asynccontext = request.startasync();

    // 执行业务代码(dosomething 指的是处理耗费时间长的方法)
    completablefuture.runasync(() -> dosomething(asynccontext,
                                                 asynccontext.getrequest(), asynccontext.getresponse()));

    system.out.println("async use:" + (system.currenttimemillis() - t1));
}

要处理复杂的逻辑时,无论是回调或 completablefuture在代码编写上都会比较复杂(代码量大,不易于看懂),而webflux使用的是reactor响应式流,里边提供了一系列的api供我们去处理逻辑,就很方便了。

更重要的是:

  • webflux使用起来可以像使用springmvc一样,能够大大减小学习成本
  • webflux也可以使用functional endpoints方式编程,总的来说还是要比回调/completablefuture要简洁和易编写。

值得一提的是:

如果web容器使用的是tomcat,那么就是使用reactor桥接的servlet async api
如果web容器是netty,那么就是使用的netty,天生支持reactive

官方的推荐是使用netty跑webflux

二、webflux性能的问题

我们从上篇文章中就发现,浏览器去调用处理慢的接口,无论是该接口是同步的,还是说是异步的,返回到浏览器的时间都是一致的

  • 同步:服务器接收到请求,一个线程会处理请求,直到该请求处理完成,返回给浏览器
  • 异步:服务器接收到请求,一个线程会处理请求,然后指派别的线程处理请求,请求的线程直接空闲出来。

官网也说了:

reactive and non-blocking generally do not make applications run faster

使用异步非阻塞的好处就是:

the key expected benefit of reactive and non-blocking is the ability to scale with a small, fixed number of threads and less memory.that makes applications more resilient under load, because they scale in a more predictable way

好处:只需要在程序内启动少量线程扩展,而不是水平通过集群扩展。异步能够规避文件io/网络io阻塞所带来的线程堆积

下面来看一下针对相同的请求量,同步阻塞和异步非阻塞的吞吐量和响应时长对比:

注:

  • 请求量不大时(3000左右),同步阻塞多线程处理请求,吞吐量和响应时长都没落后。(按道理webflux可能还要落后一些,毕竟多做了一步处理-->将请求委派给另一个线程去做处理
  • 请求量大时,线程数不够用,同步阻塞(mvc)只能等待,所以吞吐量要下降,响应时长要提高(排队)。

spring webflux在应对高并发的请求时,借助于异步io,能够以少量而稳定的线程处理更高吞吐量的请求,尤其是当请求处理过程如果因为业务复杂或io阻塞等导致处理时长较长时,对比更加显著。

三、webflux实际应用

webflux需要非阻塞的业务代码,如果阻塞,需要自己开线程池去运行。webflux什么场景下可以替换springmvc呢?

  • 想要内存和线程数较少的场景
  • 网络较慢或者io会经常出现问题的场景

springmvc和webflux更多的是互补关系,而不是替换。阻塞的场景该springmvc还是springmvc,并不是webflux出来就把springmvc取代了。

如果想要发挥出webflux的性能,需要从dao到service,全部都要是mono和flux,目前官方的数据层reactive框架只支持redis,mongo等几个,没有jdbc

目前对于关系型数据库,pivotal团队开源出r2dbc(reactive relational database connectivity),其github地址为:

目前r2dbc支持三种数据源:

  • postgresql
  • h2
  • microsoft sql server

总的来说,因为webflux是响应式的,要想发挥出webflux的性能就得将代码全改成响应式的,而jdbc目前是没支持的(至少mysql还没支持),而响应式的程序不好调试和编写(相对于同步的程序),所以现在webflux的应用场景还是相对较少的。

所以,我认为在网关层用webflux比较合适(本来就是网络io较多的场景)

现在再回来看spring官网的图,是不是就更亲切了?

参考资料:

  • [https://blog.lovezhy.cc/2018/12/29/webflux%e6%80%a7%e8%83%bd%e9%97%ae%e9%a2%98/](https://blog.lovezhy.cc/2018/12/29/webflux性能问题/)

四、有必要学functional endpoints 编程模式吗?

前面也提到了,webflux提供了两种模式供我们使用,一种是springmvc 注解的,一种是叫functional endpoints

lambda-based, lightweight, and functional programming model

总的来看,就是配合lambda和流式编程去使用webflux。如果你问我:有必要学吗?其实我觉得可以先放着。我认为现在webflux的应用场景还是比较少,等真正用到的时候再学也不是什么难事,反正就是学些api嘛~

有lambda表达式和stream流的基础,等真正用到的时候再学也不是啥问题~

以下是通过注解的方式来使用webflux的示例:

以下是通过functional endpoints的方式来使用webflux的示例:

路由分发器,相当于注解的getmapping...

userhandler,相当于usercontroller:

五、webflux的实际使用场景

总的来说,因为webflux是响应式的,要想发挥出webflux的性能就得将代码全改成响应式的,而jdbc目前是没支持的(至少mysql还没支持),而响应式的程序不好调试和编写(相对于同步的程序),老项目也不太可能把依赖直接升上spring5.0,所以现在webflux的应用场景还是相对较少的(个人觉得)。

网关层用webflux比较合适(本来就是网络io较多的场景)

  • springcloud gateway是基于webflux实现的

最后

这次学webflux主要的动力是公司组内分享,写了一个ppt,有需要的同学在我的公众号(java3y)下回复“ppt”即可获取。

本已收录至我的github精选文章,欢迎star:https://github.com/zhongfucheng3y/3y

乐于输出干货的java技术公众号:java3y。公众号内有300多篇原创技术文章、海量视频资源、精美脑图,关注即可获取!

非常感谢人才们能看到这里,如果这个文章写得还不错,觉得「三歪」我有点东西的话 求点赞 求关注️ 求分享

《学习WebFlux时常见的问题.doc》

下载本文的Word格式文档,以方便收藏与打印。

  • 学习Linux,要把握哪些重点?
    学习Linux,要把握哪些重点?

    学习Linux,要把握哪些重点? 不知道有没有想学习Linux,但又把握不住学习重点,找不到合适的学习方法的小伙伴,反正我刚开始学习Linux时就像无头苍蝇似的“乱撞”,没有把握住学习重点,不知道怎么去学,差点要放...

    2023-08-07编程代码,,
  • shell学习总结
    shell学习总结

    shell教程 第一个shell脚本 打开文本编辑器(可以使用 vi/vim 命令来创建文件), 新建一个文件 test.sh,扩展名为 sh(sh代表shell) #!/bin/bash echo "Hello World !" (#!) 是一个约定的标记,它告诉系统这个...

    2023-08-01编程代码,
  • SwiftUI学习(一)
    SwiftUI学习(一)

    总览 如果你想要入门 SwiftUI 的使用,那 Apple 这次给出的官方教程绝对给力。这个教程提供了非常详尽的步骤和说明,网页的交互也是一流,是觉得值得看和动手学习的参考。 不过,SwiftUI 中有一些值得注意的细节...

    2023-08-01编程代码,
  • 学习 Linux,101: 使用基本 SQL 命令
    学习 Linux,101: 使用基本 SQL 命令

    概述 在本教程中,将学习结构化查询语言 (SQL),包括: 使用基本 SQL 命令 执行基本数据操作 本教程将简要介绍您需要知道的与 LPI 102 考试相关的 SQL 概念。   回页首 数据库和 SQL 在本系列教程中,目前我...

    2023-07-31编程代码,,
  • XV6学习(1) Lab util
    XV6学习(1) Lab util

    正在学习MIT的6.S081,把做的实验写一写吧。 实验的代码放在了Github上。 第一个实验是Lab util,算是一个热身的实验,没有涉及到系统的底层,就是使用系统调用来完成几个用户模式的小程序。 Boot xv6 (easy) 启...

    2023-07-31编程代码,,
  • MySQL 学习四 SQL优化
    MySQL 学习四  SQL优化

    MySQL逻辑架构:   第一层:客户端层,连接处理,授权认证,安全等功能。     第二层:核心层,查询解析,分析,优化,缓存,内置函数(时间,数学,加密),存储过程,触发器,视图     第三...

    2023-07-31编程代码,,
  • 再学习之Spring(依赖注入)
    再学习之Spring(依赖注入)

    一、概述 Spring框架是以 简化Java EE应用程序的开发 为目标而创建的。Spring可以实现很多功能,但是这些功能的底层都依赖于它的两个核心特性,也就是依赖注入和面向切面编程。几乎Spring所做的任何事情都可以追...

    2023-07-31编程代码,,
  • Java 系列之spring学习--依赖注入(二)
    Java 系列之spring学习--依赖注入(二)

    一、依赖注入的三种方式   接口注入,set注入,构造函数注入 二、构造函数注入   2.1、测试类 package test; public class test01 { public String msg=null; public test01(String msg) { System.out...

    2023-07-31编程代码,,