Spring WebFlux 要革了谁的命?

 mp.weixin.qq.com
托梦

Java国王昨晚做了一个梦。

梦中有个白胡子老头儿,颇有仙风道骨, 告诉他说:“你们Java啊,实在是太弱了,连一个基本的功能都实现不了!”

国王大为惊奇:“什么功能是我堂堂大Java搞不定的?”

老头儿展示了两行代码:

float salary = 1000;
float tax =  salary * 0.1;

国王说:“这不很正常吗,薪水(salary)是1000, 税(tax)等于100,我国小学生都能算出来。”

老头儿说:“我要是现在让salary=2000,那tax等于多少啊?”

“还是100! 因为tax没有被重新计算!”

“薪水变了,为什么税不变呢? ”

“这个......”

看到国王不说话了,老头儿继续说:“你们得建立变量tax和变量salary之间的关联,让他们像Excel一样,一个单元格的值变了,Excel的公式自然会更新另外一个单元格,让它随之变化。”

float salary = 1000;
//假设这个命令建立了tax和salary之间的关联
float tax <=  salary *0.1
salary = 2000 ;
assertEquals(200,tax);  //现在tax自动变成了200

国王说道:“你这么做有什么用啊,再说这也不是Java的问题,所有的语言都有这个问题啊......”

仙风道骨的老头儿没有回答, 慢慢消失了。

发布-订阅模式

第二天早朝,国王给大臣们讲了自己这个奇怪的梦,看看谁能帮自己解一解, 没想到大臣们七嘴八舌,说自己也做了同样的梦。

这就奇怪了,难道有神灵故意给大家托梦吗? 在梦中,仙人老头儿给大家举的例子都是一模一样的。

集合大臣小心翼翼地说道:“难道他是在暗示我国的税收太高了吗, 有10%,要降低一点?”

国王瞪了他一眼:“胡说些什么?我们的税一点都不高,起征点提高到了5000,除了五险一金扣除,我们还有房贷减免,租房减免,独生子女减免,赡养老人减免等一系列政策, 怎么能叫高呢?”

集合大臣赶紧噤声。

IO大臣继续说:“陛下不用烦恼,老头儿说的问题,我们Java早已经提供了对应的解决方案,那就是发布者-订阅者模式啊。如果把Salary当成一个数据源的发布者, 把Tax当成一个订阅者,注册到Salary当中, 每当Salary有变化,就发送一个事件给Tax,Tax收到后,做相应的计算就可以了。”

“这不就是一种把数据持续推送给观察者或者订阅者的一种模式嘛,这种小儿科的东西,有什么用?” 老对头线程大臣说道。

“在这个场景下确实没啥用处,但是这种事件流的方式,如果处理好了,也许能解决大问题。” IO大臣虽然不太服气,但是也想不出什么好的应用场景出来。

高并发

国王看到两个老家伙又要干起来,马上转移话题:“听说我们Java在高并发方面遇到了一点儿问题?”

IO大臣立刻兴奋起来,顺杆就爬:“没错,这二十来年,我们Java一直使用Tomcat那种线程池的方式,现在在越来越差劲了,难以应对高并发了。”

这一下子把Tomcat大臣,线程大臣,甚至Servlet大臣都给拉了进来,国王暗自后悔。

IO大臣继续侃侃而谈:“现在的模型每来一个请求都会有一个线程来处理,如果这个请求涉及到IO或者网络操作,这个线程不得不阻塞等待,没法干别的事情。”

“如果用户的请求太多,那线程池中的线程很快就会被用光。这时候就没办法对外提供服务了。”

这确实是实情,基于Servlet的线程模型,就是这么工作的。

国王问道:“那个线程调用RPC服务的时候,为什么要等待呢? 让它去干别的事情,比如处理另外一个请求不就可以了?”

“陛下圣明,这就是关键所在,要充分地利用线程,一点出现IO调用,立刻走开,去干别的事情,等到IO调用结束了,就可以通知线程去处理,这样我们用少量的线程,就可以实现大量的并发了。”

