1 dubbo的调用关系主要由四部分组成:

  1. Provider:暴露服务的服务提供方
  • Protocol:协议, 负责提供者和消费者之间协议交互数据
  • Service:服务,真实的业务服务信息,可以理解为接口和实现
  • Container:容器,dubbo的运行环境
  1. Consumer:调用远程服务的服务消费方
  • Protocol:协议,负责提供者和消费者之间协议交互数据
  • Cluster:集群,感知提供者端的列表信息
  • Proxy:代理,可以理解为提供者的服务调用代理类,由它接管Consumer中的接口调用逻辑
  1. Registry:注册中心,用于作为服务发现和路由配置等工作,提供者和消费者都会在这里进行注册
  2. Monitor:用于提供者和消费者的数据统计,如调用频次、成功失败次数等

dubbo hession协议jetty版本 dubbo协议底层_java

2.dubbo的启动和执行流程说明

  1. 提供者端启动, 容器负责把service信息加载,并通过Protocol注册到注册中心
  2. 消费端启动,通过监听提供者列表来感知提供者信息,并在提供者发生改变时,通过注册中心及时通知消费端
  3. 消费方发起请求,通过Proxy模块
  4. 利用Cluster模块选择真是的要发送给的提供者信息
  5. 交由Consumer中的Protocol把信息发送给提供者
  6. 提供者同样需要通过Protocol模块来处理消费者的信息
  7. 最后由真正的服务提供者service来处理请求

3.dubbo的整体调用链路

dubbo hession协议jetty版本 dubbo协议底层_rpc_02

  • 淡绿色代表了 服务生产者的范围 ;淡蓝色代表了服务消费者的范围 ;红色箭头代表了调用的方向
  • 图中主要分为三个层面:业务逻辑层(我们编写的业务代码);RPC层(远程调用过程) ;Remoting(远程数据传输)

4. 整体链路调用的流程

  1. 消费者通过Interface进行方法调用,统一交由消费者的Proxy处理(Proxy通过ProxyFactory来进行代理对象的创建)
  2. Proxy调用Filter模块,做一个统一的过滤请求。(这里的过滤一般是服务容错、调用本地缓存等)
  3. 进入到最主要的invoker调用逻辑:
  4. 通过Directory读取配置信息, 最终通过list方法获取所有的invoker
    通过Cluster模块,根据选择的具体路由规则来选择invoker列表
    通过loadBalance模块,选择负载均衡策略,选择一个具体的invoker来处理请求
    如果执行出错,且Consumer阶段配置了重试机制,则会重新尝试执行
  5. 继续经过filter,进行执行功能的前后封装 invoker 选择具体的执行协议。(注意这里的filter要区别于上面步骤2中的filter,这里做的事情与上面完全不同,这里操作有:context、deprecated、count、limit、monitor等)
  6. 请求进入client,进行codec(编码)、serialization(序列化),然后发送数据给provider
  7. 请求到达Provider中的Server, 这里进行方便吗和反序列化的接收数据
  8. 使用Exporter选择执行器(执行协议)
  9. 交给Filter进行一个提供者端的过滤, 到达invoker
  10. 这里我们可以将6、7、8的执行过程与前面 4、5的执行过程做一个对比,前面4、5的执行过程时通过filter包装对象 – > invoker选择执行协议 —> client编码和序列化数据 ;这里6、7、8的执行过程是 server反序列化接收到的数据 --> Export选择执行协议 —> filter 包装数据。我们可以看到前面4、5的过程是服务消费端加工数据和传输的过程, 后面6、7、8是服务提供端接收数据和翻译数据的过程。
  11. 通过invoker调用接口的具体实现,然后返回

5.dubbo的源码整体设计

