我是2016年毕业的,在我毕业以前,我在学校里面学到的框架都是SSH,即struts+spring+hibernate,是的你没有看错,在大学里面的课本里面学的是strusts,这个还没毕业就被基本抛弃的框架。然而我大四出去实习,用的技术是SSM,即Spring,SpringMVC,Mybatis。实习的时候作的项目都是外包项目,很是传统的单体大项目,和学校里面作课程设计同样,全部的功能包括先后端都糅合在一个项目里面,根本不知道什么是分布式架构,不夸张的说,那个时候我对分布式这一块的知识无限趋近于零。spring
第一次接触到分布式的概念是我正式参加工做后,第一家公司属于一家互联网公司,作第三方支付。我甚至如今还记得加入这个公司以后,第一次在同事的帮助下,分别把支付服务和帐务服务的Demo,两个项目,在两个IDEA中运行起来,而后我在帐务服务打了一个断点,运行支付服务的测试用例,最后程序在帐务服务的断点处停了下来!程序停下来的时候,我仿佛感受看到了"神迹",颠覆了我前4年的大学学习中的固有印象!那个时候,我才知道了还有分布式这么一回事,才第一次接触到Dubbo,那个时候,程序猿的大门才向我徐徐打开,那个时候,我才知道,我一直在新手村待了4年。apache
你的一辈子中老是会碰到几个十分神秘的人,他们看起来或者仙风道骨,或者平平无奇,他们老是问你一些终极的问题,老是会引发你的思考,你也会无情的拒绝,由于你知道,这事,不靠谱,这些人就是“算命先生”,老是问你:"你从哪里来?你往哪里去?你60岁的时候会有一道坎,了解一下?"编程
我不知道个人前世,也没法预知本身此生还没发生的坎,可是我知道Dubbo从哪里来,往哪里去,了解一下?后端
出生豪门:api
2011 年 10 月 27 日,阿里巴巴开源了本身服务化治理方案的核心框架
Dubbo,服务治理的设计理念开始逐渐在国内软件行业中落地,并被普遍应用。自开源后,许多非阿里系公司选择使用 Dubbo。
半路夭折:服务器
2012 年 10 月 23 日,Dubbo 2.5.3 发布后,在 Dubbo 开源将满一周年之际,阿里基本中止了对 Dubbo
的主要升级。 2012 年 10 月 23 日,Dubbo 2.5.3 发布后,在 Dubbo 开源将满一周年之际,阿里基本中止了对
Dubbo 的主要升级。2013 年,2014 年,更新了 2 次 Dubbo 2.4 的维护版本,而后中止了全部维护工做。至此,Dubbo
对 Spring 的支持也停留在了 Spring 2.5.6 版本上。
同行续命:架构
阿里中止维护和升级 Dubbo 期间,当当网开始维护本身的 Dubbo 分支版本 Dubbox,新增支持了新版本的 Spring,支持了
Rest 协议等,并对外开源了 Dubbox。同时,网易考拉也维护了本身的独立分支 Dubbok,惋惜并未对外开源。
起死回生:框架
2017 年 9 月 7 日,Dubbo 悄悄在 GitHub 发布了 2.5.4 版本。随后,又迅速发布了
2.5.五、2.5.六、2.5.7 等版本。在 10 月举行的云栖大会上,阿里宣布 Dubbo 被列入集团重点维护开源项目,这也就意味着 Dubbo 起死回生,开始从新进入快车道。
回归正统:异步
2018 年 1 月 8 日,Dubbo 2.6.0 版本发布,新版本将以前当当网开源的 Dubbox 进行了合并,实现了 Dubbo
版本的统一整合。
走向巅峰:async
2018年2月,阿里巴巴宣布将Dubbo捐献给apache,进入apache孵化器。
2019 年 1 月,2.7.0 release 版本发布,这个即将毕业的 apache 版本支持了丰富的新特性,全新的 Dubbo Ops
控制台。时至 5 月,Dubbo 来到了 2.7.2 版本,期间积极引入了新的特性,支持 consul,nacos,etcd 等注册中心。
2019 年 5 月 21 号,通过了漫长的孵化期,Dubbo 迎来了毕业。成为Apache基金会顶级项目。
从Dubbo的历程能够看出,Dubbo的一辈子是坎坷的一辈子,虽然半路夭折,可是最后仍是走向了巅峰。不知道为何,这个时候我想起了马云爸爸说的一句话:
阿里说:我对Dubbo没有兴趣。由于我最快乐的时候,是当当网,帮我续命的时候!
颇有马云爸爸的气质,一脉相承,厉害厉害!
Dubbo2.7新特性包括但不限于以下几点:
1.异步化改造
2.三大中心改造
3.服务治理加强
本文主要分享Dubbo2.7新特征之一,异步化改造相关的内容。
此图是本文的核心,本文分享的内容基本上都是对于此图深刻到源码解级别的解析:
oneway 指的是客户端发送消息后,不须要接受响应。对于那些不关心服务端响应的请求,比较适合使用 oneway 通讯。可是请注意,返回值定义为void的并非oneway的调用方式,void表示的程序上不须要关心返回值,可是对Dubbo框架而言,仍是须要构建返回数据的。
仔细看oneway调用方式的图,能够看出:从客户端到服务端,只有req,没有resp;因此客户端不须要阻塞等待。
sync是最经常使用的通讯方式,也是Dubbo默认的通讯方法。
仍是仔细看sync调用方式的图,再想想你本身写的Dubbo应用,或者公司其余的Dubbo应用,是否是就是大家如今正在使用的通讯方式。客服端发起req请求到A服务端,而后在设置的超时时间内,一直等待A服务器的响应resp,这个时候,咱们说客户端处于阻塞的状态。当A服务器返回resp后,客户端才会继续运行。
future 和 callback 都属于异步调用的范畴,咱们放在一块儿讨论。
继续仔细看future和callback调用方式的图,能够看出他们的区别是:在接收响应时,future.get() 会致使线程的阻塞;callback 一般会设置一个回调线程,当接收到服务端响应时,自动执行,不会对当前线程形成阻塞。
有了前面的四种调用方式的简单介绍铺垫。咱们深刻到源码中一探究竟:
Dubbo2.6.0 --- DubboInvoke.doInvoke()方法
上图是Dubbo2.6.0版本DubboInvoke.doInvoke()方法的截图,先看个全局的代码。
其中的箭头解释一下:
箭头1:代表这段代码的版本,Dubbo2.6.0版本。
箭头2:判断调用方式是不是oneway模式,即有去无回调用。
箭头3:判断调用是不是否是async模式,即异步调用。
箭头4:既不是有去无回(oneway),也不是 异步调用(async),那么就是sync模式,即同步调用。
对于红框中的代码,放大以下:
Dubbo2.6.0 --- DubboInvoke.doInvoke()方法关键代码
接下来对关键代码进行解读:
Dubbo2.6.0 --- DubboInvoke.doInvoke()方法关键代码解读
1.首先,Dubbo是怎么判断调用方式是前面说的4种调用方式(对于Dubbo2.6.x来讲,实际上是3种,2.7.0以后才支持了callback的调用方式)的哪种的呢?
能够看到这两行代码:
boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);
boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
能够对着关键代码解读的图来看,这两行代码的用途,就是判断你的配置文件(注解的方式或者dubbo.xml)中有没有配置async=true或者return=true。
2.接下来咱们重点看一下我说的“最骚”的这一行代码:
"骚"在哪里?
是的,当是异步调用的时候,Dubbo把future放到RpcContext的上下文中,而后构造一个空的RpcResult给调用方,调用方再从上下文中把future取出来,须要用返回的值的时候调用一下future.get()方法。完成异步调用的操做。
同步调用的时候,dubbo也有拿到了这个future,可是并无返回,而是直接调用了future.get()方法,这就是同步调用。
综上:我认为同步调用和异步调用的区别就是谁去调用future.get()方法。若是是Dubbo调用则是同步调用,若是是客户端调用者是异步方法。
接下来,咱们看一下Alibaba提供给Apache的初始版本,即2.7.0版本中体现调用方式的关键代码。
Dubbo2.7.0 --- DubboInvoke.doInvoke()方法关键代码
朋友们能够先看左上角,确实是Dubbo2.7.0版本的代码。而后红框中圈起来的代码,看起来和Dubbo2.6.0版本中的差很少,那咱们就对比着看。
Dubbo2.6.0与Dubbo2.7.0 DubboInvoke.doInvoke()关键代码对比
看到这个地方的时候我曾经走了一点弯路,甚至走上了歧途,一度质疑Dubbo的这个地方的源码是有问题的,毕竟咱们搞技术的,就是一个大胆假设,当心求证吧。因此我给Dubbo提了一个issue.以下:
这里就不讲我走上歧途的过程了,后面有机会再分享。链接在这里,你们能够去看看,会不会被固化思惟给带偏了。Dubbo issus#4421
这里的两个回答,第一个解答了个人问题,我看了后恍然大悟,这题属于当局者迷旁观者清。
第二个回答,建议我看一下最新版本的代码,当时的最新版本的代码是2.7.3。因此我把2.7.3版本的代码拉了下来。
接下来,咱们就看看2.7.3中体现调用方式的关键代码,请各位朋友坐稳扶好,这里变化较大,车速较快,很是优秀。
Dubbo 2.7.3中体现调用方式的关键代码
首先咱们能够看到isOneway的判断仍是咱们熟悉的代码。可是这里只有一个if-else了。Dubbo调用有四种方式,if判断了isOneway,那么剩下的三种都在这个else里面啦。
看到这里,笔者冷静的思考了一下,剩下的三种调用方式,sync调用,future调用,callback调用。其中sync调用是默认的方式,没有在这个地方体现出来,那么直觉告诉我在某个地方必定有一个异步转同步的调用。因而乎,我发现了这样一个神奇的类:
AsyncToSyncInvoker方法调用
AsyncToSyncInvoker方法中的54行asyncResult.get(),其中asyncResult继承自Future,用源码说话:
Result继承了Future
接着咱们说说AsyncToSyncInvoker方法中的53行,getInvokeMode().
getInvokeMode()是RpcInvocation里InvokeMode的get方法。并且2.6.0里面RpcInvocation是没有invokeMode这个成员变量的。是2.7.0版本后新加的。
剩下的三种调用方式在InvokeMode里面
至此,基本圆满了。感谢大神指引我看最新版本的代码。
而后在上一个对比图:
Dubbo2.6.0/2.7.0/2.7.3 DubboInvoke.doInvoke()关键代码对比
1.dubbo.xml配置,加入async="true"
<dubbo:reference id="asyncService" interface="org.apache.dubbo.demo.api.AsyncService" async="true"/>
2.dubbo接口定义:
public interface AsyncService{ String sayHello(String name); }
3.异步调用,从RpcContext上下文中取出future,而后调用这个最"骚"的future.get()方法。还记得以前说的嘛:同步调用和异步调用的区别就是谁去调用future.get()方法。这里是客户端调用,因此是异步调用。
AsyncService.sayHello("hello"); Future<String> fooFuture=RpcContext.getContext().getFuture(); fooFuture.get();
有几个弊端:
1.不太符合异步编程的习惯,须要从一个上下文类中获取到 Future
2.若是多个异步调用,使用不当很容易形成上下文污染
3.Future 并不支持 callback 的调用方式
无需相关配置中进行特殊配置,显示声明异步接口便可:
public interface AsyncService{ String sayHello(String name); default CompletableFuture<String> sayHiAsync(String name){ return CompletableFuture.completedFuture(sayHello(name)); } }
使用callback方式处理返回值
CompletableFuture<String> future = asyncService.sayHiAsync("hi"); future.whenComplete((retValue, exception) ->{ if (exception == null) { System.out.println(retValue); } else { exception.printStackTrace(); } });
那么为何Dubbo2.7.0这样简单的几行代码就能实现异步化了呢?记住,源码之下无秘密:
完结撒花,谢谢你们!欢迎你们关注个人公众号。why技术