dubbo协议

文章目录

  • Dubbo
      • 负载均衡策略
      • 协议
        • dubbo
        • rmi
        • hessian
      • 集群容错策略
      • 服务启动依赖检查
      • 服务治理
      • 服务降级
        • 失败直接返回固定结果
        • 失败时执行二级方案
      • 循环依赖问题
      • 失败重试和超时重试
      • 路由
      • 同步/异步调用
        • 同步/阻塞
        • 异步
        • 事件通知
      • 优雅下线
      • 优雅上线
      • Spi机制
        • dubbo自定义一个Spi扩展
      • 源码解读
        • ExtensionLoader
        • 扩展点自适应

Dubbo

默认使用dubbo协议,hessian序列化框架,zookepper注册中心,使用Neety通信,随机负载均衡,失败自动切换,自动重试其它服务器,默认同步阻塞

dubbo协议_第1张图片

Consumer:服务消费者,即服务调用方;

Provider:服务提供者,即被调用方;

Registry:注册中心,服务注册与服务发现,dubbo支持4种注册中心(multicast zookeeper redis simple),dubbo推荐使用zookeeper注册中心;

Monitor:服务监控中心,提供服务调用次数和调用时间统计;

Container:服务运行容器;

分为十层

代理层的动态代理使用的是javaSsesit

负载均衡策略

  1. random loadBalance

    默认情况下,使用随机调用来实现负载均衡,可以对不同provider。也可以使用不同权重,来进行负载均衡

  2. randomRobin loadBalance

    轮询来分发流量,每台机器的次数严格一样。也可以调整权重

  3. leastactive loadBalance

    自动感知,如果某个机器性能越差,分发的请求就越少

  4. 一致性hash

    如果需要一类请求都到一个节点,就可以使用,配合缓存很好用

协议

dubbo

默认的协议是dubbo,适用于小数据量大并发,单一长连接,NIO异步传输(TCP传输),Hessian 二进制序列化

Dubbo 缺省协议采用单一长连接和 NIO 异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。

反之,Dubbo 缺省协议不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。
dubbo协议_第2张图片

特性
缺省协议,使用基于 mina 1.1.7 和 hessian 3.2.1 的 tbremoting 交互。

连接个数:单连接
连接方式:长连接
传输协议:TCP
传输方式:NIO 异步传输
序列化:Hessian 二进制序列化
适用范围:传入传出参数数据包较小(建议小于100K),消费者比提供者个数多,单一消费者无法压满提供者,尽量不要用 dubbo 协议传输大文件或超大字符串。
适用场景:常规远程服务方法调用

全量配置

<dubbo:protocol name=“dubbo” port=“9090” server=“netty” client=“netty” codec=“dubbo” serialization=“hessian2” charset=“UTF-8” threadpool=“fixed” threads=“100” queues=“0” iothreads=“9” buffer=“8192” accepts=“1000” payload=“8388608” />

//简单配置,使用dubbo协议,端口20868,线程数200
<dubbo:protocol name="dubbo" port="20868" threadpool="cached" threads="200"/>

rmi

阻塞式短连接,同步TCP传输,Java 标准二进制序列化

hessian

多个短连接,HTTP同步传输,Hessian二进制序列化

集群容错策略

在服务提供方或服务消费方中,均可设置

<dubbo:reference id="demoService" interface="org.apache.dubbo.demo.DemoService" cluster="failover" retries="1"/>
<dubbo:service interface="org.apache.dubbo.demo.DemoService" ref="demoService" cluster="failover" retries="2" />
  1. failOver cluster模式

    失败自动切换,自动重试。

  2. fialFast cluster模式

    一次调用失败就立即失败,不在重试,适用于写操作

  3. failSafe cluster模式

    出现异常忽略掉,常用语记录日志

  4. failBack

    失败了后台自动记录请求,然后定时重发,比较适合于写消息队列

  5. forking

    并行的调用多个provider,只要一个成功就立即返回

  6. broadcast

    依次调用所有的provider

服务启动依赖检查


duboo在启动时候会进行所有消费者的服务检查,如果服务都不可用,会抛错,默认为true,通过false进行关闭

服务治理

服务降级

