前言

只有光头才能变强。

文本已收录至我的GitHub精选文章,欢迎Starhttps://github.com/ZhongFuCheng3y/3y

回顾一下上篇我对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支持三种数据源:

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

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

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

参考资料:

四、有必要学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精选文章,欢迎Starhttps://github.com/ZhongFuCheng3y/3y

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

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

学习WebFlux时常见的问题的更多相关文章

  1. 新手学习Python时常见的错误

    最近学习Python,现在把一些常见的错误总结如下: 1)忘记在 if , elif , else , for , while , class ,def 声明末尾添加 :(导致 "Synta ...

  2. 编写html页面时常见的问题(转)

    编写html页面时常见的问题(一)   说到写页面,肯定有很多人在刚接触编写页面这一块时遇到很多细节和兼容性的问题,那么在这里我总结一些经常遇到的小问题.希望能够帮助学习页面搭建的初学者! 虽然说ie ...

  3. python类,魔术方法等学习&&部分ssti常见操作知识点复习加深

    python类学习&&部分ssti常见操作知识点复习加深 在做ssti的模块注入的时候经常觉得自己python基础的薄弱,来学习一下,其实还是要多练习多背. 在python中所有类默认 ...

  4. 上四条只是我目前总结菜鸟们在学习FPGA时所最容易跑偏的地

    长期以来很多新入群的菜鸟们总 是在重复的问一些非常简单但是又让新手困惑不解的问题.作为管理员经常要给这些菜鸟们普及基础知识,但是非常不幸的是很多菜鸟怀着一种浮躁的心态来学习 FPGA,总是急于求成. ...

  5. jsp编写页面时常见错误提示

    jsp编写页面时常见错误提示 404-->未部署web应用 500-->代码有问题 无法显示网页-->未启动tomcat webRoot-->URL输入有误 web-inf-- ...

  6. 浅谈学习C++时用到的【封装继承多态】三个概念

    封装继承多态这三个概念不是C++特有的,而是所有OOP具有的特性. 由于C++语言支持这三个特性,所以学习C++时不可避免的要理解这些概念. 而在大部分C++教材中这些概念是作为铺垫,接下来就花大部分 ...

  7. 在学习泛型时遇到的困惑经常与func<T,U>混淆

    在学习泛型时遇到的困惑经常与func<T,U>混淆,总认为最后一个值是返回类型.现在区分一下,原来问题出在泛型委托上. C#委托的介绍(delegate.Action.Func.predi ...

  8. PHP编译安装时常见错误解决办法,php编译常见错误

    PHP编译安装时常见错误解决办法,php编译常见错误 1.configure: error: xslt-config not found. Please reinstall the libxslt & ...

  9. 浅谈区间DP的解题时常见思路

    一.区间DP解题时常见思路 如果题目中答案满足: 大的区间的答案可以由小的区间答案组合或加减得到 大的范围可以由小的范围代表 数据范围较小 我们这时可以考虑采用区间DP来解决. 那么常见的解法有两种: ...

随机推荐

  1. (十二)golang--进制和位运算

    1.基本进制 (1)二进制:0,1,满2进1 在golang中,不能直接使用一个二进制表示一个整数,可以用八进制.十进制和十六进制表示 (2)十进制:0-9,满10进1 (3)八进制:0-7,满8进1 ...

  2. 使用Typescript重构axios(二十五)——文件上传下载进度监控

    0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...

  3. npm start 的应用 改为forever 后台启动的方法记录

    记录日志的启动方式 forever start -l forever.log -o out.log -e err.log app.js 记录日志的启动方式 forever start -a app.j ...

  4. [UWP]为番茄钟应用设计一个平平无奇的状态按钮

    1. 为什么需要设计一个状态按钮 OnePomodoro应用里有个按钮用来控制计时器的启动/停止,本来这应该是一个包含"已启动"和"已停止"两种状态的按钮,但我 ...

  5. 安装cnpm遇到的问题

    安装 cnpm时,用git安装时,安装好node环境后,测试版本号node -v和npm -v都没问题,可以输出版本号,但是安装cnpm时,使用淘宝镜像安装后,会出现如下警告: 这个是提醒你安装的版本 ...

  6. ubuntu下minicom和usb串口转接

    ubuntu下minicom和USB转串口(转) (2013-03-23 21:07:54) 转载▼ 标签: it 分类: 嵌入式linux minicom是linux下串口通信的软件,它的使用完全依 ...

  7. Unity 横版2D移动跳跃问题——关于一段跳与二段跳

    1.初始条件: 1.角色只绑定一个碰撞体,移动时施加力或给予速度,用跳跃次数JumpTimes或者bool值OnGround判断是否在地面. 2.只用一个tilemap搭建2D场景,因此所有tilem ...

  8. powerdesigner连接Mysql进行反向工程并生成word文档图文教程

    1 软件版本 windows7 64位 powerdesigner 15.1 Mysql 5.1.56 mysql-connector-odbc-3.51.30-winx64 对于mysql-conn ...

  9. 力扣(LeetCode)查找常用字符 个人题解

    给定仅有小写字母组成的字符串数组 A,返回列表中的每个字符串中都显示的全部字符(包括重复字符)组成的列表.例如,如果一个字符在每个字符串中出现 3 次,但不是 4 次,则需要在最终答案中包含该字符 3 ...

  10. 【01】主函数main

    java和C#非常相似,它们大部分的语法是一样的,但尽管如此,也有一些地方是不同的. 为了更好地学习java或C#,有必要分清它们两者到底在哪里不同. 首先,我们将探讨主函数main. java的主函 ...