Java进阶-Dubbo-基础

一、Dubbo简介

  总体架构:

image.png

  分层结构:

image.png

  Dubbo核心领域模型:

  • Protocol是服务域,它是Invoker暴露和引用的主功能入口,它负责Invoker的生命周期管理
  • Invoker是实体域,它是Dubbo的核心模型,其它模型都向它靠扰,或转换成它,它代表一个可执行体,可向它发起invoke调用,它有可能是一个本地的实现,也可能是一个远程的实现,也可能一个集群实现;
  • Invocation是会话域,它持有调用过程中的变量,比如方法名,参数等。

  基本设计原则:

  • 采用Microkernel+Plugin模式,Microkernel只负责组装Plugin,Dubbo自身的功能也是通过扩展点实现的,也就是Dubbo的所有功能点都可被用户自定义扩展所替换;
  • 采用URL作为配置信息的统一格式,所有扩展点都通过传递URL携带配置信息。

  SPI(Service Provider Interface),是JDK内置的一个服务发现机制,它使得接口和具体实现完全解耦。我们只声明接口,具体的实现类在配置中选择

  服务暴露/消费者调用过程

image.png

框架设计:https://dubbo.apache.org/zh/docs/v2.7/dev/design/

二、Dubbo SPI

  通过ExtensionLoader的getExtensionLoader方法获取一个ExtensionLoader实例,然后再通过ExtensionLoader的getExtension方法获取拓展类对象。

public static  ExtensionLoader getExtensionLoader(Class type) {
    if (type == null) {
        throw new IllegalArgumentException("Extension type == null");
    } else if (!type.isInterface()) {
        throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
    } else if (!withExtensionAnnotation(type)) {
        throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
    } else {
        ExtensionLoader loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);
        if (loader == null) {
            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type));
            loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);
        }

        return loader;
    }
}
public T getExtension(String name) {
    if (name == null || name.length() == 0)
        throw new IllegalArgumentException("Extension name == null");
    if ("true".equals(name)) {
        // 获取默认的拓展实现类
        return getDefaultExtension();
    }
    // Holder,顾名思义,用于持有目标对象
    Holder holder = cachedInstances.get(name);
    if (holder == null) {
        cachedInstances.putIfAbsent(name, new Holder());
        holder = cachedInstances.get(name);
    }
    Object instance = holder.get();
    // 双重检查
    if (instance == null) {
        synchronized (holder) {
            instance = holder.get();
            if (instance == null) {
                // 创建拓展实例
                instance = createExtension(name);
                // 设置实例到 holder 中
                holder.set(instance);
            }
        }
    }
    return (T) instance;
}
 
 
创建拓展对象.png
private T injectExtension(T instance) {
    try {
        if (objectFactory != null) {
            // 遍历目标类的所有方法
            for (Method method : instance.getClass().getMethods()) {
                // 检测方法是否以 set 开头,且方法仅有一个参数,且方法访问级别为 public
                if (method.getName().startsWith("set")
                    && method.getParameterTypes().length == 1
                    && Modifier.isPublic(method.getModifiers())) {
                    // 获取 setter 方法参数类型
                    Class pt = method.getParameterTypes()[0];
                    try {
                        // 获取属性名,比如 setName 方法对应属性名 name
                        String property = method.getName().length() > 3 ? 
                            method.getName().substring(3, 4).toLowerCase() + 
                                method.getName().substring(4) : "";
                        // 从 ObjectFactory 中获取依赖对象
                        Object object = objectFactory.getExtension(pt, property);
                        if (object != null) {
                            // 通过反射调用 setter 方法设置依赖
                            method.invoke(instance, object);
                        }
                    } catch (Exception e) {
                        logger.error("fail to inject via method...");
                    }
                }
            }
        }
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
    }
    return instance;
}

三、SPI自适应扩展

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
    String[] value() default {};
}

  当Adaptive注解在类上时,Dubbo不会为该类生成代理类。注解在方法(接口方法)上时,Dubbo则会为该方法生成代理逻辑。Adaptive注解在类上的情况很少,在Dubbo中,仅有两个类被 Adaptive注解了,分别是AdaptiveCompiler和AdaptiveExtensionFactory。此种情况,表示拓展的加载逻辑由人工编码完成。更多时候,Adaptive是注解在接口方法上的,表示拓展的加载逻辑需由框架自动生成

