首页 / 军事 / 中国军情 / 正文

dubbo(Dubbo 的 8000 字图文详解,建议收藏)

放大字体  缩小字体 来源:爱得利官网 2026-04-17 17:35  浏览次数:9

为了更好地了解和使用 Dubbo,今天来介绍一下 Dubbo 的主要组件和实现原理。

Dubbo 是一款高性能 Java RPC 架构。它实现了面向接口代理的 RPC 调用,服务注册和发现,负载均衡,容错,扩展性等等功能。

Dubbo 的三层结构

组件功能列表

Dubbo 调用工作流

上面介绍了 Dubbo 的框架分层,下图的工作流就展示了他们是如何工作的。

Dubbo 的 8000 字图文详解,建议收藏nerror="javascript:errorimg.call(this);">

工作流涉及到服务提供者(Provider),注册中心(Registration),网络(Network)和服务消费者(Consumer):

  • 服务提供者在启动的时候,会通过读取一些配置将服务实例化。
  • Proxy 封装服务调用接口,方便调用者调用。客户端获取 Proxy 时,可以像调用本地服务一样,调用远程服务。
  • Proxy 在封装时,需要调用 Protocol 定义协议格式,例如:Dubbo Protocol。
  • 将 Proxy 封装成 Invoker,它是真实服务调用的实例。
  • 将 Invoker 转化成 Exporter,Exporter 只是把 Invoker 包装了一层,是为了在注册中心中暴露自己,方便消费者使用。
  • 将包装好的 Exporter 注册到注册中心。
  • 服务消费者建立好实例,会到服务注册中心订阅服务提供者的元数据。元数据包括服务 IP 和端口以及调用方式(Proxy)。
  • 消费者会通过获取的 Proxy 进行调用。通过服务提供方包装过程可以知道,Proxy 实际包装了 Invoker 实体,因此需要使用 Invoker 进行调用。
  • 在 Invoker 调用之前,通过 Directory 获取服务提供者的 Invoker 列表。在分布式的服务中有可能出现同一个服务,分布在不同的节点上。
  • 通过路由规则了解,服务需要从哪些节点获取。
  • Invoker 调用过程中,通过 Cluster 进行容错,如果遇到失败策略进行重试。
  • 调用中,由于多个服务可能会分布到不同的节点,就要通过 LoadBalance 来实现负载均衡。
  • Invoker 调用之前还需要经过 Filter,它是一个过滤链,用来处理上下文,限流和计数的工作。
  • 生成过滤以后的 Invoker。
  • 用 Client 进行数据传输。
  • Codec 会根据 Protocol 定义的协议,进行协议的构造。
  • 构造完成的数据,通过序列化 Serialization 传输给服务提供者。
  • Request 已经到达了服务提供者,它会被分配到线程池(ThreadPool)中进行处理。
  • Server 拿到请求以后查找对应的 Exporter(包含有 Invoker)。
  • 由于 Export 也会被 Filter 层层包裹
  • 通过 Filter 以后获得 Invoker
  • 最后,对服务提供者实体进行调用。

实际上都是调用实体在不同阶段的不同表现形式,本质是一样的,在不同的使用场景使用不同的实体。

后面我们会对具体流程进行解析。如果时间不够无法阅读完全文,可以把上面的图保存。

上面讲到的服务调用流程中,开始服务提供者会进行初始化,将暴露给其他服务调用。服务消费者也需要初始化,并且在注册中心注册自己。

Dubbo 的 8000 字图文详解,建议收藏nerror="javascript:errorimg.call(this);">

首先来看看服务提供者暴露服务的整体机制:

Dubbo 的 8000 字图文详解,建议收藏nerror="javascript:errorimg.call(this);">

开篇的大图中列举了 Config 核心组件,在服务提供者初始化的时候,会通过 Config 组件中的 ServiceConfig 读取服务的配置信息。

在读取配置文件生成服务实体以后,会通过 ProxyFactory 将 Proxy 转换成 Invoker。

