Dubbo 服务发现&引用

Dubbo 引用的服务消费者最终会构造成一个 Spring 的 Bean,具体是通过 ReferenceBean 来实现的。它是一个 FactoryBean,所有的服务消费者 Bean 都通过它来生产。

  1. ReferenceBean#getObject() --> ReferenceConfig#get()

ReferenceConfig 最终会创建一个动态代理类返回:

  1. private T createProxy(Map<String, String> map) {
  2. ......
  3. // assemble URL from register center's configuration
  4. // 从注册中心的配置组装 URL(服务发现)
  5. List<URL> us = loadRegistries(false);
  6. if (us != null && !us.isEmpty()) {
  7. for (URL u : us) {
  8. URL monitorUrl = loadMonitor(u);
  9. if (monitorUrl != null) {
  10. map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
  11. }
  12. urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
  13. }
  14. }
  15. ......
  16. if (urls.size() == 1) {
  17. // 创建 Invoker
  18. invoker = refprotocol.refer(interfaceClass, urls.get(0));
  19. } else {
  20. List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
  21. URL registryURL = null;
  22. for (URL url : urls) {
  23. invokers.add(refprotocol.refer(interfaceClass, url));
  24. if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
  25. registryURL = url; // use last registry url
  26. }
  27. }
  28. if (registryURL != null) { // registry url is available
  29. // use AvailableCluster only when register's cluster is available
  30. URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
  31. // 当服务提供者有多个时,就创建一个 ClusterInvoker
  32. invoker = cluster.join(new StaticDirectory(u, invokers));
  33. } else { // not a registry url
  34. invoker = cluster.join(new StaticDirectory(invokers));
  35. }
  36. }
  37. ......
  38. // create service proxy
  39. return (T) proxyFactory.getProxy(invoker);
  40. }

服务发现

dubbo 的服务发现,是通过从注册中心订阅服务提供者组装成 URL,然后通过 URL 创建出 Invoker 来实现的。

服务引用

Dubbo 的服务引用,实际上是为引用的接口创建一个 Proxy,这个 Proxy 的功能就是去执行 refprotocol.refer(interfaceClass, url) 创建出来的 Invoker。 当服务提供者有多个时,就创建一个 ClusterInvoker。Cluster 是一个 SPI 扩展点,默认使用的是 failover=com.alibaba.dubbo.rpc.cluster.support.FailoverCluster。

所以,Consumer 端服务调用的逻辑被封装在 refprotocol.refer(interfaceClass, url) 创建出来的 Invoker 上。通过之前 Dubbo Protocol & Filter 的学习,我们可以知道 refprotocol 是一个 Wrapped Protocol,refer() 方法创建出来的 Invoker 是被 Filter 包裹的一个 DubboInvoker。

综上,Consumer 端服务调用的逻辑是:

  1. 执行 FailoverClusterInvoker#invoke(Invocation invocation) (负载均衡。当有多个 provider 时走这一步,没有的话,就跳过)

  2. 执行 Filter#invoke(Invoker<?> invoker, Invocation invocation) (所有 group="provider" 的 Filter)

  3. 执行 DubboInvoker#invoke(Invocation inv)

服务消费端创建 tcp 连接

refprotocol.refer(interfaceClass, url) 被调用时会去创建与 provider 的 tcp 连接。

DubboProtocol#refer(Class<T> serviceType, URL url) 源码如下:

  1. public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
  2. optimizeSerialization(url);
  3. // create rpc invoker. 同时,创建一条与 provider 的 tcp 连接
  4. DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
  5. invokers.add(invoker);
  6. return invoker;
  7. }

Dubbo 服务引用时,首先创建一条与 provider 的 tcp 连接 ExchangeClient,然后再创建一个 DubboInvoker。

getClients(url) 最终会调用 initClient(URL url) 来创建一条新连接:

  1. private ExchangeClient initClient(URL url) {
  2. ......
  3. // 设置 codec = "dubbo"
  4. url = url.addParameter(Constants.CODEC_KEY, DubboCodec.NAME);
  5. // enable heartbeat by default
  6. url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
  7. ......
  8. ExchangeClient client;
  9. try {
  10. // connection should be lazy
  11. if (url.getParameter(Constants.LAZY_CONNECT_KEY, false)) {
  12. client = new LazyConnectExchangeClient(url, requestHandler);
  13. } else {
  14. // 创建一条新连接
  15. client = Exchangers.connect(url, requestHandler);
  16. }
  17. } catch (RemotingException e) {
  18. throw new RpcException("Fail to create remoting client for service(" + url + "): " + e.getMessage(), e);
  19. }
  20. return client;
  21. }

