通过上一篇dubbo源码解析-简单原理、与spring融合的铺垫,咱们已经能简单的实现了dubbo的服务引用.其实上一篇中的代码,不少都是从dubbo源码中复制出来,甚至有些类名,变量名都没改.那请问,我为何要这么作?java
我认为学习一个框架,无非就三个步骤.面试
掌握基本使用spring
看过源码,知道其中原理编程
临摹源码,本身仿写一个简易的框架设计模式
其实你们都清楚,编程这东西,最关键是多动手.也就是,第三步才是最关键的.可是现实也是很是残酷的,绝大多数人都停留在第一步.光是第二步,都有些让人产生的内心恐惧.因此在写服务引用的时候,我就想到了小时候看纪晓岚的一个片断.当时红楼梦是禁书,纪晓岚为了让太后看红楼梦,就把红楼梦这个名字换成了石头记.这样太后天然就没有内心负担.我以为用一个图来描述可能更贴切架构
固然临摹源码的这个过程,依肥朝拙见,也须要分为三个过程,分别是入门版
(用最简单的代码表达出框架原理)、进阶版
(加入设计模式等思想,在入门版的基础上优化代码)、高级版
(和框架代码基本一致).框架
固然上一篇的入门版
只是抛砖引玉,等整个dubbo源码解析系列完结以后,和你们一块儿临摹dubbo源码也在计划当中.固然更多后续进展关注肥朝便可.分布式
描述一下dubbo服务引用的过程,原理源码分析
既然你提到了dubbo的服务引用中封装通讯细节是用到了动态代理,那请问建立动态代理经常使用的方式有哪些,他们又有什么区别?dubbo中用的是哪种?(高频题)性能
除了JDK动态代理
和CGLIB动态代理
外,还知不知道其余实现代理的方式?(区分度高)
看源码对于大多数人来讲,最难的一点莫过于"从源码的哪一个地方开始看".虽然我以前数十篇dubbo源码解析
都在回答这个问题,可是每发出一篇,都仍是有小伙伴私信问我一样的问题.对此,我固然是选择"原谅他".所以,本篇我又再次粗暴式的点题,"怎么看源码".就把本篇来讲,这个服务引用的原理,咱们要从哪里开始看呢?咱们一块儿看一下官方文档
若是你在上一篇中把我贴出来的demo都实现过一遍,再看到这个图,就不难总结出服务引用无非就是作了两件事
将spring的schemas标签信息转换bean,而后经过这个bean的信息,链接、订阅zookeeper节点信息建立一个invoker
将invoker
的信息建立一个动态代理对象
舒适提示:除了看官方文档入手,在dubbo源码解析-服务暴露原理中我还提到了从输出日志入手.固然,我这里列举了两种方式只是给你提供参考,并非说一共就只有这两种方式,也不是说,这两种就是最优的.
有部分朋友反馈说代码贴图手机阅读不友好,可是若是不贴图的话,不少朋友看完文章本身debug的时候找相应的类和方法又要花费大量时间,因此折中一下,贴图和贴代码结合
public Invoker refer(Class type, URL url) throws RpcException { url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY); //序号2,这里的逻辑和以前分享的'zookeeper链接'基本一致,不熟悉的能够回去看看 Registry registry = registryFactory.getRegistry(url); if (RegistryService.class.equals(type)) { return proxyFactory.getInvoker((T) registry, type, url); } // group="a,b" or group="*" Map qs = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY)); String group = qs.get(Constants.GROUP_KEY); if (group != null && group.length() > 0 ) { if ( ( Constants.COMMA_SPLIT_PATTERN.split( group ) ).length > 1 || "*".equals( group ) ) { return doRefer( getMergeableCluster(), registry, type, url ); } } return doRefer(cluster, registry, type, url); }
private Invoker doRefer(Cluster cluster, Registry registry, Class type, URL url) { RegistryDirectory directory = new RegistryDirectory(type, url); directory.setRegistry(registry); directory.setProtocol(protocol); URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, NetUtils.getLocalHost(), 0, type.getName(), directory.getUrl().getParameters()); if (! Constants.ANY_VALUE.equals(url.getServiceInterface()) && url.getParameter(Constants.REGISTER_KEY, true)) { registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY, Constants.CHECK_KEY, String.valueOf(false))); } //序号3,这里的逻辑和以前分享的'zookeeper订阅'基本一致,不熟悉的能够回去看看 directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY, Constants.PROVIDERS_CATEGORY + "," + Constants.CONFIGURATORS_CATEGORY + "," + Constants.ROUTERS_CATEGORY)); //序号4,cluster关键字在集群容错系列也提到过,不熟悉的能够回去看看 return cluster.join(directory); }
上面的这4步,就完成了schemas标签信息到invoker
的转换,那么下面就是建立代理对象了(序号5)
private T createProxy(Map map) { //......(省略部分代码) // 建立服务代理 return (T) proxyFactory.getProxy(invoker); }
咱们知道,要封装这个通讯细节,让用户像以本地调用方式调用远程服务,就必须使用代理,而后说到动态代理,通常咱们就想到两种,一种是JDK的动态代理
,一种是CGLIB的动态代理
,那咱们看看二者有什么特色.
JDK的动态代理
代理的对象必需要实现一个接口,而针对于没有接口的类,则可用CGLIB
.要明白二者区别必需要了解原理,以前反复强调,明白了原理天然一通百通.CGLIB
其原理也很简单,对指定的目标类生成一个子类,并覆盖其中方法实现加强,但因为采用的是继承,因此不能对final修饰的类进行代理.
除了以上两种你们都很熟悉的方式外,其实还有一种方式,就是javassist
生成字节码来实现代理(后面会详细讲,dubbo多处用到了javassist).那dubbo究竟用到了哪一种方式实现代理呢?咱们往下看
序号5的结束本篇也接近了尾声.本篇综合性较强,其中涉及到以前的内容本篇将再也不重复说起,可根据注释中的标记自行查看.
欢迎学Java和大数据的朋友们加入java架构交流: 855835163
加群连接:https://jq.qq.com/?_wv=1027&k=5dPqXGI 群内提供免费的架构资料还有:Java工程化、高性能及分布式、高性能、深刻浅出。高架构。性能调优、Spring,MyBatis,Netty源码分析和大数据等多个知识点高级进阶干货的免费直播讲解 能够进来一块儿学习交流哦