国王说:“爱卿言之有理,你已经可以实现这种NIO的方式了,对吧?”

“没错,现在我们要做的就是要改造Tomcat,改造甚至替换Servlet !”

回调地狱

Servlet大臣一听就急了,自己就是传统的工作方式,一个请求一个线程,要是这么搞,自己位置不保,他把目光投向了忠实的盟友线程大臣。

线程大臣心领神会:“IO大臣的办法其实就是把同步的阻塞调用改成异步的非阻塞调用,不是那么容易啊,别的不说,这异步编程,对我们臣民来说就很不容易。”

“你不是有什么Future,Callable之类的东西吗? 可以让臣民们去用啊!”  IO大臣不依不饶。

线程大臣笑了:“你是只知其一,不知其二,当你调用那个future.get()的时候,如果线程的工作(例如数据库查询)还没有做完,当前线程还得等待,还是阻塞的。”

IO大臣有点儿后悔,自己怎么忽略了这一层呢?

线程大臣趁胜追击:“还有啊,即使是按你所说,所有的操作都是异步的,都是事件驱动的,那回调会大量存在,这代码中回调地狱问题,你考虑了吗?”

fun1(param,new Callback(){
    void onSuccess(...){
        ......执行业务逻辑......
        fun2(param,new Callback(){  
            void onSuccess(...){
                ......执行业务逻辑......
                fun3(param,new Callback(){
                    void onSuccess(...){
                        ......执行业务逻辑......
                        fun4(param,new Callback(){
                            void onSuccess(...){
                                ......执行业务逻辑......                              
                            }
                            void onError(...){
                            }
                        });
                    }
                    void onError(...){
                    }
                });
            }
            void onError(...){
            }
        });
    }
    void onError(...){
    }
});

IO大臣看到这如同乱麻般的代码,头嗡的一声就大了,这异步操作居然这么变态!

国王看到IO大臣神色有异,不再说话,赶紧宣布退朝。

事件流

在朝堂上很郁闷的IO大臣怒气冲冲地回到了家,下人送上的茶水也被他打翻在地。

幕僚已经了解今日朝堂发生之事,走上前来:“大人息怒,小人听说民间有个叫做Reactor的东西,用什么事件流和函数式编程中的高阶函数,就能解决这个回调地狱问题。”

事件流? IO大臣突然被点醒了,我怎么没想起这茬儿, 昨天仙人托梦不就是引导我用事件流吗?

他赶紧问道:“具体是怎么做的?”

“我们用图来表示,一个事件流是这样的, 在这个时间线上,还有Error事件和Complete事件,分别用来表示出错和完成,我就不画了。”

“了解,可是这又有什么用呢?” IO大臣问道。

“可以使用函数式编程对这个事件流做变换,例如map,把事件从‘圆圈’ 变成了‘三角’”

“还可以用filter对事件流做过滤”

“嗯, 看起来很清楚,我想到一个场景, 先调用函数1,产生了事件流,然后对事件流中的每个元素,又要调用函数B,又产生了新的事件流,该怎么办? ”  IO大臣问道。

“大人真是厉害,抽象思维能力极高! ” 幕僚适时地拍了一下马屁,“这时候可以用flatmap,把新的事件流给平铺了。”

“map, filter, flatmap 仅仅是最基本的操作,还有switch , take, merge,zip等很多运算符,你想要的功能都能满足!”

“不错,不错,” IO大臣兴奋地直搓手,他已经把握住了其中的关键思想,回调地狱可以被解决了。

比如原来的需求是先异步调用fun1,  根据fun1的结果调用fun2, 只能这么写:

fun1(param,new Callback(){
    void onSuccess(...){     
        fun2(param,new Callback(){  
            void onSuccess(...){
                ......
            }
            void onError(...){
            }
        });
    }
    void onError(...){
    }
});

现在假设fun1 返回的是数据流,fun2返回的也是数据流,用这种新的方式,可以写成这样:

