[JD] 二、Dubbo实现原理

一、Dubbo核心特性
二、Dubbo整体设计
三、Dubbo扩展点加载机制
四、Dubbo扩展点特性

一、Dubbo核心特性
    Dubbo具备面向接口代理的高性能RPC调用、服务注册与发现、运行期流量管理、智能负载均衡和高度可扩展等特性

二、Dubbo整体设计

1.Dubbo总体分为 业务层、RPC层、Remote三大层(下图左侧)

dubbo系统架构 dubbo的底层实现原理和机制_封装

2.在框架细分的10层分层架构中,各个层次的设计要点
    · 服务接口层(Service):该层是与实际业务逻辑相关的,根据服务提供方和服务消费方的业务设计对应的接口和实现。
    · 配置层(Config):对外配置接口,以ServiceConfig和ReferenceConfig为中心,可以直接new配置类,也可以通过spring解析配置生成配置类。
    · 服务代理层(Proxy):服务接口透明代理,生成服务的客户端Stub和服务器端Skeleton,以ServiceProxy为中心,扩展接口为ProxyFactory。
    · 服务注册层(Registry):封装服务地址的注册与发现,以服务URL为中心,扩展接口为RegistryFactory、Registry和RegistryService。可能没有服务注册中心,此时服务提供方直接暴露服务。
    · 集群层(Cluster):封装多个提供者的路由及负载均衡,并桥接注册中心,以Invoker为中心,扩展接口为Cluster、Directory、Router和LoadBalance。将多个服务提供方组合为一个服务提供方,实现对服务消费方来透明,只需要与一个服务提供方进行交互。
    · 监控层(Monitor):RPC调用次数和调用时间监控,以Statistics为中心,扩展接口为MonitorFactory、Monitor和MonitorService。
    · 远程调用层(Protocol):封将RPC调用,以Invocation和Result为中心,扩展接口为Protocol、Invoker和Exporter。Protocol是服务域,它是Invoker暴露和引用的主功能入口,它负责Invoker的生命周期管理。Invoker是实体域,它是Dubbo的核心模型,其它模型都向它靠扰,或转换成它,它代表一个可执行体,可向它发起invoke调用,它有可能是一个本地的实现,也可能是一个远程的实现,也可能一个集群实现。
    · 信息交换层(Exchange):封装请求响应模式,同步转异步,以Request和Response为中心,扩展接口为Exchanger、ExchangeChannel、ExchangeClient和ExchangeServer。
    · 网络传输层(Transport):抽象mina和netty为统一接口,以Message为中心,扩展接口为Channel、Transporter、Client、Server和Codec。
    · 数据序列化层(Serialize):可复用的一些工具,扩展接口为Serialization、 ObjectInput、ObjectOutput和ThreadPool。
  通过Dubbo的代码简单说明相关源码包的情况:
    · dubbo-common 公共逻辑模块,包括Util类和通用模型。
    · dubbo-remoting 远程通讯模块,相当于Dubbo协议的实现,如果RPC用RMI协议则不需要使用此包。
    · dubbo-rpc 远程调用模块,抽象各种协议,以及动态代理,只包含一对一的调用,不关心集群的管理。
    · dubbo-cluster 集群模块,将多个服务提供方伪装为一个提供方,包括:负载均衡、容错、路由等,集群的地址列表可以是静态配置的,也可以是由注册中心下发。
    · dubbo-registry 注册中心模块,基于注册中心下发地址的集群方式,以及对各种注册中心的抽象。
    · dubbo-monitor 监控模块,统计服务调用次数,调用时间的,调用链跟踪的服务。
    · dubbo-config 配置模块,是Dubbo对外的API,用户通过Config使用Dubbo,隐藏Dubbo所有细节。
    · dubbo-container 容器模块,是一个Standalone的容器,以简单的Main加载Spring启动,因为服务通常不需要Tomcat/JBoss等Web容器的特性,没必要用Web容器去加载服务。

