首页 > 编程知识 正文

spring dubbo(feign dubbo)

时间:2023-05-06 17:03:19 阅读:96972 作者:279

推荐学习

一举两得!阿里巴巴架构师,手记云开机微服务架构,被微服务炸了?不要害怕!为您提供35天的“微服务学习教程”

作为一个通用的RPC框架,性能非常重要,易用性和可扩展性也极其重要。

简单无创地扩展和定制RPC在各个阶段的功能是很多团队的需求,Dubbo满足了这些需求。

通过微内核设计和SPI扩展,一些有特殊需求的业务团队可以在不修改源代码的情况下,在Dubbo中实现自己的扩展。

Dubbo的成功离不开这个设计。今天我们就来看看Dubbo是如何实现非侵入式扩张的,我们也会看到Dubbo的IOC和AOP。

也有先开枪的。今天的内容代码有点多。毕竟要想深入分析,源代码是必不可少的。只是顺便提一下看源代码的一些小技巧。

所以建议在电脑上看得更清楚更舒服。

如果有没有看过源代码的同学,请跟着Dubbo系列走,这样就不怕面试官问他们有没有看过源代码了。

SPI

Dubbo使用SPI(服务提供商接口)实现扩展机制。

这个SPI我想大家都很熟悉,大学写数据库作业的时候就遇到过。您需要使用java.sql.Driver来访问数据库。

市场上有各种各样的数据库,每个数据库制造商都有自己的实现,所以我们肯定需要定制一个接口,以便我们可以为该接口编程。

具体实现可以通过配置加载,此时JDK SPI就派上用场了。

其实一点也不神奇,就是约定了一个地方,加载的时候会去那个地方找实现类。

简单来说,代码中写了一个目录,这个目录就是META-INF/services/。

然后在这个目录中创建一个文件,并用接口的完全限定名来命名它。文件的内容是实现类的完全限定名。

到了实现类的时候,根据接口名称在这里查看,然后实例化它。

很简单,这是JDK SPI,但是不符合Dubbo的需求。

因为Dubbo把自己的一些实现剥离成扩展,这些实现还是有点多,不需要全部使用。

如果使用JDK SPI,配置文件中的所有类都将被加载,这将导致资源浪费。当您使用它时,您需要遍历过去以找到相应的实现。

因此,Dubbo实现了一个基于JDK SPI的Dubbo SPI,可以根据需要按照指定的名称加载实现类。例如,集群中有如此多的实现类。

约定的地点已经改变了。有三个目录。

META-INF/Dubbo/internal/:这里是供Dubbo内部使用的SPI配置文件。META-INF/dubbo/:这是存储用户定义的SPI的地方。

配置文件。META-INF/services/:兼容 JDK SPI

然后文件里面的内容是key=value形式,这样就可以根据 key 找到对应的实现类。

然后在注解上可以配置默认的 key 来选择默认的实现类,比如 Cluster 默认的实现是 failover。

也可以通过 URL 参数来选择实现类。

还有像 JDK SPI 扩展点加载失败的话,连扩展点名称都拿不到,到时候报错也不知道哪里出问题。

而 Dubbo SPI 则不会吃了错误,并且还提供了扩展点的自动注入和 AOP 功能。

大致了解了 Dubbo SPI 之后,我们再来深入看看实现细节。

Dubbo SPI 实现细节

Dubbo SPI 的核心实现在 ExtensionLoader 中,它负责扩展点的加载和生命周期的维护,类似 JDK SPI 的 ServiceLoader。

这里要先提一点看源码的小技巧了。

开源框架都会有单元测试,而单元测试里面就会有我们看源码时候想要的各种功能实现,我们就可以从单元测试入手得知一些功能的划分,然后断点调试逐渐深入。

比如今天文章的 ExtensionLoader ,它在 dubbo-common 模块中,咱们就进入 test 来看看它测试用例怎么写的。

当然除了通过文件夹来找,直接用文件名搜也行。

找到了就好办了,数据都是造好的,找到你想要调试的方法,断点一设,箭头一点,这不就美滋滋了吗?