mock只在出现非业务异常(比如超时,网络异常等)时执行,可配置bool值,或null

失败直接返回固定结果

<dubbo:reference id="xxxService" interface="com.x..service.xxxxService" check="false" mock="return null" />

失败时执行二级方案

现在消费的标签中,写mock=true

<dubbo:reference id="xxxService" interface="com.x..service.xxxxService" check="false" mock="true" />

然后服务提供方在目录下创建mock的处理类,dubbo在调用失败的时候会自动的访问mockxxxxService来进行执行

class xxxxServiceMock implements xxxxService{
 //注意类名必须是接口名+Mock
  fun1;
  
  fun2;
  
}

循环依赖问题

可以通过启动时的健康检查来暴露(依赖的双方启动时候必有先后)

  1. 划分功能
  2. 使用MQ进行解偶

失败重试和超时重试

在 Provider 上可以配置的 Consumer 端的属性有:

  1. timeout
  2. retries
  3. loadbalance

路由

  1. 脚本路由

  2. 条件路由,需要在管理控制台中输入路由规则

# DemoService的sayHello方法只能消费所有端口为20880的服务实例
# DemoService的sayHi方法只能消费所有端口为20881的服务实例
scope: service 
force: true
runtime: true
enabled: true
key: org.apache.dubbo.samples.governance.api.DemoService
conditions:
  - method=sayHello => address=*:20880
  - method=sayHi => address=*:20881
...
    
    
# app1的消费者只能消费所有端口为20880的服务实例
# app2的消费者只能消费所有端口为20881的服务实例
---
scope: application
force: true
runtime: true
enabled: true
key: governance-conditionrouter-consumer
conditions:
  - application=app1 => address=*:20880
  - application=app2 => address=*:20881
...
  • scope

    表示路由规则的作用粒度,scope的取值会决定key的取值。必填

    • service 务粒度
    • application 应用粒度
  • Key

    明确规则体作用在哪个服务或应用。必填

    • scope=service时,key取值为[{group}:]{service}[:{version}]的组合
    • scope=application时,key取值为application名称
  • enabled=true 当前路由规则是否生效,可不填,缺省生效。

  • force=false 当路由结果为空时,是否强制执行,如果不强制执行,路由结果为空的路由规则将自动失效,可不填,缺省为 false

  • runtime=false 是否在每次调用时执行路由规则,否则只在提供者地址列表变更时预先执行并缓存结果,调用时直接从缓存中获取路由结果。如果用了参数路由,必须设为 true,需要注意设置会影响调用的性能,可不填,缺省为 false

  • priority=1 路由规则的优先级,用于排序,优先级越大越靠前执行,可不填,缺省为 0

  • conditions 定义具体的路由规则内容。必填

同步/异步调用

同步/阻塞

同步调用是一种阻塞式的调用方式,即 Consumer 端代码一直阻塞等待,直到 Provider 端返回为止;

通常,一个典型的同步调用过程如下:

  1. Consumer 业务线程调用远程接口,向 Provider 发送请求,同时当前线程处于阻塞状态;
  2. Provider 接到 Consumer 的请求后,开始处理请求,将结果返回给 Consumer;
  3. Consumer 收到结果后,当前线程继续往后执行。

Dubbo底层Consumer发起调用后返回一个Future对象,Future通过.get(Timeout)来进行阻塞,并在超时时间以后,业务线程将会异常返回; 在超时时间前返回,会唤醒阻塞的业务线程

异步

consumer端对应的服务设置 async=“true”。

使用案例,Dubbo的异步调用

AsyncService service = ...;
String result = service.goodbye("samples");// 这里的返回值为空,请不要使用
Future<String> future = RpcContext.getContext().getFuture();
... // 业务线程可以开始做其他事情
result = future.get(); // 阻塞需要获取异步结果时,也可以使用 get(timeout, unit) 设置超时时间

使用案例,服务端返回异步对象

基于java 8中引入的CompletableFuture,dubbo在2.7.0版本中也增加了对CompletableFuture的支持,我们可以直接定义一个返回CompletableFuture类型的接口。

