Dubbo-自适应扩展机制之Adaptive注解原理

简介: Dubbo-自适应扩展机制之Adaptive注解原理

什么是自适应机制

上篇讲解Dubbo SPI机制的文章中提到,Dubbo里可以通过ExtensionLoader.getExtensionLoader(XXXClass).getExtension(key)的形式来获取接口的某个实现类。

但这种形式本质上还是通过硬编码的形式在代码中固定的获取了接口的一个实现,诸如Protocol(实现有Dubbo、Redis、Thrift等),或者Transporter(实现有Netty、Mina等)这些接口,我们是可以在Dubbo服务声明时指定具体实现的。如图所示:

那么Dubbo是如何实现根据用户指定的参数来找到对应的接口实现类的呢?

先给出结论:Dubbo接口在拓展方法被调用时,根据运行时参数(URL中参数)动态生成接口的Adaptive自适应拓展类,在生成的拓展方法中根据URL中用户指定的参数key,调用

ExtensionLoader.getExtensionLoader(XXXClass).getExtension(key)来获取具体的实现类


那么上面提到的运行时动态生成的拓展类代码是什么样的呢?以Protocol接口为例


先看下包含自适应方法的Protocol接口定义,export,refer是2个自适应的方法(用 @Adaptive注解修饰)

@SPI("dubbo")
public interface Protocol {
    int getDefaultPort();
    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
    @Adaptive
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
    void destroy();
}

Protocol接口生成的自适应扩展类,类名为Protocol$Adaptive,代码如下

package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
    public void destroy() {
        throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy()" +
                " of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
    }
    public int getDefaultPort() {
        throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol" +
                ".getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
    }
    public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {
        if (arg1 == null) throw new IllegalArgumentException("url == null");
        com.alibaba.dubbo.common.URL url = arg1;
        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
        if (extName == null)
            throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
        com.alibaba.dubbo.rpc.Protocol extension =
                (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
        return extension.refer(arg0, arg1);
    }
    public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
        if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
        if (arg0.getUrl() == null)
            throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
        com.alibaba.dubbo.common.URL url = arg0.getUrl();
        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
        if (extName == null)
            throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
        com.alibaba.dubbo.rpc.Protocol extension =
                (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
        return extension.export(arg0);
    }
}

普通方法getDefaultPortdestroy在自适应拓展类的实现中,直接抛出异常

自适应方法,如refer方法,内部基本实现思路有两点

  1. 从URL中获取接口实现extName
  1. 根据extName调用ExtensionLoader.getExtension(extName)获取实现类

先从URL中获取拓展类实现的名称extName(Protocal的默认SPI实现为Dubbo协议)

String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());

然后根据参数中的extName加载最终实现类,调用实现类的refer方法

com.alibaba.dubbo.rpc.Protocol extension =
                (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName)

看到这,可能大家对自适应机制已经有个大概的了解了,其关键就是自适应类的生成和Dubbo SPI中ExtensionLoader的使用。下面先从注解入手,开始分析自适应机制的原理。

@Adaptive注解

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

@Adaptive注解可以作用在类上、接口上。


1.作用在类上:在Dubbo中只有2个类上有Adaptive注解修饰,分别是 AdaptiveCompiler 和 AdaptiveExtensionFactory,表示拓展的加载逻辑由人工编码完成,即硬编码实现,举个例子,AdaptiveCompiler的comiple方法不需要在运行时根据URL中参数动态生成,直接代码中固定写死

20191028154055503.png

2.作用在接口上:如上文提到的,Adaptive注解作用在接口上时,能在程序运行时,根据参数(URL中参数)动态生成接口的Adaptive自适应拓展类,下面针对此种情况做具体分析

ExtensionLoader#getAdaptiveExtension

自适应拓展类加载时,调用的是ExtensionLoader#getAdaptiveExtension方法

自适应类相关缓存定义

//缓存的自适应扩展类实例
private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();
//缓存的自适应扩展类
private volatile Class<?> cachedAdaptiveClass = null;

代码如下,同样的先从缓存查询,没有再创建