新连接创建过程:

Exchangers.connect(url, requestHandler) --> ExchangeClient#connect(URL url, ExchangeHandler handler) --> Exchanger$Adaptive#connect(URL url, ExchangeHandler handler) --> HeaderExchanger#connect(URL url, ExchangeHandler handler) --> 返回 new HeaderExchangeClient(client, true)

  1. // HeaderExchanger.class
  2. public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
  3. // 创建新连接;启动 dubbo 心跳
  4. return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))), true);
  5. }

Transporters#connect(URL url, ChannelHandler... handlers) --> Transporter$Adaptive#connect(URL url, ChannelHandler handler) --> NettyTransporter#connect(URL url, ChannelHandler listener) --> 返回 new NettyClient(url, listener)

编解码最终使用的是:com.alibaba.dubbo.rpc.protocol.dubbo.DubboCodec

附:

Dubbo 心跳(HeartBeatTask)的作用是:当检测到心跳超时的时候,自动重连

官方如是说:

引用服务

1. 直连引用服务:

在没有注册中心,直连提供者的情况下 [3]ReferenceConfig 解析出的 URL 的格式为:dubbo://service-host/com.foo.FooService?version=1.0.0

基于扩展点自适应机制,通过 URL 的 dubbo:// 协议头识别,直接调用 DubboProtocolrefer() 方法,返回提供者引用。

2. 从注册中心发现引用服务:

在有注册中心,通过注册中心发现提供者地址的情况下 [4]ReferenceConfig 解析出的 URL 的格式为:registry://registry-host/org.apache.dubbo.registry.RegistryService?refer=URL.encode("consumer://consumer-host/com.foo.FooService?version=1.0.0")

基于扩展点自适应机制,通过 URL 的 registry:// 协议头识别,就会调用 RegistryProtocolrefer()方法,基于 refer 参数中的条件,查询提供者 URL,如: dubbo://service-host/com.foo.FooService?version=1.0.0

基于扩展点自适应机制,通过提供者 URL 的 dubbo:// 协议头识别,就会调用 DubboProtocolrefer()方法,得到提供者引用。

然后 RegistryProtocol 将多个提供者引用,通过 Cluster 扩展点,伪装成单个提供者引用返回。

服务消费者消费一个服务的详细过程


上图是服务消费的主过程:

首先 ReferenceConfig 类的 init 方法调用 Protocolrefer 方法生成 Invoker 实例(如上图中的红色部分),这是服务消费的关键。接下来把 Invoker 转换为客户端需要的接口(如:HelloWorld)。

关于每种协议如 RMI/Dubbo/Web service 等它们在调用 refer 方法生成 Invoker 实例的细节和上一章节所描述的类似。

  1. 如果想了解更多Dubbo源码的知识,请移步 Dubbo源码解读——通向高手之路 的视频讲解:
  2. http://edu.51cto.com/sd/2e565