好了,小技巧分享完毕,回到 ExtensionLoader,我们简单点就用 Dubbo 单元测试的数据来看看实现。

有个叫 SimpleExt 的类,有三个实现,默认的实现是 impl1。

再来看看 SPI 配置文件的内容,可以看到为了测试还故意写了一些空格在配置文件中。

然后现在如果要找 impl2 这个实现,通过以下代码调用即可。

SimpleExt ext = ExtensionLoader .getExtensionLoader(SimpleExt.class).getExtension("impl2")

一个扩展接口对应有个 ExtensionLoader,找到对应的 ExtensionLoader,然后再加载对应名字的实现类。

接下来会有源码,不过没关系,还是很简单的,想要深入源码这关必须过。

可以看到getExtensionLoader 是静态的,里面逻辑也很简单就是从缓存找接口对应的 ExtensionLoader,找不到就新建一个返回。

现在有了 ExtensionLoader,咱们再来看看 getExtension 的逻辑,来看看是如何通过扩展点 name 找到对应的实现类的。

可以看到又是有个缓存操作,逻辑非常简单,先去缓存找实例,如果没有则创建实例。

要说细节就是用到了双检锁,然后用 holder 来保证可见性和防止指令重排。应该看到注释上的 holder 构造了吧,volatile 和双检锁的搭配,这里就不深入了。

我们来看看 createExtension,这是要创建扩展点了,代码有点长,但是我都做了相应的注释,包括绿色的注释。

逻辑还是很简单的,详细的代码没有具体展示,我先口述一下。

通过接口类名去三个目录找到对应的文件。解析文件内容生成 class 对象,然后缓存到 cachedClasses 中。然后通过 name 去 cachedClasses 中找到对应的 class 对象。去缓存 EXTENSION_INSTANCES 看看是否已经实例化过了。没有的话就实例化,然后调用 injectExtension 实现自动注入。再通过 cachedWrapperClasses 实现包装,将最后的包装类返回。

有几点不清晰没关系,咱们接着分析,脑海中先大概知道要做什么,然后再来看看具体是怎么做的。

源码中的 loadDirectory 就是去目录找文件,然后解析,最终会调用 loadClass,这个方法很关键,我们详细分析一下,为了便于观看,删除了一些代码。

自适应咱们先略过,只要知道是在这里记录的即可。

然后上面提到的 AOP 相关的 cachedWrapperClasses 就是在这里记录的,如果判断它是包装类呢?

简单粗暴但是有效,只要有当前类作为构造器参数的类就是包装类,有点拗口,多读几遍就理解了。

现在我们再回过头来看看这段代码,Dubbo 的 AOP。

把扩展类对应的包装类都记录下来放在 cachedWrapperClasses 中,然后在实例化扩展类的时候就一层一层的把扩展类包起来,最终返回的就是包装类。

为什么说这就是 AOP 呢?因为等于把一些逻辑切进了扩展实现类中。

其实就是把扩展对象的公共逻辑移到包装类中,我们看下单元测试的例子就很清晰了。

从图中可以看到有两个扩展实现类,两个包装类,具体逻辑就不看了,不是重点,配置文件如下:

然后再看一下单元测试的运行结果,可以看到最终返回的其实是 Ext5Wrapper1 对象,并且它还包着 wrapper2 对象。

所以 echo 方法的调用链就是:Ext5Wrapper1 ->Ext5Wrapper2->Ext5impl1

也就起到了 AOP 的效果。

接下来我们再来看看 injectExtension,是如何实现 Dubbo 的自动注入。

看了代码之后是不是有点失望,就这?

是的就是这么朴素地判断有没有 set 方法,然后根据参数找到对象,执行 set 方法注入即可。

所以说源码之下无秘密,看起来好像很高级的东西,就这。

上面代码中还有个objectFactory.getExtension(),这个和扩展自适应有关系,还有个@Activate也没说。

这些内容还是有点多的,也很重要,需仔细品味。

作者:yes的练级攻略

原文链接:https://www.cnblogs.com/yescode/p/14265476.html

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。