public interface AsyncService {
    String sayHello(String name);
    CompletableFuture<String> sayHelloAsync(String name);
}
//具体实现:
public class AsyncServiceImpl implements AsyncService {
    @Override
    public String sayHello(String name) {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return name;
    }
    @Override
    public CompletableFuture<String> sayHelloAsync(String name) {
        return CompletableFuture.supplyAsync(() -> name);
    }
}

//消费端调用:
CompletableFuture<String> completableFuture = asyncService.sayHelloAsync("async");
String result = completableFuture.get();

注意

​ 异步调用具有传递性,B同步调用C,但是当A异步调用B,就会导致B异步调用C

事件通知

dubbo允许consumer 端在调用之前、调用之后或出现异常时,触发 oninvoke、onreturn、onthrow 三个事件。类似于Spring中的前置增强、后置增强和异常抛出增强。

只需要在服务引用时,增加以下配置指定事件通知的方法即可。

//配置
<dubbo:reference id="asyncService" check="false" interface="com.alibaba.dubbo.demo.AsyncService" url="localhost:20880">
    <dubbo:method name="sayHello" 
                  oninvoke="notifyServiceImpl.onInvoke" 
                  onreturn="notifyServiceImpl.onReturn" 
                  onthrow="notifyServiceImpl.onThrow" />
</dubbo:reference>
  
//事件通知服务
public class NotifyServiceImpl implements NotifyService {
    // 方法参数与调用方法参数相同
    @Override
    public void onInvoke(String name) {
        System.out.println("onInvoke: " + name);
    }
    // 第一个参数为调用方法的返回值,其余为调用方法的参数
    @Override
    public void onReturn(String retName, String name) {
        System.out.println("onReturn: " + name);
    }
    // 第一个参数为调用异常,其余为调用方法的参数
    @Override
    public void onThrow(Throwable ex, String name) {
        System.out.println("onThrow: " + name);
    }
}

优雅下线

优雅下线的要求是:

  1. 服务消费者不应该请求到已经下线的服务提供者

  2. 在途请求需要处理完毕,不能被停机指令中断

Dubbo的优雅下线:

  1. 等待进程关闭的指令,利用ShutdownHook

  2. 先将所有的已经提供的服务和订阅的服务从列表中移除(对于自己是消费者来说,提供者的变更不会再通知到自己), 然后关闭所有使用的注册中心(比如断开ZK的连接)

  3. 等待10S,等消费者来得及反应

  4. 关闭所有的协议(先关闭服务提供者、在关闭服务消费者),需要等待所有正在执行的协议执行完成(应对上面第二点)

    • 销毁所有InvokerExporter
    • 关闭Server,向所有已连接Client发送当前Server只读事件;
    • 关闭独享/共享Client,断开连接,取消超时和重试任务;
    • 释放所有相关资源。
  5. 关闭JVM

优雅上线

  1. 延迟上线, 通过delay的配置,在SpringBoot启动以后5S才注册服务

具体的是Spring启动完成以后会发一个ContextRefreshedEvent事件,Dubbo的ServiceBean实现ApplicationListener,监听这个事件做处理

Spi机制

部分参考实战

dubbo是基于java的spi机制进行扩展。

  • 对Dubbo进行扩展,不需要改动Dubbo的源码
  • 自定义的Dubbo的扩展点实现,是一个普通的Java类,Dubbo没有引入任何Dubbo特有的元素,对代码侵入性几乎为零。
  • 将扩展注册到Dubbo中,只需要在ClassPath中添加配置文件。使用简单。而且不会对现有代码造成影响。符合开闭原则。
  • dubbo的扩展机制设计默认值:@SPI(“dubbo”) 代表默认的spi对象
  • Dubbo的扩展机制支持IoC,AoP等高级功能
  • Dubbo的扩展机制能很好的支持第三方IoC容器,默认支持Spring Bean,可自己扩展来支持其他容器,比如Google的Guice。
  • 切换扩展点的实现,只需要在配置文件中修改具体的实现,不需要改代码。使用方便。
//在java的spi机制中,通过ServiceLoader来读取META-INF/services文件中的内容,并根据内容生成对应类,案例如下:
//1.定义一个接口IRepository用于实现数据储存,成为扩展点
public interface IRepository {
    void save(String data);
}
//2.提供IRepository的实现 IRepository有两个实现,称为扩展。MysqlRepository和MongoRepository。
public class MysqlRepository implements IRepository{
  public void save(String data) {
        System.out.println("Save " + data + " to Mysql");
    }
}

