请问Dubbo的SPI机制是啥啊?

开发 架构
Dubbo还有一个很大的优点,就是采用的微内核+SPI扩展设计,这又是什么呢,这个可以很好的支持一些有特殊需求的三方的接入,可以自定义扩展,自主定制二次开发,良好的扩展性对于框架来说是很重要的。

[[423003]]

前言

之前大致的把Dubbo的运作流程简单的分析了一遍了,Dubbo还有一个很大的优点,就是采用的微内核+SPI扩展设计。

这又是什么呢,这个可以很好的支持一些有特殊需求的三方的接入,可以自定义扩展,自主定制二次开发,良好的扩展性对于框架来说是很重要的。

简单了解下SPI,全称为 Service Provider Interface,是一种服务发现机制。

它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。这一机制为很多框架扩展提供了可能,比如在Dubbo、JDBC中都使用到了SPI机制。

举个例子,比如你有个接口,现在这个接口有 3 个实现类,那么在系统运行的时候对这个接口到底选择哪个实现类呢?这就需要SPI了,需要根据指定的配置或者是默认的配置,去找到对应的实现类加载进来,然后用这个实现类的实例对象。

Java中JDK自身实现了SPI机制,基于策略模式来实现动态加载的机制 。我们在程序只定义一个接口,具体的实现交个不同的服务提供者;在程序启动的时候,读取配置文件,由配置确定要调用哪一个实现。

但是呢,存在一定的缺点,比如不能按照需要加载,会一次性加载所有可用的扩展点,很多是不需要的,会浪费系统资源;不支持AOP和依赖注入,实现类的方式也不够灵活,只能通过 Iterator 形式获取。

你不够强,或者说你做的不符合我的需求,我就替换你。

于是呢,dubbo重新实现了一套功能更强的 SPI 机制, 支持了AOP与依赖注入,并且 利用缓存提高加载实现类的性能,同时支持实现类的灵活获取。

Java中的SPI

Java中JDK自身实现了SPI机制,基于策略模式来实现动态加载的机制 。我们在程序只定义一个接口,具体的实现交个不同的服务提供者;在程序启动的时候,读取配置文件,由配置确定要调用哪一个实现。

