Dubbo 中的 SPI 机制(上)

简介: 本文主要讲述 Dubbo 中的 SPI 机制, 首先给大家带来一个简单的 Spring-Boot-Dubbo 案例,然后通过 Java 中的 SPI 的机制和讲解来引出 SPI 解决的问题,最后再通过一下几个方面讲述 Dubbo 中的 SPI 的设。Dubbo 中的 SPI 实现Dubbo 中的依赖注入Dubbo 中的 AOPDubbo 中的 Adaptive 机制版本说明:dubbo 2.7.8

Spring-Boot-Dubbo 简单案例


Spring Boot 整合 Dubbo 版本说明:

spring-boot 2.3.0.RELEASE

dubbo 2.7.8

demo 源码地址

Dubbo 更多功能的 资料地址


什么是 SPI 机制 ?


SPI 机制


在双亲委托模型下,类加载是由下而上的,即下层的类加载器会委托上层进行加载。Java 为了实现可拓展性在核心库中只定义了接口信息,而这些接口的实现可以由不同的三方厂家实现。但是 Java 的启动类加载器不会加载其它来源的 jar 包,这样就无法满足 Java 的拓展性需求。


针对这一个问题 Java 提供了线程上下文类加载器来解决这个问题, 他可以让父 ClassLoader 可以使用当前线程Thread.currentThread().getContextLoader(). 所指定的 ClassLoader 加载的类。这就改变了父 ClassLoader 不能使用 ClassLoader 或是没有直接父子关系的 ClassLoader 加载的类的情况,即改变了双亲委托模型。


这个整个打破双亲委派模型的类加载机制,就是 Java 的 SPI(Service Provider Interface) 机制。它有很多我们熟知的案例 JDBCSpring-BootDubbo


ServiceLoader 核心原理


这个整个过程


image.png


Java 中 SPI 的使用


  1. 定义接口和实现类


// 接口类
public interface Car {
    void start();
}
// 实现类
public class RedCar implements Car {
    private String color = "red";
    @Override
    public void start() {
        System.out.println(this.color + " car start!");
    }
}  


  1. 定义 META-INF/services/ cn.spi.Car 文件文件内容


cn.spi.RedCar


  1. 通过 SPI 获取实例对象


ServiceLoader<Car> cars = ServiceLoader.load(Car.class);
for (Car car : cars) {
  car.start();
}


  1. 获取代理类

Dubbo 中的 SPI


在 Dubbo 中 SPI 的流程是这样的首先 ExtensionLoader  是 Dubbo SPI 实现的核心类,它下面有连个核心方法 getExtensioncreateExtension 。在 createExtension 中完成对象的创建,在这个创建的过程中一共有以下几个步骤:


  1. 读取配置文件通过 name 参数找到实现类;
  2. 对实现类进行实例化;
  3. 依赖注入;
  4. AOP;
  5. 返回对象实例。


一. Dubbo 中的 SPI 实现


使用步骤


还是使用我们之前 Car 接口,下面是咱们有变化的部分


  1. 定义配置文件 META-INF/dubbo/ cn.spi.Car 文件内容


red=cn.spi.RedCar


  1. 获取对象实例


ExtensionLoader<Car> extensionLoader = ExtensionLoader
  .getExtensionLoader(Car.class);
Car car = extensionLoader.getExtension("red");


  1. 依赖注入实现代理类


//  定义
@SPI
public interface Car {
    void start();
    /**
     * 方法被代理需要两个步骤:
     * 1. 增加 @Adaptive 注解
     * 2. URL 参数,
     *
     * @param url 服务提供者的 URL
     * @return
     */
    @Adaptive
    String color(URL url);
}
// 使用
ExtensionLoader<Person> extensionLoader2 = ExtensionLoader.getExtensionLoader(Person.class);
Person person = extensionLoader2.getExtension("black");
URL url = new URL("http", "localhost", 8080); //代理逻辑
url = url.addParameter("car", "red");
person.getCar().color(url);


源码分析


getExtension 方法源码分析