dubbo hession协议jetty版本 dubbo协议底层_java_03

  1. 图中左边的淡蓝色部分是服务消费端的源码设计,右边的淡绿色部分为服务提供者的源码设计, 中间用红色方框括起来的部分是二者的公共部分。
  2. 图的最左边部分, 整个服务从大的层面分我们可以分为三层:business(具体实现业务层)、RPC(远程服务调用层)、Remoting(远程数据传输成)
  3. 图中最左边,又将数据分成10小层,而图中的最右边代表着各层之间的依赖关系,从图中我们可以看出各层之间是单向依赖的,每一层都可以剥离上一层被单独调用。
  4. 图中蓝色虚线代表初始化的过程,即启动时组装链;红色实线为方法的调用过程,即运行时调用链;紫色三箭头为继承,可以把子类看作父类的同一个节点,线上的文字为调用方法。

dubbo hession协议jetty版本 dubbo协议底层_dubbo_04

dubbo hession协议jetty版本 dubbo协议底层_负载均衡_05

按照上面了解到的dubbo的整体调用链路, 在这里的调用过程是:

start —> interface — > proxy – >filter —> invoker --> filter —> client ----> codec—>serilization ----> server —> export —> filter —> invoker

6.dubbo的各个层级的状况

  • config 配置层:对外配置接口,以 ServiceConfig, ReferenceConfig 为中心,可以直接初始化配置类,也可以通过 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
  • exchange 信息交换层:封装请求响应模式,同步转异步,以 Request, Response 为中心,扩展接口为 Exchanger, ExchangeChannel, ExchangeClient, ExchangeServer
  • transport 网络传输层:抽象 mina 和 netty 为统一接口,以 Message 为中心,扩展接口为 Channel, Transporter, Client, Server, Codec
  • serialize 数据序列化层:可复用的一些工具,扩展接口为 Serialization, ObjectInput, ObjectOutput, ThreadPool

Dubbo建议使用Zookeeper作为服务的注册中心。

  1. Zookeeper的作用:
    zookeeper用来注册服务和进行负载均衡,哪一个服务由哪一个机器来提供必需让调用者知道,简单来说就是ip地址和服务名称的对应关系。当然也可以通过硬编码的方式把这种对应关系在调用方业务代码中实现,但是如果提供服务的机器挂掉调用者无法知晓,如果不更改代码会继续请求挂掉的机器提供服务。zookeeper通过心跳机制可以检测挂掉的机器并将挂掉机器的ip和服务对应关系从列表中删除。至于支持高并发,简单来说就是横向扩展,在不更改代码的情况通过添加机器来提高运算能力。通过添加新的机器向zookeeper注册服务,服务的提供者多了能服务的客户就多了。
  2. dubbo:
    是管理中间层的工具,在业务层到数据仓库间有非常多服务的接入和服务提供者需要调度,dubbo提供一个框架解决这个问题。
    注意这里的dubbo只是一个框架,至于你架子上放什么是完全取决于你的,就像一个汽车骨架,你需要配你的轮子引擎。这个框架中要完成调度必须要有一个分布式的注册中心,储存所有服务的元数据,你可以用zk,也可以用别的,只是大家都用zk。
  3. zookeeper和dubbo的关系:
    Dubbo的将注册中心进行抽象,是得它可以外接不同的存储媒介给注册中心提供服务,有ZooKeeper,Memcached,Redis等。
    引入了ZooKeeper作为存储媒介,也就把ZooKeeper的特性引进来。首先是负载均衡,单注册中心的承载能力是有限的,在流量达到一定程度的时候就需要分流,负载均衡就是为了分流而存在的,一个ZooKeeper群配合相应的Web应用就可以很容易达到负载均衡;资源同步,单单有负载均衡还不够,节点之间的数据和资源需要同步,ZooKeeper集群就天然具备有这样的功能;命名服务,将树状结构用于维护全局的服务地址列表,服务提供者在启动的时候,向ZK上的指定节点/dubbo/${serviceName}/providers目录下写入自己的URL地址,这个操作就完成了服务的发布。其他特性还有Mast选举,分布式锁等。