public T getAdaptiveExtension() {
     //查询缓存
        Object instance = cachedAdaptiveInstance.get();
        //缓存中没有,双重检查锁定
        if (instance == null) {
            if (createAdaptiveInstanceError == null) {
                synchronized (cachedAdaptiveInstance) {
                    instance = cachedAdaptiveInstance.get();
                    if (instance == null) {
                        try {
                          //调用createAdaptiveExtension创建自适应拓展类的实例
                            instance = createAdaptiveExtension();
                            cachedAdaptiveInstance.set(instance);
                        } catch (Throwable t) {
                            createAdaptiveInstanceError = t;
                            throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
                        }
                    }
                }
            } else {
                throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
            }
        }
        return (T) instance;
    }

createAdaptiveExtension方法如下

private T createAdaptiveExtension() {
        try {
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        } catch (Exception e) {
            throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
        }
    }

做的事情比较简单,分为如下几步,重点是getAdaptiveExtensionClass方法

  1. 调用getAdaptiveExtensionClass方法获取自适应扩展类Class
  2. 调用反射创建自适应扩展类实例
  1. 为实例调用injectExtension注入其依赖的对象

getAdaptiveExtensionClass方法如下

private Class<?> getAdaptiveExtensionClass() {
     getExtensionClasses();
     if (cachedAdaptiveClass != null) {
         return cachedAdaptiveClass;
     }
     return cachedAdaptiveClass = createAdaptiveExtensionClass();
 }

分为两步

  1. getExtensionClasses获取所有接口的SPI扩展类实现,比如Protolcol接口的实现类有:

同时,如果Adaptive注解被修饰在类上,会将该类加入cachedAdaptiveClass缓存中,即上面提到的AdaptiveCompiler 和 AdaptiveExtensionFactory不会再动态生成自适应拓展类,直接返回缓存中的Class



2.第二步就是调用createAdaptiveExtensionClass方法创建自适应拓展类,代码如下

private Class<?> createAdaptiveExtensionClass() {

//动态生成自适应拓展类代码(字符串拼装)

String code = createAdaptiveExtensionClassCode();

ClassLoader classLoader = findClassLoader();

//调用JavaAssist,Jdk等编译器对生成的代码进行编译

com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();

return compiler.compile(code, classLoader);

}


如注释里说的,首先拼装出自适应拓展类的代码,再调用编译器编译代码,生成Class对象

自适应代码生成

createAdaptiveExtensionClassCode代码较长,但最终生成结果不复杂,Protocal接口生成的自适应拓展类代码Protocol$Adaptive已经在文章开头贴过了,就不重复贴了,只讲解代码中几个关键步骤

  1. 校验方法中是否有Adaptive注解,没有则抛出异常

  2. 导入package包信息

    实现类只依赖ExtensionLoader
  3. 普通方法不生成拓展方法

普通方法的拓展方法如下


public void destroy() {

throw new UnsupportedOperationException(“method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy()” +

" of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");

}


4.else逻辑中的实现即为具体自适应拓展方法的实现,具体实现有几个关键,一是根据URL及Adaptive中注解定义的key值获取具体的实现类的key值,如果Adaptive注解中没有指定value,则获取SPI注解中的默认值,什么意思呢?以Transporter#connect的自适应实现为例


优先从URL中获取Adaptive中指定value的拓展实现,获取不到时,再获取SPI中指定的默认实现

从URL获得了extName时,拼装出

ExtensionLoader.getExtensionLoader(XXXClass).getExtension(extName),达到根据参数配置来调用具体实现的效果,即如图所示代码

自适应代码编译

到上面为止,动态生成的代码已经有了,但是要在JVM里运行,光有字符串形式的代码是不够的,需要对代码进行编译处理

ClassLoader classLoader = findClassLoader();
        com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
        return compiler.compile(code, classLoader);

Compiler本身也是一个自适应实现,默认是JavaAssist的实现,具体实现在JavassistCompiler中,有兴趣的自己看,JavaAssist相关内容不在本文讨论范围