3.Dubbo底层实现
  3.1 协议支持
    在通信过程中,不同的服务等级一般对应着不同的服务质量,那么选择合适的协议便是一件非常重要的事情。例如,使用RMI协议,一般会受到防火墙的限制,所以对于外部与内部进行通信的场景,就不要使用RMI协议,而是基于HTTP协议或者Hessian协议。
    主要支持的协议有:
        Dubbo协议、Hessian协议、HTTP协议、RMI协议、WebService协议、Thrift协议、Memcached协议、Redis协议

  3.2 默认使用Dubbo协议
    · 连接个数:单连接
    · 连接方式:长连接
    · 传输协议:TCP
    · 传输方式:NIO异步传输
    · 序列化:Hessian二进制序列化
    · 适用范围:传入传出参数数据包较小(建议小于100K),消费者比提供者个数多,单一消费者无法压满提供者,尽量不要使用dubbo协议传输大文件或超大字符串
    · 使用场景:常规远程服务方法调用
    即,dubbo适合小数据量大并发的服务调用,以及消费者机器远大于生产者机器数的情况,不适合传输大数据量的服务比如文件、视频等,除非请求量很低。
  3.3 基本原理
    · client一个线程调用远程接口,生成一个唯一的ID(比如一段随机字符串,UUID等),Dubbo是使用AtomicLong从0开始累计数字的
    · 将打包的方法调用信息(如调用的接口名称,方法名称,参数值列表等),和处理结果的回调对象callback,全部封装在一起,组成一个对象object
    · 向专门存放调用信息的全局ConcurrentHashMap里面put(ID, object)
    · 将ID和打包的方法调用信息封装成一对象connRequest,使用IoSession.write(connRequest)异步发送出去
    · 当前线程再使用callback的get()方法试图获取远程返回的结果,在get()内部,则使用synchronized获取回调对象callback的锁, 再先检测是否已经获取到结果,如果没有,然后调用callback的wait()方法,释放callback上的锁,让当前线程处于等待状态。
    · 服务端接收到请求并处理后,将结果(此结果中包含了前面的ID,即回传)发送给客户端,客户端socket连接上专门监听消息的线程收到消息,分析结果,取到ID,再从前面的ConcurrentHashMap里面get(ID),从而找到callback,将方法调用结果设置到callback对象里。
    · 监听线程接着使用synchronized获取回调对象callback的锁(因为前面调用过wait(),那个线程已释放callback的锁了),再notifyAll(),唤醒前面处于等待状态的线程继续执行(callback的get()方法继续执行就能拿到调用结果了),至此,整个过程结束。
  核心点:先生成一个对象obj,在一个全局map里put(ID,obj)存放起来,再用synchronized获取obj锁,再调用obj.wait()让当前线程处于等待状态,然后另一消息监听线程等到服 务端结果来了后,再map.get(ID)找到obj,再用synchronized获取obj锁,再调用obj.notifyAll()唤醒前面处于等待状态的线程

三、Dubbo扩展点加载机制
1.Dubbo扩展点加载机制使框架的接口和具体实现完全解耦,是框架可扩展的基础
2.Dubbo扩展点是Dubbo的SPI层所有接口都可以基于Dubbo框架做定制二次开发,如:负载均衡、协议、注册中心等都可以扩展
3.Java SPI:Service Provider Interface 一个接口多种实现,通过配置确定使用哪个实现
4.Dubbo SPI:Java SPI增强版,增加了对扩展点IoC和AOP的支持

四、Dubbo扩展点特性
1.扩展点自动包装
    主要是通过使用组合关系、装饰模式 实现扩展点接口,实现类组合真正的扩展点实现类作为属性,在扩展点上添加逻辑。实际上是个AOP增强
2.扩展点自动装配
    如果扩展点实现类的成员是其他的扩展点,则自动注入依赖。实际上是IoC增强
3.扩展点自适应
    有些扩展并不是在框架启动阶段被加载,而是希望在扩展方法被调用时,根据运行时参数进行加载
    · 静态指定扩展点实现,通过SPI注解+配置
    · 动态指定扩展点实现,由运行时决定,可以使用@Adaptive注解标识完成
4.扩展点自动激活
    用@Activate注解标识,即对于集合类扩展点,如Filter,每个Filter实现不同的功能,需要同时激活就可以使用自动激活简化配置
5.扩展点的使用
  5.1 每个扩展点对应一个ExtensionLoader扩展点加载器
    · 扩展点加载器的获取通过 ExtensionLoader.getExtensionLoader(Class<T> type)获取 
    · 在通过扩展点加载器的getExtension(String name) 获取扩展点
  5.2 在ExtensionLoader类中,主要的属性是 两个Map+多个Cache
    · 两个Map:一个是存放扩展类加载器实例,另一个是存放扩展类实例,map的key都是扩展点类型Class<?>
    · 多个Cache:
        所有扩展类实现类 存放在 Holder<Map<String, Class<?>>> cachedClasses 中
        所有包装扩展类 存放在 Set<Class<?>> cachedWrapperClasses 中
        所有自激活类注解属性值 存放在 Map<String, Object> cachedActivates 中
        类上带Adaptive注解的缓存在 cachedAdaptiveClass 中

  5.3 getExtension的流程
    先从缓存cachedClasses中获取,获取到返回扩展类;未获取到,先加载扩展类信息,实例化,注入依赖,查找匹配包装类注入扩展类实例。