public class MongoRepository implements IRepository {
    public void save(String data) {
        System.out.println("Save " + data + " to Mongo");
    }
}
//3 添加配置文件 在META-INF/services目录添加一个文件,文件名和接口全名称相同,所以文件是META-INF/services/com.demo.IRepository。文件内容为:
com.demo.MongoRepository
com.demo.MysqlRepository
//4 利用ServiceLoader来加载IRepository的实现:
ServiceLoader<IRepository> serviceLoader = ServiceLoader.load(IRepository.class);
Iterator<IRepository> it = serviceLoader.iterator();
while (it != null && it.hasNext()){
    IRepository demoService = it.next();
    System.out.println("class:" + demoService.getClass().getName());
    demoService.save("tom");
}

在上面这个案例中,利用ServiceLoader来读取文件,并根据文件生成对应的类实例

  1. 扩展点

    java接口

  2. 扩展

    java接口实现类

  3. 扩展实例

    java接口实现类的实例

  4. 扩展自适应实例

    java接口实现类的代理类实例

在Dubbo中:

  1. @Spi标示接口表示这个接口是一个扩展点,可以被Dubbo的ExtentionLoader加载。

  2. @Adaptive标示接口的方法,表示该方法是一个自适应方法,Dubbo在为扩展点生成自适应实例时,如果方法有@Adaptive注解,会为该方法生成对应的代码。方法内部会根据方法的参数,来决定使用哪个扩展。 @Adaptive注解用在类上代表实现一个装饰类,类似于设计模式中的装饰模式,它主要作用是返回指定类,目前在整个系统中AdaptiveCompiler、AdaptiveExtensionFactory这两个类拥有该注解。

  3. extentionLoader,Dubbo使用这个,ExtensionLoader.class.getClassLoader();来获得ClassLoader去加载对应目录的文件

  4. 扩展别名,在对应目录的文件中,和Java SPI不同,Dubbo中的扩展都有一个别名,用于在应用中引用它们。

    random=com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance roundrobin=com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance

  5. 加载目录:

    META-INF/dubbo/internal

    META-INF/dubbo

    META-INF/services

    dubbo会加载这几个文件目录下的配置文件

dubbo自定义一个Spi扩展

案例如下完整代码

//1.定义LoadBalance接口,实现LoadBalance,在LoadBalance中已经用@Spi标示了接口,用@Adaptive("loadbalance")标示了select方法
package com.dubbo.spi.demo.consumer;
public class DemoLoadBalance implements LoadBalance {
    @Override
    public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
        System.out.println("DemoLoadBalance: Select the first invoker...");
        return invokers.get(0);
    }
}
//2.添加配置文件:META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.LoadBalance
demo=com.dubbo.spi.demo.consumer.DemoLoadBalance
//3. 需要显式的告诉Dubbo使用demo的负载均衡进行消费。如果是通过spring的方式使用Dubbo,可以在xml文件中进行设置。
  <dubbo:reference id="helloService" interface="com.dubbo.spi.demo.api.IHelloService" loadbalance="demo" />
//启动

源码解读

部分参考源码

ExtensionLoader

主要类,不继承或实现其它类或接口

主要方法:

  • LoadBalance lb = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(loadbalanceName)

  • RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getAdaptiveExtension()

  1. getExtensionLoader方法 这是一个静态工厂方法,入参是一个可扩展的接口,返回一个该接口的ExtensionLoader实体类。通过这个实体类,可以根据name获得具体的扩展,也可以获得一个自适应扩展。
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
        // 扩展点必须是接口
        if (!type.isInterface()) {
            throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
        }
        // 必须要有@SPI注解
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type without @SPI Annotation!");
        }
        // 从缓存中根据接口获取对应的ExtensionLoader
        // 每个扩展只会被加载一次
        ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        if (loader == null) {
            // 初始化扩展
            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
            loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        }
        return loader;
}
    