// 获取实例
public T getExtension(String name, boolean wrap) {
  if (StringUtils.isEmpty(name)) {
    throw new IllegalArgumentException("Extension name == null");
  }
  if ("true".equals(name)) {
    return getDefaultExtension();
  }
  final Holder<Object> holder = getOrCreateHolder(name);
  Object instance = holder.get();
  if (instance == null) { // DCL
    synchronized (holder) {
      instance = holder.get();
      if (instance == null) {
        // 创建对象
        instance = createExtension(name, wrap);
        holder.set(instance);
      }
    }
  }
  return (T) instance;
}
// 创建实例
private T createExtension(String name, boolean wrap) {
  Class<?> clazz = getExtensionClasses().get(name);
  if (clazz == null) {
    throw findException(name);
  }
  T instance = (T) EXTENSION_INSTANCES.get(clazz);
  if (instance == null) {
    EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
    instance = (T) EXTENSION_INSTANCES.get(clazz);
  }
  // ....  ioc, aop
  return instance;
}
// 读取类
private Map<String, Class<?>> getExtensionClasses() {
  Map<String, Class<?>> classes = cachedClasses.get();
  if (classes == null) {
    synchronized (cachedClasses) {
      classes = cachedClasses.get();
      if (classes == null) {
        classes = loadExtensionClasses();
        cachedClasses.set(classes);
      }
    }
  }
  return classes;
}
// 加载类
private Map<String, Class<?>> loadExtensionClasses() {
  cacheDefaultExtensionName();
  Map<String, Class<?>> extensionClasses = new HashMap<>();
  // LoadingStrategy 策略接口,读取 SPI 目录下的类
  for (LoadingStrategy strategy : strategies) {
    loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
    loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache", "com.alibaba"), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
  }
  return extensionClasses;
}
// loadDirectory  读取目录查找文件
// loadResource 读取文件
// loadClass 加载类
// saveInExtensionClass 保存类信息到 Map


相关文章
|
7月前
|
存储 负载均衡 Dubbo
深入理解Dubbo-4.Dubbo扩展SPI
深入理解Dubbo-4.Dubbo扩展SPI
111 1
|
7月前
|
Dubbo Java 应用服务中间件
Dubbo服务暴露机制解密:深入探讨服务提供者的奥秘【九】
Dubbo服务暴露机制解密:深入探讨服务提供者的奥秘【九】
105 0
|
7月前
|
缓存 Dubbo Java
Dubbo 第三节_ Dubbo的可扩展机制SPI源码解析
Dubbo会对DubboProtocol对象进⾏依赖注⼊(也就是⾃动给属性赋值,属性的类型为⼀个接⼝,记为A接⼝),这个时候,对于Dubbo来说它并不知道该给这个属性赋什么值,换句话说,Dubbo并不知道在进⾏依赖注⼊时该找⼀个什么的的扩展点对象给这个属性,这时就会预先赋值⼀个A接⼝的⾃适应扩展点实例,也就是A接⼝的⼀个代理对象。在调⽤getExtension去获取⼀个扩展点实例后,会对实例进⾏缓存,下次再获取同样名字的扩展点实例时就会从缓存中拿了。Protocol是⼀个接。但是,不是只要在⽅法上加了。
|
1月前
|
负载均衡 监控 Dubbo
Dubbo 原理和机制详解(非常全面)
本文详细解析了 Dubbo 的核心功能、组件、架构设计及调用流程,涵盖远程方法调用、智能容错、负载均衡、服务注册与发现等内容。欢迎留言交流。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
Dubbo 原理和机制详解(非常全面)
|
4月前
|
负载均衡 Dubbo Java
Dubbo服务Spi机制和原理
该文章主要介绍了Dubbo中的SPI(Service Provider Interface)机制和原理,包括SPI的基本概念、Dubbo中的SPI分类以及SPI机制的实现细节。
Dubbo服务Spi机制和原理
|
7月前
|
设计模式 JSON Dubbo
超越接口:探索Dubbo的泛化调用机制
超越接口:探索Dubbo的泛化调用机制
421 0
|
7月前
|
Dubbo 网络协议 应用服务中间件
分布式微服务框架dubbo原理与机制
分布式微服务框架dubbo原理与机制
|
7月前
|
XML 缓存 Dubbo
Dubbo的魔法之门:深入解析SPI扩展机制【八】
Dubbo的魔法之门:深入解析SPI扩展机制【八】
91 0
|
7月前
|
XML 负载均衡 Dubbo
了解Dubbo配置:优先级、重试和容错机制的秘密【五】
了解Dubbo配置:优先级、重试和容错机制的秘密【五】
381 0
|
7月前
|
Dubbo Java 应用服务中间件
微服务框架(十五)Dubbo 超时机制及服务降级
此系列文章将会描述Java框架Spring Boot、服务治理框架Dubbo、应用容器引擎Docker,及使用Spring Boot集成Dubbo、Mybatis等开源框架,其中穿插着Spring Boot中日志切面等技术的实现,然后通过gitlab-CI以持续集成为Docker镜像。 本文为Dubbo超时机制及服务降级 当服务出现创建超时的时候,TimeoutFilter会打印该创建记录的详细信息,日志级别为WARN,即为可恢复异常,或瞬时的状态不一致
下一篇
DataWorks