fun1(param)
.flatMap( e -> func2(e))
.subscribe(r -> showResult(r), 
      error -> handleError(error));

相当于把这一系列的回调给压平了!

IO大臣问道:“你刚才说的民间的那个软件叫什么来着? ”

“民间很多的,有RxJava, Reactor,要不要我把他们的负责人叫来聊聊?”

“慢着,光是这个Reactor, 用处不大,你把Spring大臣也请来,我们需要让Spring去使用Reactor,抛弃Servlet, 把所有的请求和处理都变成异步处理!”

新框架

三个月后,IO大臣喜气洋洋地向国王汇报:“陛下,臣已经解开了仙人所托的梦,那其实是让我Java帝国实现反应式编程(Reactive Programming)!”

“反应式编程? 这名字有点古怪!”

“对,这种方式是基于事件流和函数式编程的, 可以让我们用非阻塞的、异步的方式来处理请求,还能解决回调地狱的问题。”

IO大臣把Reactor给国王讲了一遍。

“那这个Reactor该如何使用? ”

“陛下还记得我们Java的高并发问题吧,就是由于没法有效地管理异步和回调地狱导致的, 现在好了,臣和Spring携手做了一个叫做Spring WebFlux的东西,献给陛下,它不用Servlet,可以实现非阻塞的IO,可以有效地应对高并发。 ” IO大臣展示了一幅图。

Servlet大臣一看,脸都绿了:我的位置在哪儿?

Tomcat大臣也觉得不爽,原来自己一家独大,现在被Netty给挤走了。

只有JDBC大臣还不慌不忙:“用异步非阻塞处理所有东西? 你省省吧,我这里访问数据库还是阻塞的呢!”

IO大臣心中暗叫不妙,怎么忘了JDBC这么重要的东西,既然想实现异步、非阻塞,那一定是端到端的,全链路的实现,某个点的阻塞调用都会导致整体出问题。

可他还是保持了镇静:“不用担心,民间的开源社区很快就会搞出来非阻塞的JDBC驱动的。”

国王看到这个新的Spring WebFlux简直是要革了好几个大员的命,也只好安抚一下Tomcat, Servlet:“这样吧,新事物总得有个渐进的采纳过程,我们让Spring MVC和Spring WebFlux 并存一段时间,让臣民们按照自己的实际情况来选择吧!”

后记:本文中托梦中的例子来源于:http://blog.leapoahead.com/2016/03/02/introduction-to-reactive-programming/  我做了改编。

Spring WebFlux 要革了谁的命?的更多相关文章

  1. Spring Webflux: Kotlin DSL [片断]

    原文链接:https://dzone.com/articles/spring-webflux-kotlin-dsl-snippets 作者:Biju Kunjummen 译者:Jackie Tang ...

  2. Spring WebFlux开门迎客,却来了一位特殊客人

    话说Spring WebFlux已经出现有一段时间了,但是知道他的人并不是很多.这让他很是闷闷不乐. 还有更惨的是,那些敢于吃螃蟹的人在尝试了他之后,有的竟把代码重新改回到Spring MVC的同步模 ...

  3. 爸爸又给Spring MVC生了个弟弟叫Spring WebFlux

    情景引入 很早之前,Java就火起来了,是因为它善于开发和处理网络方面的应用. Java有一个爱好,就是喜欢制定规范标准,但自己又不善于去实现. 反倒是一些服务提供商使用它的规范标准来制造应用服务器而 ...

  4. Spring WebFlux 响应式编程学习笔记(一)

    各位Javaer们,大家都在用SpringMVC吧?当我们不亦乐乎的用着SpringMVC框架的时候,Spring5.x又悄(da)无(zhang)声(qi)息(gu)的推出了Spring WebFl ...

  5. 基于Angular和Spring WebFlux做个小Demo

    前言 随着Spring Boot2.0正式发布,Spring WebFlux正式来到了Spring Boot大家族里面.由于Spring WebFlux可以通过更少的线程去实现更高的并发和使用更少的硬 ...

  6. Spring WebFlux, 它是一种异步的, 非阻塞的, 支持背压(Back pressure)机制的Web 开发WebFlux 支持两种编程风(姿)格(势) 使用@Controller这种基于注解

    概述 什么是 Spring WebFlux, 它是一种异步的, 非阻塞的, 支持背压(Back pressure)机制的Web 开发框架. 要深入了解 Spring WebFlux, 首先要了知道 R ...

  7. 朱晔和你聊Spring系列S1E5:Spring WebFlux小探

    阅读PDF版本 本文会来做一些应用对比Spring MVC和Spring WebFlux,观察线程模型的区别,然后做一下简单的压力测试. 创建一个传统的Spring MVC应用 先来创建一个新的web ...

  8. 【SFA官方翻译】Spring WebFlux和Spring Cloud进行响应式微服务开发

    源码,修正一些错误: https://github.com/bigben0123/sample-spring-cloud-webflux 原创 SpringForAll社区 2018-05-18 作者 ...

  9. 初识Spring Webflux

    Important to know is that there are two ways to use Spring Webflux. One using annotations, which is ...