private ExtensionLoader(Class<?> type) {
        this.type = type;
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
  1. getExtension方法,利用缓存来根据name获取对应的实例,主要实现是createExtension方法。注意两次的为null检验
public T getExtension(String name) {
        Holder<Object> holder = cachedInstances.get(name);
        if (holder == null) {
            cachedInstances.putIfAbsent(name, new Holder<Object>());
            holder = cachedInstances.get(name);
        }
        Object instance = holder.get();
        // 从缓存中获取,如果不存在就创建
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                    instance = createExtension(name);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
}

createExtension

private T createExtension(String name) {
        // 根据扩展点名称得到扩展类,比如对于LoadBalance,根据random得到RandomLoadBalance类
        Class<?> clazz = getExtensionClasses().get(name);
        
        T instance = (T) EXTENSION_INSTANCES.get(clazz);
        if (instance == null) {
              // 使用反射调用nesInstance来创建扩展类的一个示例
            EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
            instance = (T) EXTENSION_INSTANCES.get(clazz);
        }
        // 对扩展类示例进行依赖注入
        injectExtension(instance);
        // 如果有wrapper,添加wrapper
        Set<Class<?>> wrapperClasses = cachedWrapperClasses;
        if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
            for (Class<?> wrapperClass : wrapperClasses) {
                instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
            }
        }
        return instance;
}

createExtension方法做了以下事情:

  • 先根据name来得到对应的扩展类。从ClassPath下META-INF文件夹下读取扩展点配置文件。

  • 使用反射创建一个扩展类的实例

  • 对扩展类实例的属性进行依赖注入,即IOC。

  • 如果有wrapper,添加wrapper。即AOP,与上一步一起组合成了dubbo的自动装配。

