梦王爪哇昨晚做了一个梦。梦里有个白胡子老头,颇有仙气,对他说:“你的Java太弱了,连一个基本的功能都实现不了!”国王惊奇:“有什么作用?”我用Java算不出来?”老头子拿出两行代码:floatsalary=1000;floattax=salary*0.1;国王说:“这不是很正常吗?工资1000,税100,我们国家的小学生都能算出来。老人说:“如果我现在让工资=2000,税收等于多少?”“还是100!因为税还没有重新计算!”什么?”“这……”老者见国王不说话了,继续道:“你要在可变税和可变薪水之间建立关系,让它们就像Excel一样,一个单元格的值发生变化了,Excel公式自然会更新另一个单元格,使其相应地发生变化。”floatsalary=1000;//假设这条命令建立了tax和salary的关联floattax<=salary*0.1salary=2000;assertEquals(200,tax);//现在tax自动变成200国王说:“你有什么用这样做,再说了,这不是java的问题,所有的语言都有这个问题……”仙空老者没有回答,缓缓的消失了。发布-订阅模式第二天早上,国王将他的奇怪梦告诉了大臣们,看是否有人能帮助他理解。没想到大臣议论纷纷,说他也做了同样的梦。这就奇怪了,难道有神明故意把梦托付给大家?在梦境中,仙人老人举的例子,都是一模一样的。议事大臣小心翼翼地说:“他是不是在暗示我们国家的税收太高了,10%,应该降低?”国王瞪了他一眼:“胡说八道?我们的税一点都不高,点到5000了,除了五险一金的扣除,还有按揭等一系列政策。”减免、减免房租、减免独生子女、减免养老,怎么能叫高呢?”议会大臣很快就沉默了。IO大臣继续说道:“陛下,您不用担心,老夫说的问题,我们的Java已经提供了相应的解决方案,那就是发布订阅者模型。如果使用Salary作为数据源发布者,Tax作为一个订阅者,在Salary中注册,每当Salary有变化时,发送一个事件给Tax,Tax收到后,做相应的计算。”“这不就是一种持续向观察者推送数据的方式或者订阅者的模型吗,这种小儿科的东西有什么用?”老冤家线臣说。“在这种场景下确实没什么用,但这种事件流转的方式,如果处理得好,或许能解决大问题。”IO部长虽然不服气,但也想不出什么好的应用场景。高并发王见两个老家伙又要干活了,立马转移话题:“听说我们Java高并发遇到了一些问题?”过去二十年,我们的Java一直在用Tomcat的线程池方式,现在越来越差,难以应对高并发。”这一次,Tomcat的部长,线程的部长,以及连Servlet的部长都带进来了,国王暗自后悔。IO部长继续滔滔不绝地说:“现在的模型,每一个请求都会有一个线程来处理。如果请求涉及到IO或者网络操作,这个线程就得阻塞等待,无法做其他事情。目前没有办法对外提供服务。”确实是这个道理,基于Servlet的线程模型就是这样工作的。国王问:“为什么那个线程在调用RPC服务时要等待?让它去做其他的事情,比如处理另一个请求?”线程,如果某一点有IO调用,立即走开,去做其他事情。当IO调用结束的时候,可以通知线程去处理,这样我们就可以用少量线程实现大并发量。”王说:“艾青说的有道理,你已经可以实现这个NIO方法对不对?”“对了,我们现在要做的就是改造Tomcat,甚至替换Servlet!”,我是传统的工作方式,一个请求一个线程,如果我这样做,我的位置就不会了得到保证,于是他把目光投向了自己的忠实盟友线程部长,线程部长明白了:“IO部长的方法其实就是把同步阻塞调用改成异步非阻塞调用。这不是那么容易。别的不说,这种异步编程对我们的课题来说并不容易。你可以让臣民使用!”IO大臣有些不情愿。线程大臣笑道:“你只知道一件事,不知道另一件事。当你打电话给那个未来。阻止。”IO部长有些遗憾,他怎么忽略了这一层?线程部长乘胜追击:“还有,就算按照你说的,所有的操作都是异步的,事件驱动的,所以回调那里这段代码中会有很多回调地狱,你考虑过吗?”fun1(param,newCallback(){voidonSuccess(...){...执行业务逻辑...fun2(param,newCallback(){voidonSuccess(...){...执行业务逻辑...fun3(param,newCallback(){voidonSuccess(...){...执行业务逻辑...fun4(param,newCallback(){voidonSuccess(...){...执行业务逻辑...}voidonError(...){}});}voidonError(...){}});}voidonError(...){}});}voidonError(...){}});IO大臣看到这乱七八糟的代码,脑袋嗡的好大,这异步操作好变态啊!大王看到IO大臣神色不一样,不再说话,赶紧宣布他的撤退。在事件中非常沮丧的IO部长在法庭上流动并返回家中气呼呼的,下人给他送来的茶叶也被他打翻在地。工作人员已经知道了今天在法庭上发生的事情,上前道:“大人,您冷静点,我听说民间有一种叫做Reactor的东西,什么事件流和函数式编程,用高阶函数就可以解决这个回调地狱问题。”事件流?IO部长猛然惊醒。为什么我没有想到这个?仙女昨天不是托了我一个梦,指导我使用事件流吗?他连忙问道:“我们怎么办?”“我们用图片来表示一个事件流是这样的,在这个时间线上,还有Error事件和Complete事件,分别用来表示错误和完成,我就不画了。”明白了,但是什么使用它吗?”IO部长问道。“你可以用函数式编程来改造这个事件流,比如map,把事件从‘圆’变成‘三角’”“你也可以用filter来过滤事件流”“嗯,好像很清楚,我想在一个场景中,先调用函数1生成一个事件流,然后为事件流中的每个元素调用函数B生成一个新的事件流,怎么办?”IO部长问道。“阁下果然了不起,抽象思维能力极高!”工作人员适时拍了拍他的屁股,“这个时候可以用flatmap把新的事件流扁平化。”“map、filter、flatmap只是最基本的Operation,switch、take、merge、zip等操作符还有很多,你想要的功能都能满足!”“对对对,”IO部长兴奋的搓着手,他已经抓住了关键思路,Callbackhell是可以解决的。比如原来的需求是先异步调用fun1,然后根据fun1的结果调用fun2。只能这样写:fun1(param,newCallback(){voidonSuccess(...){fun2(param,newCallback(){voidonSuccess(...){...}voidonError(...){}});}voidonError(...){}});现在假设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帝国实现了反应式编程(ReactiveProgramming)!”奇怪!”“是的,这种方法是基于事件流和函数式编程的,可以让我们以非阻塞、异步的方式处理请求,也可以解决回调地狱的问题。”IO部长告诉Reactorking又过来了。“那这个Reactor怎么用?”“陛下还记得我们Java的高并发问题吗,就是无法有效管理异步和回调地狱导致的。”东西,献给陛下,它不使用Servlet,可以实现非阻塞IO,可以有效应对高并发。”IO大臣展示了一张图。Servlet部长看到脸都绿了:我的位置呢?雄猫大臣也觉得不自在。原来只有他一家,现在却被Netty排挤了。只有JDBC部长还在急:“用异步非阻塞处理一切?救救你自己,我访问数据库还被阻塞!”既然要实现异步非阻塞,那肯定是端到端的,全链路的实现,在某一点阻塞调用会造成全局的问题。但他仍然保持冷静:“不用担心,非阻塞JDBC驱动很快就会被民间开源社区生产出来。”大王看到这个新的SpringWebFlux要干掉好几个高官了,他也只好安抚Tomcat和Servlet:“嗯,新的东西肯定有一个逐步采用的过程,还是让SpringMVC和SpringWebFlux共存吧一会儿,让受试者根据自己的实际情况选择!”里面的例子来自:http://blog.leapoahead.com/2016/03/02/introduction-to-reactive-programming/我做了改编。【本文为专栏作家“刘欣”原创稿件,转载请通过作者微信获取授权公众号coderising】点此查看该作者更多好文