首先,我们需要定义一个接口,SPIService。

  1. public interface SPIService { 
  2.     void execute(); 

然后,定义两个实现类,没别的意思,只输入一句话。‍

  1. public class SpiImpl1 implements SPIService{ 
  2.     public void execute() { 
  3.         System.out.println("SpiImpl1.execute()"); 
  4.     } 
  5.  
  6. public class SpiImpl2 implements SPIService{ 
  7.     public void execute() { 
  8.         System.out.println("SpiImpl2.execute()"); 
  9.     } 

最后呢,要在ClassPath路径下配置添加一个文件。文件名字是接口的全限定类名,内容是实现类的全限定类名,多个实现类用换行符分隔。内容就是实现类的全限定类名:

  1. com.tech.dayu.spi.SpiImpl1 
  2. com.tech.dayu.spi.SpiImpl2 

测试

  1. public class Test { 
  2.     public static void main(String[] args) {     
  3.         Iterator<SPIService> providers = Service.providers(SPIService.class); 
  4.         ServiceLoader<SPIService> load = ServiceLoader.load(SPIService.class); 
  5.  
  6.         while(providers.hasNext()) { 
  7.             SPIService ser = providers.next(); 
  8.             ser.execute(); 
  9.         } 
  10.         System.out.println("###################"); 
  11.         Iterator<SPIService> iterator = load.iterator(); 
  12.         while(iterator.hasNext()) { 
  13.             SPIService ser = iterator.next(); 
  14.             ser.execute(); 
  15.         } 
  16.     } 

两种方式的输出结果是一致的:

  1. SpiImpl1.execute() 
  2. SpiImpl2.execute() 
  3. -------------------------------- 
  4. SpiImpl1.execute() 
  5. SpiImpl2.execute() 

我们来看下源码,位于java.util包下。我们就以ServiceLoader.load为例,通过源码看看它里面到底怎么做的。

ServiceLoader.load()其实就是 Java SPI 入口

看到最后调用的是reload,最后生效的是在这个LazyIterator的内部,等同于是一个迭代器的遍历,遍历相应的文件中的service的实现类,就是我们上面命名的那些。

这里无论是if还是else最后调用的都是nextService()方法,点进去看

可以看到无非就是通过名字获取到文件路径,获取全限定名来加载类,并且创建其实例放入到相应的缓存之后并且返回实例,这大体就是整个的实现逻辑,应该不难吧,咱们自己来实现个这个应该也是分分钟的事

好了,Java的SPI源码分析的差不多了,问题也随之而来,比如不能按照需要加载,会一次性加载所有可用的扩展点,很多是不需要的,会浪费系统资源;不支持AOP和依赖注入,实现类的方式也不够灵活,只能通过 Iterator 形式获取

接下来咱们来分析Dubbo的SPI

Dubbo中的SPI

Dubbo 并未使用 Java SPI,而是重新实现了一套功能更强的 SPI 机制。

Dubbo SPI 的相关逻辑被封装在了 ExtensionLoader 类中,通过 ExtensionLoader,我们可以加载指定的实现类。Dubbo SPI 所需的配置文件需放置在 META-INF/dubbo 路径下。

Dubbo要判断一下,在系统运行时,应该选用这个Protocol接口的哪个实现类。它会去找一个你配置的Protocol,将你配置的Protocol实现类,加载进JVM,将其实例化,微内核,可插拔,大量的组件,Protocol负责RPC调用的东西,你可以实现自己的RPC调用组件,实现Protocol接口,给自己的一个实现类就可以啦

Dubbo里很多都是保留一个接口和多个实现,然后在系统运行的时候动态根据配置去找到对应的实现类。如果你没配置,那就走默认的实现就可以啦

我们随便来看一下其中的

并且 Dubbo SPI 除了可以按需加载实现类之外,增加了 IOC 和 AOP 的特性,还有个自适应扩展机制。

我们先来看一下 Dubbo 对配置文件目录的约定,不同于 Java SPI ,Dubbo 分为了三类目录。

  • META-INF/services/ 目录:该目录下的 SPI 配置文件是为了用来兼容 Java SPI 。
  • META-INF/dubbo/ 目录:该目录存放用户自定义的 SPI 配置文件。
  • META-INF/dubbo/internal/ 目录:该目录存放 Dubbo 内部使用的 SPI 配置文件。

接下来我们来看Dubbo的SPI的源码

在Dubbo中ExtensionLoader类似 Java SPI 中 ServiceLoader 的存在。大致流程就是先通过接口类找到ExtensionLoader ,然后再通过 ExtensionLoader.getExtension(name) 得到指定名字的实现类实例。

其实也是很简单的,就是通过一顿判断然后在缓存中检查是否存在这个类型的ExtensionLoader ,没有的话就新建一个放进去缓存,最后返回接口类的对应的ExtensionLoader

getExtension() 方法,从现象我们可以知道这个方法就是从类对应的 ExtensionLoader 中通过名字找到实例化完的实现类

内部的createExtension()方法,我就不截图了,比较长,就是先找实现类,判断是否有该类的缓存,没有的话就通过反射新建一个实例对象,然后放进去

到这里其实就差不多了分析的,拿到实例对然后就可以执行了

Dubbo的SPI主要就是为了增加框架的可拓展性,可以在其基础上进行二次开发,还有一个更重要的点就是不会像Java的SPI一样直接全部加载,那样可能会造成大量的资源浪费的,甚至可能还会做无用功

【编辑推荐】

 

责任编辑:姜华 来源: 大鱼仙人
相关推荐

2020-12-14 11:35:22

SPI Java机制

2020-06-30 15:35:36

JavaSPI代码

2011-11-30 14:35:19

JavaSPI

2024-01-15 08:25:53

SPI机制JavaDubbo

2023-12-11 07:21:12

SPI机制插件

2018-07-06 15:30:14

DubboSPIJDK

2020-08-18 08:04:16

DubboSPI框架

2022-08-17 08:17:01

SPI机制接口

2022-05-06 08:26:32

JavaSPI机制

2023-03-13 22:09:59

JavaSpring机制

2021-08-28 09:06:11

Dubbo架构服务

2023-08-28 10:42:25

DubboSPIJava

2021-05-30 07:54:24

SPI机制场景

2020-11-20 07:51:02

JavaSPI机制

2017-05-14 16:34:07

语言PLSQL

2020-08-17 07:50:09

阻塞CPU中断

2021-12-17 17:50:50

RTORPO场景

2023-04-12 08:00:34

Dubbo分布式服务

2021-04-20 10:20:27

Dubbo网络通信通信协议

2019-09-10 09:58:19

Dubbo负载均衡Hash
点赞
收藏

51CTO技术栈公众号