下面是针对多协议多注册中心进行源代码分析:

Dubbo 的 8000 字图文详解,建议收藏nerror="javascript:errorimg.call(this);">

doExportUrlsFor1Protocol 方法-1

Dubbo 的 8000 字图文详解,建议收藏nerror="javascript:errorimg.call(this);">

上面截取了服务提供者暴露服务的代码片段,从注释上看整个暴露过程分为七个步骤:

  • 读取其他配置信息到 map 中,用来后面构造 URL。
  • 读取全局配置信息。
  • 配置不是 remote,也就是暴露本地服务。
  • 如果配置了监控地址,则服务调用信息会上报。
  • 通过 Proxy 转化成 Invoker,RegistryURL 存放的是注册中心的地址。
  • 暴露服务以后,向注册中心注册服务信息。
  • 没有注册中心直接暴露服务。

说完了服务提供者的暴露再来看看服务消费者。

Dubbo 的 8000 字图文详解,建议收藏nerror="javascript:errorimg.call(this);">

服务消费者首先持有远程服务实例生成的 Invoker,然后把 Invoker 转换成用户接口的动态代理引用。

这里一起来看看 createProxy 的源代码:

Dubbo 的 8000 字图文详解,建议收藏nerror="javascript:errorimg.call(this);">

getProxy 代码片段 2


注册中心

配置中心

配置中心工作流

注册调用流程图

注册中心工作原理

这里着重介绍一下 ZooKeeper 的实现。ZooKeeper 是负责协调服务式应用的。

客户端第一次连接注册中心的时候,会获取全量的服务元数据,包括服务提供者和服务消费者以及路由和配置的信息。

如果服务的元数据信息发生变化,客户端会接受到变更通知,然后去注册中心更新元数据信息。变更时根据 ZNode 节点中版本变化进行。


Dubbo 的 8000 字图文详解,建议收藏nerror="javascript:errorimg.call(this);">

分布式服务多以集群形式出现,Dubbo 也不例外。在消费服务发起调用的时候,会涉及到 Cluster,Directory,Router,LoadBalance 几个核心组件。

Dubbo 的 8000 字图文详解,建议收藏nerror="javascript:errorimg.call(this);">

先看看他们是如何工作的:

②获取可调用的服务列表,可以通过 Directory 的 List 方法获取。这里有两类服务列表的获取方式。

在 Directory 获取所有 Invoker 列表之后,会调用路由接口(Router)。其会根据用户配置的不同策略对 Invoker 列表进行过滤,只返回符合规则的 Invoker。

这里介绍一下 RegistryDirectory 的实现,它通过 Subscribe 和 Notify 方法,订阅和监听注册中心的元数据。

管理员会通过 dubbo-admin 修改 Configurators 的内容,Notify 监听到该信息,就更新本地服务的 Configurators 信息。

Notify 监听三类信息

Dubbo 的负载均衡策略有四种:

  • Random LoadBalance,随机,按照权重设置随机概率做负载均衡。
  • RoundRobinLoadBalance,轮询,按照公约后的权重设置轮询比例。
  • LeastActiveLoadBalance,按照活跃数调用,活跃度差的被调用的次数多。活跃度相同的 Invoker 进行随机调用。
  • ConsistentHashLoadBalance,一致性 Hash,相同参数的请求总是发到同一个提供者。

用户可以在,,, 标签上通过 Cluster 属性设置:

  • Failover,出现失败,立即重试其他服务器。可以设置重试次数。
  • Failfast,请求失败以后,返回异常结果,不进行重试。
  • Failsafe,出现异常,直接忽略。
  • Failback,请求失败后,将失败记录放到失败队列中,通过定时线程扫描该队列,并定时重试。
  • Forking,尝试调用多个相同的服务,其中任意一个服务返回,就立即返回结果。
  • Broadcast,广播调用所有可以连接的服务,任意一个服务返回错误,就任务调用失败。
  • Mock,响应失败时返回伪造的响应结果。
  • Available,通过遍历的方式查找所有服务列表,找到第一个可以返回结果的节点,并且返回结果。
  • Mergable,将多个节点请求合并进行返回。