随机推荐

  1. web.xml的<url-parttern>的匹配规则

    <url-parttern>顾名思义是对url(统一资源定位符)的一种匹配,是对“http:........."地址的匹配,但是会减去应用上下文,就是你的web程序的名字,如果你 ...

  2. Django学习之十一:真正理解Django的路由分发和反解url原理

    目录 URL Dispatcher 简介 模式概念 对比URLPattern 与 URLResolver (多态的体现) 构建子路由几种方式 反解url算法逻辑 URL Dispatcher 简介 d ...

  3. JavaScript是如何工作的:引擎,运行时和调用堆栈的概述!

    摘要: 理解JS执行原理. 原文:JavaScript是如何工作的:引擎,运行时和调用堆栈的概述! 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 本文是旨在深入研究JavaScrip ...

  4. 关于sass与VScode 一些配置 学习记录

    VScode三个关于sass的插件 .vscode-Sass------sass/scss文件语法提示.(sublime text也有) .vscode-Easy Sass------scss编译成c ...

  5. Dynamics CRM项目实例之九:CRM 2015的产品中的捆绑销售

    关注本人微信和易信公众号: 微软动态CRM专家罗勇,回复140或者20150112可方便获取本文,同时可以在第一时间得到我发布的最新的博文信息,follow me!      今天的博客主要是介绍Dy ...

  6. zList一个块状链表算法可以申请和释放同种对象指针,对于大数据量比直接new少需要差不多一半内存

    zList是一个C++的块状内存链表,特点: 1.对于某种类别需要申请大量指针,zList是一个很好的帮手,它能比new少很多内存. 2.它对内存进行整体管理,可以将数据和文件快速互操作 3.和vec ...

  7. java:数据结构(二)栈的应用(进制转换全收集)

    说到进制转换,java已经封装了基本的方法,在竞赛中使用封装的方法自然能节省大量时间 另一位仁兄介绍的封装好的方法: https://blog.csdn.net/m0_37961948/article ...

  8. mysqlbinlog 工具分析binlog日志

    MySQL的binlog 日志对于生产环境非常有用,任何时间对数据库的修改都会记录在binglog中:当数据发生增删改,创建数据库对象都会记录到binlog中,数据库的复制也是基于binlog进行同步 ...

  9. Go 定时任务

    本文基于Golang Crontab 实现了一个Crontab Job Manager.更加容易使用,同时也能够满足更加复杂的场景. 仓储地址, 如果有用,欢迎点赞,欢迎讨论,欢迎找茬. 需求 在开发 ...

  10. Visual Studio Code自定义快捷键(eclipse习惯)

    左下角设置按钮 -> Keyboard Shortcuts -> keybindings.json. [ { "key": "alt+/", &qu ...