dubbo源码解析-服务引用原理

前言

经过上一篇dubbo源码解析-简单原理、与spring融合的铺垫,我们已经能简单的实现了dubbo的服务引用.其实上一篇中的代码,很多都是从dubbo源码中复制出来,甚至有些类名,变量名都没改.那请问,我为什么要这么做?

我认为学习一个框架,无非就三个步骤.

  • 掌握基本使用
  • 看过源码,知道其中原理
  • 临摹源码,自己仿写一个简易的框架

其实大家都清楚,编程这东西,最关键是多动手.也就是,第三步才是最关键的.但是现实也是非常残酷的,绝大多数人都停留在第一步.光是第二步,都有些让人产生的心里恐惧.所以在写服务引用的时候,我就想到了小时候看纪晓岚的一个片段.当时红楼梦是禁书,纪晓岚为了让太后看红楼梦,就把红楼梦这个名字换成了石头记.这样太后自然就没有心里负担.我觉得用一个图来描述可能更贴切

当然临摹源码的这个过程,依肥朝拙见,也需要分为三个过程,分别是入门版(用最简单的代码表达出框架原理)、进阶版(加入设计模式等思想,在入门版的基础上优化代码)、高级版(和框架代码基本一致).

当然上一篇的入门版只是抛砖引玉,等整个dubbo源码解析系列完结之后,和大家一起临摹dubbo源码也在计划当中.当然更多后续进展关注肥朝即可.

插播面试题

  • 描述一下dubbo服务引用的过程,原理

  • 既然你提到了dubbo的服务引用中封装通信细节是用到了动态代理,那请问创建动态代理常用的方式有哪些,他们又有什么区别?dubbo中用的是哪一种?(高频题)

  • 除了JDK动态代理CGLIB动态代理外,还知不知道其他实现代理的方式?(区分度高)

原谅他

看源码对于大多数人来说,最难的一点莫过于"从源码的哪个地方开始看".虽然我之前数十篇dubbo源码解析都在回答这个问题,但是每发出一篇,都还是有小伙伴私信问我同样的问题.对此,我当然是选择"原谅他".因此,本篇我又再次粗暴式的点题,"怎么看源码".就把本篇来说,这个服务引用的原理,我们要从哪里开始看呢?我们一起看一下官方文档

如果你在上一篇中把我贴出来的demo都实现过一遍,再看到这个图,就不难总结出服务引用无非就是做了两件事

  • 将spring的schemas标签信息转换bean,然后通过这个bean的信息,连接、订阅zookeeper节点信息创建一个invoker

  • invoker的信息创建一个动态代理对象

温馨提示:除了看官方文档入手,在dubbo源码解析-服务暴露原理中我还提到了从输出日志入手.当然,我这里列举了两种方式只是给你提供参考,并不是说一共就只有这两种方式,也不是说,这两种就是最优的.

时序图

直入主题

有部分朋友反馈说代码贴图手机阅读不友好,但是如果不贴图的话,很多朋友看完文章自己debug的时候找相应的类和方法又要花费大量时间,所以折中一下,贴图和贴代码结合

public <T> Invoker<T> refer(Class<T> 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<String, String> 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 <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
    RegistryDirectory<T> directory = new RegistryDirectory<T>(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<String, String> map) {
    //......(省略部分代码)

    // 创建服务代理
    return (T) proxyFactory.getProxy(invoker);
}

我们知道,要封装这个通信细节,让用户像以本地调用方式调用远程服务,就必须使用代理,然后说到动态代理,一般我们就想到两种,一种是JDK的动态代理,一种是CGLIB的动态代理,那我们看看两者有什么特点.

JDK的动态代理代理的对象必须要实现一个接口,而针对于没有接口的类,则可用CGLIB.要明白两者区别必须要了解原理,之前反复强调,明白了原理自然一通百通.CGLIB其原理也很简单,对指定的目标类生成一个子类,并覆盖其中方法实现增强,但由于采用的是继承,所以不能对final修饰的类进行代理.

除了以上两种大家都很熟悉的方式外,其实还有一种方式,就是javassist生成字节码来实现代理(后面会详细讲,dubbo多处用到了javassist).那dubbo究竟用到了哪种方式实现代理呢?我们往下看

序号5的结束本篇也接近了尾声.本篇综合性较强,其中涉及到之前的内容本篇将不再重复提及,可根据注释中的标记自行查看.

写在最后

2017即将结束,这一年来,给我的一些感悟就是,任何事情无关大小,只要加上"坚持"二字,都会变得格外的不易.大家都知道,健身房主要赚的就是那些坚持不下去人的钱,我就有一个爱好健身的朋友,他能坚持每天健身,我也问过他"秘诀".他是这样说的

别人都是为了做别的事轻易就放弃健身,而我最常和别人说的话是,我要去健身了,不能聚会了

这个真实的例子,总结起来也就一句话时间在哪,成就就在哪.其实所谓门槛,能力够了就是门,能力不足就是坎.

期待下周的dubbo源码解析继续与你相遇.鉴于本人才疏学浅,不对的地方还望斧正

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,688评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,559评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,749评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,581评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,741评论 3 271
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,684评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,122评论 2 292
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,847评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,441评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,939评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,333评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,783评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,275评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,830评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,444评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,553评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,618评论 2 249