服务消费者经过容错,Invoker 列表,路由和负载均衡以后,会对 Invoker 进行过滤,之后通过 Client 编码,序列化发给服务提供者。

Dubbo 的 8000 字图文详解,建议收藏nerror="javascript:errorimg.call(this);">

从上图可以看出在服务消费者调用服务提供者的前后,都会调用 Filter(过滤器)。

Dubbo 系统有自带的系统过滤器,服务提供者有 11 个,服务消费者有 5 个。过滤器的使用可以通过 @Activate 的注释,或者配置文件实现。

Dubbo 的 8000 字图文详解,建议收藏nerror="javascript:errorimg.call(this);">

过滤器的使用遵循以下几个规则:

  • 过滤器顺序,过滤器执行是有顺序的。例如,用户定义的过滤器的过滤顺序默认会在系统过滤器之后。又例如,上图中 filter=“filter01, filter02”,filter01 过滤器执行就在 filter02 之前。
  • 过滤器失效,如果针对某些服务或者方法不希望使用某些过滤器,可以通过“-”(减号)的方式使该过滤器失效。例如,filter=“-filter01”。
  • 过滤器叠加,如果服务提供者和服务消费者都配置了过滤器,那么两个过滤器会被叠加生效。

调用请求经过过滤以后,会以 Invoker 的形式对 Client 进行调用。Client 会交由底层 I/O 线程池处理,其包括处理消息读写,序列化,反序列化等逻辑。

协议体包含了传输的主要内容,其意义不言而喻,它是由 16 字节长的报文组成,每个字节包括 8 个二进制位。

服务消费者在调用之前会将上述服务消息体,根据 Dubbo 协议打包好。框架内部会调用 DefaultFuture 对象的 get 方法进行等待。

在 HashMap 中会找到对应的 Request 对象,并且返回给服务消费者。

Dubbo 的 8000 字图文详解,建议收藏nerror="javascript:errorimg.call(this);">

协议打包好以后就需要给协议编码和序列化。这里需要用到 Dubbo 的编码器,其过程是将信息传化成字节流。

同样通过 encodeResponse 编码协议头,encodeResponseData 编码协议体。

虽然,编码和解码的细节在这里不做展开,但是以下几点需要注意:

  • 构造 16 字节的协议头,特别是需要创建前面两个字节的魔法数,也就是“0xdabb”,它是用来分割两个不同请求的。
  • 生成唯一的请求/响应 ID,并且根据这个 ID 识别请求和响应协议包。
  • 通过协议头中的 19-23 位的描述,进行序列化/反序列化操作。
  • 为了提高处理效率,每个协议都会放到 Buffer 中处理。

由于服务提供者在注册中心是通过 Exporter 的方式暴露服务的,服务消费者也是通过 Exporter 作为接口进行调用的。

总结

在这 22 步的流程中,以服务提供者和服务消费者的初始化为起点,用到了 Config 和 Proxy 以及 Protocol,Invoker。

在服务消费者调用提供者之前,需要通过 Cluster 容错机制,Directory 获取 Invoker 列表,Router 找到路由信息,再使用 LoadBalance 知道具体服务。

对于远程调用,需要调用打包协议,针对 Dubbo 协议进行了描述,并且针对该协议进行了编码/解码和序列化/反序列化的操作。

服务提供者收到请求以后,会将请求放到 ThreadPool 中逐一处理。通过 Exporter,Invoker,Filter 的逐级转换,最后响应请求。

打赏
0相关评论
热门搜索排行
精彩图片
友情链接
声明:本站信息均由用户注册后自行发布,本站不承担任何法律责任。如有侵权请告知立立即做删除处理。
违法不良信息举报邮箱:115904045
头条快讯网 版权所有
中国互联网举报中心