美文网首页Java 杂谈
dubbo源码怎么看

dubbo源码怎么看

作者: shysheng | 来源:发表于2019-07-27 22:09 被阅读4次

    从去年年中开始,断断续续看了一些dubbo源码。刚开始方式很简单,就是在网上找一些博主的文章跟着一起学,应该说看得很痛苦,说很多东西跟看天书一样也不为过。为什么会有这种感觉?现在回想起来,最大的原因可能是当时对dubbo并没有一个系统的了解,完全不知从何入手,而且网上的很多文章往往只是讲了其中一个点,我并不知道这个东西有什么用,为什么要这么写,以及各模块之间到底是个什么关系。

    现在回过头来看,如果当时能有一份类似dubbo学习进阶路线图的资料,也许这源码看起来会轻松很多。

    如果现在让我从头开始,我一定不会一开始就闷头扎到源码中去。首先我会从dubbo框架设计入手,从整体设计上对它有个印象。

    image

    然后具体到源码部分,一定会从SPI入手,了解自适应扩展机制,接下来会看服务导出和服务引用,到这里,基本上hello world的demo你就知道大概是怎么跑起来的了。

    当然知其然还要知其所以然,想更深入地了解其运行原理,还必须要了解其协议是怎么设计的,通信过程中的编解码、序列化都做了哪些工作。至此,简单的服务调用过程,你就有了基本的概念。

    image

    扩展到集群的场景,自然而然,你需要去了解多个生产者如何组成服务目录,消费者如何通过路由和负载均衡来选择生产者,以及相应的集群容错机制。

    image

    到这里就结束了吗?当然没有。在最新的2.7版本中,dubbo已经形成了三大中心的管理机制,即2.7之前就有的注册中心,以及在2.7中新增的元数据中心和配置中心,在dubbo进阶之路上,这三个中心你当然也需要有所了解。

    除此之外,dubbo也一直在演进,在上周参加的dubbo社区开发者大会上,小马哥说将来可能会去掉元数据中心和配置中心(如果我没听错的话),以及在现在很火的云原生方面,dubbo将何去何从?如果你对dubbo感兴趣,这些都是你可以关注的一些话题。

    这里给出一张自己简单整理的dubbo学习路径脑图吧。

    image

    当然,随着去年dubbo进入Apache孵化项目,官方文档上已经有了很多很有价值的资料,里面有很多dubbo commiter以及PMC成员的技术博客,甚至还有dubbo核心模块的源码解析(http://dubbo.apache.org/zh-cn/index.html),我相信这些资料可以帮助初学者更快地入门dubbo框架。

    另外,一直以来,我略感困惑的一点是,作为一款优秀的开源框架,为什么市面上一直缺少一本系统介绍dubbo、适合初学者入门和进阶的图书?那么现在,这个困惑可以消除了。因为dubbo PMC成员诣极刚刚出版了这样一本书,我上面提到的一切,这本书里基本都作了详细深入的讲解,干货满满。回想起来,在上一家公司,还跟本书作者诣极大牛有过几次交流,那时候只知道他是dubbo的commiter,如今已是dubbo PMC成员了,还出了这么一本好书,果然,比我优秀的人比我还努力。


    image

    题外话

    坊间一直有一种说法,说dubbo的代码写得不好,跟Spring等优秀的开源框架有不少差距。官方应该也看到这一点了,所以在dubbo重新开始维护之后,除了新特性的开发,官方也对已有功能的代码作了大量的重构和优化。这里以生成自适应扩展类代码的代码来略窥一二。

    重构之前的版本是这样的,5+个for循环,15+个if分支,嵌套深度 > 5,单个函数超过300行。

    private String createAdaptiveExtensionClassCode() {
    ...
            for (Method method : methods) {
               ...
                if (adaptiveAnnotation == null) {
                    ...
                } else {
                    ...
                    if (urlTypeIndex != -1) {
                        ...
                    }
                    else {
                        ...
                        if (attribMethod == null) {
                            ...
                    }
                    ...
                    for (int i = value.length - 1; i >= 0; --i) {
                        if (i == value.length - 1) {
                            if (null != defaultExtName) {
                                if (!"protocol".equals(value[i])) {
                                    if (hasInvocation) {
                                        ...
                                    } else {
                                        ...
                                    }
                                } else {
                                   ...
                                }
                            } else {
                                if (!"protocol".equals(value[i])) {
                                    if (hasInvocation) {
                                       ...
                                    } else {
                                        ...
                                    }
                                } else {
                                    ...
                                }
                            }
                        } else {
                            if (!"protocol".equals(value[i])) {
                                if (hasInvocation) {
                                    ...
                                } else {
                                   ...
                                }
                            } else {
                                ...
                            }
                        }
                    }
                   ...
                    for (int i = 0; i < pts.length; i++) {
                        if (i != 0) {
                           ...
                        }
                       ...
                    }
                   ...
                }
               ...
                for (int i = 0; i < pts.length; i++) {
                    if (i > 0) {
                        ...
                    }
                   ...
                }
                if (ets.length > 0) {
                   ...
                    for (int i = 0; i < ets.length; i++) {
                        if (i > 0) {
                           ...
                        }
                        ...
                    }
                }
               ...
            }
           ...
        }
    

    老实说,当初看到这段代码的时候,我也是一眼蒙圈,不敢相信这真的是dubbo的代码,可以说很好地诠释了如何写出让同事不好维护的代码这一反模式。

    好在今年年初,这段代码终于被重构了,重构之后的版本是这样的:

    public String generate() {
            // no need to generate adaptive class since there's no adaptive method found.
            if (!hasAdaptiveMethod()) {
                throw new IllegalStateException("No adaptive method exist on extension " + type.getName() + ", refuse to create the adaptive class!");
            }
    
            StringBuilder code = new StringBuilder();
            code.append(generatePackageInfo());
            code.append(generateImports());
            code.append(generateClassDeclaration());
    
            Method[] methods = type.getMethods();
            for (Method method : methods) {
                code.append(generateMethod(method));
            }
            code.append("}");
    
            if (logger.isDebugEnabled()) {
                logger.debug(code.toString());
            }
            return code.toString();
        }
    
    
        private String generateMethod(Method method) {
            String methodReturnType = method.getReturnType().getCanonicalName();
            String methodName = method.getName();
            String methodContent = generateMethodContent(method);
            String methodArgs = generateMethodArguments(method);
            String methodThrows = generateMethodThrows(method);
            return String.format(CODE_METHOD_DECLARATION, methodReturnType, methodName, methodArgs, methodThrows, methodContent);
        }
    

    整个过程被抽到了一个单独的类中,并按照类文件的格式被拆分成了十几个短小的方法,基本上每个方法不超过20行,这样的代码层次分明,一目了然,基本上从方法名上你就能知道他要干什么。看了下提交记录,这个重构是在今年2.11(大年初七)提交的,看来作者过年期间也没闲着😆

    当然,如果不是下面这段代码的存在,可以说这是一次教科书级别的重构。不过这段代码虽然分支也较多,但好在也只有40行,读起来也不是那么难理解。有意思的是,作者显然也意识到这个问题了,标了个巨大的// TODO: refactor it在这里😆

     private String generateExtNameAssignment(String[] value, boolean hasInvocation) {
            // TODO: refactor it
            String getNameCode = null;
            for (int i = value.length - 1; i >= 0; --i) {
                if (i == value.length - 1) {
                    if (null != defaultExtName) {
                        if (!"protocol".equals(value[i])) {
                            if (hasInvocation) {
                               ...
                            } else {
                               ...
                            }
                        } else {
                            ...
                        }
                    } else {
                        if (!"protocol".equals(value[i])) {
                            if (hasInvocation) {
                                ...
                            } else {
                                ...
                            }
                        } else {
                           ...
                        }
                    }
                } else {
                    if (!"protocol".equals(value[i])) {
                        if (hasInvocation) {
                            ...
                           
                        }
                    } else {
                        ...
                    }
                }
            }
    
            ...
        }
    

    相关文章

      网友评论

        本文标题:dubbo源码怎么看

        本文链接:https://www.haomeiwen.com/subject/svjyrctx.html