【Dubbo 源码解析】05_Dubbo 服务发现&引用的更多相关文章

  1. dubbo源码解析-spi(4)

    前言 本篇是spi的第四篇,本篇讲解的是spi中增加的AOP,还是和上一篇一样,我们先从大家熟悉的spring引出AOP. AOP是老生常谈的话题了,思想都不会是一蹴而就的.比如架构设计从All in ...

  2. dubbo源码解析-spi(一)

    前言 虽然标题是dubbo源码解析,但是本篇并不会出现dubbo的源码,本篇和之前的dubbo源码解析-简单原理.与spring融合一样,为dubbo源码解析专题的知识预热篇. 插播面试题 你是否了解 ...

  3. dubbo源码解析五 --- 集群容错架构设计与原理分析

    欢迎来我的 Star Followers 后期后继续更新Dubbo别的文章 Dubbo 源码分析系列之一环境搭建 博客园 Dubbo 入门之二 --- 项目结构解析 博客园 Dubbo 源码分析系列之 ...

  4. Dubbo 源码解析四 —— 负载均衡LoadBalance

    欢迎来我的 Star Followers 后期后继续更新Dubbo别的文章 Dubbo 源码分析系列之一环境搭建 Dubbo 入门之二 --- 项目结构解析 Dubbo 源码分析系列之三 -- 架构原 ...

  5. Netty 4源码解析:服务端启动

    Netty 4源码解析:服务端启动 1.基础知识 1.1 Netty 4示例 因为Netty 5还处于测试版,所以选择了目前比较稳定的Netty 4作为学习对象.而且5.0的变化也不像4.0这么大,好 ...

  6. dubbo源码解析-spi(3)

    前言 在上一篇的末尾,我们提到了dubbo的spi中增加了IoC和AOP的功能.那么本篇就讲一下这个增加的IoC,spi部分预计会有四篇,因为这东西实在是太重要了.温故而知新,我们先来回顾一下,我们之 ...

  7. dubbo源码解析-zookeeper创建节点

    前言 在之前dubbo源码解析-本地暴露中的前言部分提到了两道高频的面试题,其中一道dubbo中zookeeper做注册中心,如果注册中心集群都挂掉,那发布者和订阅者还能通信吗?在上周的dubbo源码 ...

  8. Dubbo源码解析(一)服务发现

    一.Dubbo源码模块 官网地址 源码地址 1.1 源码模块组织 Dubbo工程是一个Maven多Module的项目,以包结构来组织各个模块. 核心模块及其关系,如图所示: 1.2 模块说明 dubb ...

  9. 【Dubbo 源码解析】06_Dubbo 服务调用

    Dubbo 服务调用 根据上图,可以看出,服务调用过程为: Consumer 端的 Proxy 调用 Cluster 层选择集群中的某一个 Invoker(负载均衡) Invoker 最终会调用 Pr ...

随机推荐

  1. PAT Basic 1026

    1026 程序运行时间 (15 分) 要获得一个 C 语言程序的运行时间,常用的方法是调用头文件 time.h,其中提供了 clock() 函数,可以捕捉从程序开始运行到 clock() 被调用时所耗 ...

  2. Codeforces Round #443 (Div. 1) D. Magic Breeding 位运算

    D. Magic Breeding link http://codeforces.com/contest/878/problem/D description Nikita and Sasha play ...

  3. oracle数据库启动和关闭方式

    Oracle数据库是重量级的,其管理非常复杂,将其在Linux平台上的启动和关闭步骤整理一下. 安装完毕oracle以后,需要创建oracle系统用户,并在/home/oracle下面的.bash_p ...

  4. poj2456 Aggressive cows(二分查找)

    https://vjudge.net/problem/POJ-2456 二分,从最大长度开始,不断折半试,如果牛全放下了,就是可行,修改下界,否则改上届. #include<iostream&g ...

  5. 优先队列重载<运算符

    https://vjudge.net/problem/POJ-3190 #include<iostream> #include<cstdio> #include<queu ...

  6. Codeforces899D Shovel Sale(思路)

    http://codeforces.com/problemset/problem/899/D 还是得tag一下,以下代码只有G++ 14 6.4.0能过,其他都过不了不知为什么? 思路:先求出最多的9 ...

  7. JSON序列——保存修改数据

    JSON序列——保存修改数据 procedure TForm1.Button6Click(Sender: TObject); begin var delta: TynJsonDelta := TynJ ...

  8. YUV422 YUV420 Planar \ Semi-Planar \ Interleaved YCbCr与YUV

    YCbCr是DVD.摄像机.数字电视等消费类视频产品中,常用的色彩编码方案.YCbCr 有时会称为 YCC..Y'CbCr 在模拟分量视频(analog component video)中也常被称为 ...

  9. tensorflow由于未初始化变量所导致的错误

     版权声明:本文为博主原创文章,如需转载请注明出处,谢谢. https://blog.csdn.net/qq_38542085/article/details/78742295 初始代码 import ...

  10. iOS:Gif动画功能(显示gif动画、获取gif动画时长、获取gif动画执行次数)

    一.简单介绍 gif动画是iOS开发中很常用的一个功能,有的是为了显示加载视频的过程,更多的是为了显示一个结果状态(动画更直观). 那么如何执行gif动画,方法有很多.(这里只写一下方法三,前两种之前 ...