public T getAdaptiveExtension() {
    // 从缓存中获取自适应拓展
    Object instance = cachedAdaptiveInstance.get();
    if (instance == null) {    // 缓存未命中
        if (createAdaptiveInstanceError == null) {
            synchronized (cachedAdaptiveInstance) {
                instance = cachedAdaptiveInstance.get();
                if (instance == null) {
                    try {
                        // 创建自适应拓展
                        instance = createAdaptiveExtension();
                        // 设置自适应拓展到缓存中
                        cachedAdaptiveInstance.set(instance);
                    } catch (Throwable t) {
                        createAdaptiveInstanceError = t;
                        throw new IllegalStateException("fail to create adaptive instance: ...");
                    }
                }
            }
        } else {
            throw new IllegalStateException("fail to create adaptive instance:  ...");
        }
    }

    return (T) instance;
}
private T createAdaptiveExtension() {
    try {
        // 获取自适应拓展类,并通过反射实例化
        return injectExtension((T) getAdaptiveExtensionClass().newInstance());
    } catch (Exception e) {
        throw new IllegalStateException("Can not create adaptive extension ...");
    }
}
获取自适应拓展.png

四、服务导出

  服务导出的入口方法是ServiceBean的onApplicationEvent。onApplicationEvent是一个事件响应方法,该方法会在收到Spring上下文刷新事件后执行服务导出操作

public void onApplicationEvent(ContextRefreshedEvent event) {
    // 是否有延迟导出 && 是否已导出 && 是不是已被取消导出
    if (isDelay() && !isExported() && !isUnexported()) {
        // 导出服务
        export();
    }
}

  ServiceBean是Dubbo与Spring框架进行整合的关键,可以看做是两个框架之间的桥梁。具有同样作用的类还有ReferenceBean。

  采用URL作为配置信息的统一格式,所有扩展点都通过传递URL携带配置信息。

public synchronized void export() {
    if (provider != null) {
        // 获取 export 和 delay 配置
        if (export == null) {
            export = provider.getExport();
        }
        if (delay == null) {
            delay = provider.getDelay();
        }
    }
    // 如果 export 为 false,则不导出服务
    if (export != null && !export) {
        return;
    }

    // delay > 0,延时导出服务
    if (delay != null && delay > 0) {
        delayExportExecutor.schedule(new Runnable() {
            @Override
            public void run() {
                doExport();
            }
        }, delay, TimeUnit.MILLISECONDS);
        
    // 立即导出服务
    } else {
        doExport();
    }
}
private void doExportUrls() {
    // 加载注册中心链接
    List registryURLs = loadRegistries(true);
    // 遍历 protocols,并在每个协议下导出服务
    for (ProtocolConfig protocolConfig : protocols) {
        doExportUrlsFor1Protocol(protocolConfig, registryURLs);
    }
}

  Invoker 是实体域,它是Dubbo的核心模型,其它模型都向它靠扰,或转换成它,它代表一个可执行体,可向它发起invoke调用,它有可能是一个本地的实现,也可能是一个远程的实现,也可能一个集群实现。

public  Invoker getInvoker(T proxy, Class type, URL url) {
    // 为目标类创建 Wrapper
    final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
    // 创建匿名 Invoker 类对象,并实现 doInvoke 方法。
    return new AbstractProxyInvoker(proxy, type, url) {
        @Override
        protected Object doInvoke(T proxy, String methodName,
                                  Class[] parameterTypes,
                                  Object[] arguments) throws Throwable {
            // 调用 Wrapper 的 invokeMethod 方法,invokeMethod 最终会调用目标方法
            return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
        }
    };
}
服务导出.png

五、服务引用

  Dubbo 服务引用的时机有两个:

  • 在 Spring容器调用ReferenceBean的afterPropertiesSet 方法时引用服务(饿汉式
  • 在ReferenceBean对应的服务被注入到其他类中时引用(懒汉式,默认

  先进行配置检查与收集工作。接着根据收集到的信息决定服务用的方式,有三种:

  • 第一种是引用本地 (JVM) 服务
  • 第二是通过直连方式引用远程服务
  • 第三是通过注册中心引用远程服务

  服务引用的入口方法为ReferenceBean的getObject 方法,该方法定义在Spring的FactoryBean接口中,ReferenceBean实现了这个方法。

public Object getObject() throws Exception {
    return get();
}

public synchronized T get() {
    if (destroyed) {
        throw new IllegalStateException("Already destroyed!");
    }
    // 检测 ref 是否为空,为空则通过 init 方法创建
    if (ref == null) {
        // init 方法主要用于处理配置,以及调用 createProxy 生成代理类
        init();
    }
    return ref;
}

  Invoker是Dubbo的核心模型,代表一个可执行体在服务提供方,Invoker用于调用服务提供类。在服务消费方,Invoker用于执行远程调用

public  Invoker refer(Class serviceType, URL url) throws RpcException {
    optimizeSerialization(url);
    // 创建 DubboInvoker
    DubboInvoker invoker = new DubboInvoker(serviceType, url, getClients(url), invokers);
    invokers.add(invoker);
    return invoker;
}
服务引用.png

你可能感兴趣的:(Java进阶-Dubbo-基础)