相关文章
|
2月前
|
负载均衡 Dubbo 应用服务中间件
Dubbo服务调用过程原理
该文章主要介绍了Dubbo服务调用过程的原理,包括服务调用的主要阶段和服务调用的具体步骤。
Dubbo服务调用过程原理
|
2月前
|
缓存 Dubbo Java
Dubbo服务消费者启动与订阅原理
该文章主要介绍了Dubbo服务消费者启动与订阅的原理,包括服务消费者的启动时机、启动过程以及订阅和感知最新提供者信息的方式。
Dubbo服务消费者启动与订阅原理
|
2月前
|
Dubbo 网络协议 Java
深入掌握Dubbo服务提供者发布与注册原理
该文章主要介绍了Dubbo服务提供者发布与注册的原理,包括服务发布的流程、多协议发布、构建Invoker、注册到注册中心等过程。
深入掌握Dubbo服务提供者发布与注册原理
|
2月前
|
负载均衡 Dubbo Java
Dubbo服务Spi机制和原理
该文章主要介绍了Dubbo中的SPI(Service Provider Interface)机制和原理,包括SPI的基本概念、Dubbo中的SPI分类以及SPI机制的实现细节。
Dubbo服务Spi机制和原理
|
2月前
|
缓存 负载均衡 Dubbo
Dubbo服务集群容错原理(重要)
该文章主要介绍了Dubbo服务集群容错的原理,包括集群容错技术的概念、Dubbo中使用的集群容错技术种类及其原理。
|
5月前
|
Dubbo Java 应用服务中间件
微服务学习 | Springboot整合Dubbo+Nacos实现RPC调用
微服务学习 | Springboot整合Dubbo+Nacos实现RPC调用
|
2月前
|
Dubbo Java 应用服务中间件
💥Spring Cloud Dubbo火爆来袭!微服务通信的终极利器,你知道它有多强大吗?🔥
【8月更文挑战第29天】随着信息技术的发展,微服务架构成为企业应用开发的主流模式,而高效的微服务通信至关重要。Spring Cloud Dubbo通过整合Dubbo与Spring Cloud的优势,提供高性能RPC通信及丰富的生态支持,包括服务注册与发现、负载均衡和容错机制等,简化了服务调用管理并支持多种通信协议,提升了系统的可伸缩性和稳定性,成为微服务通信领域的优选方案。开发者仅需关注业务逻辑,而无需过多关心底层通信细节,使得Spring Cloud Dubbo在未来微服务开发中将更加受到青睐。
69 0
|
12天前
|
Dubbo 应用服务中间件 Apache
Star 4w+,Apache Dubbo 3.3 全新发布,Triple X 领衔,开启微服务通信新时代
在 Apache Dubbo 突破 4w Star 之际,Apache Dubbo 团队正式宣布,Dubbo 3.3 正式发布!作为全球领先的开源微服务框架,Dubbo 一直致力于为开发者提供高性能、可扩展且灵活的分布式服务解决方案。此次发布的 Dubbo 3.3,通过 Triple X 的全新升级,突破了以往局限,实现了对南北向与东西向流量的全面支持,并提升了对云原生架构的友好性。
|
2月前
|
负载均衡 Dubbo 应用服务中间件
框架巨擘:Dubbo如何一统异构微服务江湖,成为开发者的超级武器!
【8月更文挑战第8天】在软件开发中,微服务架构因灵活性和可扩展性备受欢迎。面对异构微服务的挑战,Apache Dubbo作为高性能Java RPC框架脱颖而出。它具备服务注册与发现、负载均衡及容错机制等核心特性,支持多种通信协议和序列化方式,能有效连接不同技术栈的微服务。Dubbo的插件化设计保证了面向未来的扩展性,使其成为构建稳定高效分布式系统的理想选择。
40 5
|
5月前
|
Dubbo Java 应用服务中间件
阿里巴巴资深架构师深度解析微服务架构设计之SpringCloud+Dubbo
软件架构是一个包含各种组织的系统组织,这些组件包括Web服务器,应用服务器,数据库,存储,通讯层),它们彼此或和环境存在关系。系统架构的目标是解决利益相关者的关注点。
下一篇
无影云桌面