四步完成,即创建并初始化了一个扩展实例。这个实例的依赖被注入了,也根据需要被包装了。到此为止,这个扩展实例就可以被使用了。

  1. 根据name得到对应的扩展类,也利用了缓存,如果没有则从路径中寻找

    • META-INF/dubbo/internal
    • META-INF/dubbo
    • META-INF/services
  2. 使用反射获取对应类的实例,创建的扩展实例的属性都是空值。

  3. 依赖注入

    private T injectExtension(T instance) {
        for (Method method : instance.getClass().getMethods()) {
          	//判断是否是set方法来判断是否是属性的设置方法
            if (method.getName().startsWith("set")
                    && method.getParameterTypes().length == 1
                    && Modifier.isPublic(method.getModifiers())) {
                Class<?> pt = method.getParameterTypes()[0];
                String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
                Object object = objectFactory.getExtension(pt, property);
                if (object != null) {
                    method.invoke(instance, object);
                }
            }
        }
        return instance;
    }
    

    以下要点:

    1. Dubbo的方案是查找Java标准的setter方法。即方法名以set开始,只有一个参数。如果扩展类中有这样的set方法,Dubbo会对其进行依赖注入,类似于Spring的set方法注入。

    2. 因为注入的对象可能包括Dubbo的spi扩展,也可能包括Spring的Bean,所以使用Object object = objectFactory.getExtension(pt, property)来实现,objectFactory对象是在构造函数中确定的。

      ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension())

      而ExtensionFactory有三个实现类(真正最后生效的还是1和2):

      1. SpiExtensionFactory:Dubbo自己的Spi去加载Extension

      2. SpringExtensionFactory:从Spring容器中去加载Extension,加载了ApplicationContext

      3. AdaptiveExtensionFactory: 自适应的AdaptiveExtensionLoader

        //有@Adaptive注解。前面提到了,Dubbo会为每一个扩展创建一个自适应实例
        @Adaptive
        public class AdaptiveExtensionFactory implements ExtensionFactory {
        
            private final List<ExtensionFactory> factories;
        
            public AdaptiveExtensionFactory() {
                ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
                List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
                for (String name : loader.getSupportedExtensions()) {
                    list.add(loader.getExtension(name));
                }
                factories = Collections.unmodifiableList(list);
            }
        
            public <T> T getExtension(Class<T> type, String name) {
                for (ExtensionFactory factory : factories) {
                    T extension = factory.getExtension(type, name);
                    if (extension != null) {
                        return extension;
                    }
                }
                return null;
            }
        }
        

    ​ AdaptiveExtensionLoader类有@Adaptive注解。前面提到了,Dubbo会为每一个扩展创建一个自适应实例。如果扩展类上有@Adaptive,会使用该类作为自适应类。如果没有,Dubbo会为我们创建一个。

    ​ 所以ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension())会返回一个AdaptiveExtensionLoader实例,作为自适应扩展实例。

    ​ AdaptiveExtensionLoader会遍历所有的ExtensionFactory实现,尝试着去加载扩展。如果找到了,返回。如果没有,在下一个ExtensionFactory中继续找。

    ​ Dubbo内置了两个ExtensionFactory,分别从Dubbo自身的扩展机制和Spring容器中去寻找。由于ExtensionFactory本身也是一个扩展点,我们可以实现自己的ExtensionFactory,让Dubbo的自动装配支持我们自定义的组件。比如,我们在项目中使用了Google的guice这个 IOC 容器。我们可以实现自己的GuiceExtensionFactory,让Dubbo支持从guice容器中加载扩展。

  4. AOP

    ​ dubbo的扩展机制支持Aop,在Dubbo中有一种特殊的类是Wrapper类,通过装饰器模式,使用包装类包装原始的扩展点实例。在原始扩展点实现前后插入其他逻辑,实现AOP功能。

    class A{
        private A a;
        public A(A a){//通过构造函数来设置属性
            this.a = a;
        }
    }
    

    ​ Dubbo中这样的Wrapper类有ProtocolFilterWrapper, ProtocolListenerWrapper等,分别在dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol路径的配置文件中进行配置:

    filter=org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper listener=org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper mock=org.apache.dubbo.rpc.support.MockProtocol

    ​ 加载配置文件:

    try {  
      clazz.getConstructor(type);     //通过clazz.getConstructor(type)来获取,参数是扩展点接口的构造函数,注意构造函数的参数类型是扩展点接口,而不是扩展类。
      Set<Class<?>> wrappers = cachedWrapperClasses;
      if (wrappers == null) {
        cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
        wrappers = cachedWrapperClasses;
      }
      wrappers.add(clazz);
    } catch (NoSuchMethodException e) {}
    
    //以ProtocolFilterWrapper为例,配置文件对应的name是filter
    public class ProtocolFilterWrapper implements Protocol {
        private final Protocol protocol;
        // 有一个参数是Protocol的复制构造函数
        public ProtocolFilterWrapper(Protocol protocol) {
            if (protocol == null) {
                throw new IllegalArgumentException("protocol == null");
            }
            this.protocol = protocol;
        }
    }
    

扩展点自适应

​ 扩展点自适应其实就是一个扩展点的代理。Dubbo中每一个扩展点都有一个自适应类,如果没有显式提供,Dubbo会自动为我们创建一个,默认使用Javaassist。

//自适应扩展类的代码
public T getAdaptiveExtension() {
    Object instance = cachedAdaptiveInstance.get();
    if (instance == null) {
            synchronized (cachedAdaptiveInstance) {
                instance = cachedAdaptiveInstance.get();
                if (instance == null) {
                      instance = createAdaptiveExtension();
                      cachedAdaptiveInstance.set(instance); 
                }
            }        
    }
    return (T) instance;
}
// 核心方法createAdaptiveExtension内容
private T createAdaptiveExtension() {        
    return injectExtension((T) getAdaptiveExtensionClass().newInstance());
}
//getAdaptiveExtensionClass内容
private Class<?> getAdaptiveExtensionClass() {
        getExtensionClasses();
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
//createAdaptiveExtensionClass内容
private Class<?> createAdaptiveExtensionClass() {
  			//生成自适应类的Java源码,使用一个StringBuilder来构建自适应类的Java源码。
        String code = createAdaptiveExtensionClassCode();
        ClassLoader classLoader = findClassLoader();
        org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
  //再将源码编译成Java的字节码,加载到JVM中。
        return compiler.compile(code, classLoader);
}
//compiler的内容,使用javassist:
@SPI("javassist")
public interface Compiler {
    Class<?> compile(String code, ClassLoader classLoader);
}

参考: Dubbo用户手册添加链接描述。

你可能感兴趣的:(java,java)