Dubbo中的统一契约是如何实现的?

不得不说的URL

URL全称为统一资源定位符,它能够在互联网中定位到唯一的一个网络地址。URL的格式如下所示。

protocol://username:password@host:port/path?key=value&key=value

其中,各个部分的简要说明如下所示。

  • protocol:URL的协议。最常见的协议就是HTTP和HTTPS,其他的还有FTP、WS、FILE、SMTP等。
  • username:用户名。
  • password:密码。
  • host:主机,通常是域名或者IP地址。
  • port:主机的端口号。
  • path:请求的目标文件的路径。
  • parameters:请求的具体参数信息,这里为key=value&key=value。

这就是我们互联网中的URL的简单说明。

那么,在Dubbo内部,大量的方法接收的参数都是以URL进行封装的,那么,URL在Dubbo内部到底起到了什么作用呢?我们继续往下看。

Dubbo中的URL

总的来说,在Dubbo内部,服务提供者Provider会将自身的相关信息封装成URL注册到Zookeeper或其他注册中心中,从而对外暴露自己提供的服务。而服务消费者Consumer也会通过URL的形式向Zookeeper或其他注册中心订阅自己想要调用的服务。而在Dubbo的SPI实现中,URL又会参与扩展实现的逻辑处理。所以说,URL在Dubbo的实现中是非常重要的。也可以这么说,Dubbo中的URL就是Dubbo的统一契约。

我们先来看一下Dubbo中的URL具体长什么样吧,通过调试Dubbo自带Provider的示例源码,我们可以看到在Dubbo中的URL如下所示。

dubbo://192.168.175.1:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-annotation-provider&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello,sayHelloAsync&pid=15012&release=&side=provider×tamp=1610857629484

这也是Provider注册到Zookeeper或者其他注册中心的信息。各个部分的说明如下所示。

  • dubbo:使用的是dubbo协议。
  • host:主机的IP地址为192.168.175.1。
  • port:端口号为20880。
  • path:这里的请求路径为:org.apache.dubbo.demo.DemoService
  • parameters:请求的参数信息,这里为:anyhost=true&application=dubbo-demo-annotation-provider&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello,sayHelloAsync&pid=15012&release=&side=provider×tamp=1610857629484。

既然Dubbo是向Zookeeper或其他注册中心注册这些信息的,那Dubbo内部是如何对URL进行封装的呢。

在dubbo-common模块中,有一个URL类专门用于封装URL,如下所示。

[图片上传失败...(image-5ae4a5-1611283308296)]

在URL类中,我们来看一个核心构造函数,如下所示。

public URL(String protocol,
           String username,
           String password,
           String host,
           int port,
           String path,
           Map<String, String> parameters,
           Map<String, Map<String, String>> methodParameters) {
    if (StringUtils.isEmpty(username)
        && StringUtils.isNotEmpty(password)) {
        throw new IllegalArgumentException("Invalid url, password without username!");
    }
    this.protocol = protocol;
    this.username = username;
    this.password = password;
    this.host = host;
    this.port = Math.max(port, 0);
    this.address = getAddress(this.host, this.port);

    // trim the beginning "/"
    while (path != null && path.startsWith("/")) {
        path = path.substring(1);
    }
    this.path = path;
    if (parameters == null) {
        parameters = new HashMap<>();
    } else {
        parameters = new HashMap<>(parameters);
    }
    this.parameters = Collections.unmodifiableMap(parameters);
    this.methodParameters = Collections.unmodifiableMap(methodParameters);
}

可以看到,Dubbo对于URL的核心封装,基本与互联网中的URL封装是一致的。

在Dubbo的dubbo-common模块提供了处理URL的工具类:URLBuilder和URLStrParser。如下所示。

image

这两个类的实现还是比较简单的,小伙伴们可以自行阅读Dubbo的源码。

接下来,我们一起来看看在Dubbo内部,URL是如何实现统一契约的?

Dubbo中URL的实际应用

这里,我们主要通过三方面来简单聊聊URL在Dubbo内部的实际应用:

  • URL在SPI中的应用。
  • URL在服务注册中的应用。
  • URL在服务发现中的应用。

URL在SPI中的应用

稍微了解过Dubbo的小伙伴都知道,Dubbo具有高度的可扩展性,而这种扩展性是基于Dubbo自身的SPI来实现的。在Dubbo实现的SPI中,URL又起到了非常重要的作用。

在Dubbo SPI的实现中,一个典型的场景就是被@Adaptive注解修饰的接口方法,例如,在dubbo-registry-api 模块中的RegistryFactory接口中的getRegistry()方法上被@Adaptive({"protocol"})注解修饰。如下所示。

image

说明RegistryFactory接口中的getRegistry()方法是一个适配器方法,Dubbo在运行的过程中,会为getRegistry()方法动态生成 RegistryFactoryAdaptive 类型。例如,生成的RegistryFactoryAdaptive 类型如下所示。

public class RegistryFactory$Adaptive
              implements RegistryFactory { 
    public Registry getRegistry(org.apache.dubbo.common.URL arg0) { 
        if (arg0 == null) throw new IllegalArgumentException(""); 
        org.apache.dubbo.common.URL url = arg0; 
        String extName = (url.getProtocol() == null ? "dubbo" :  url.getProtocol()); 
        if (extName == null) 
            throw new IllegalStateException(""); 
        RegistryFactory extension = (RegistryFactory) ExtensionLoader.getExtensionLoader(RegistryFactory.class).getExtension(extName); 
        return extension.getRegistry(arg0); 
    } 
}

这段代码相对来说还是比较容易理解的,生成的RegistryFactory$Adaptive会自动实现getRegistry()方法,在getRegistry()方法中,会获取URL中的protocol参数来确定URL的协议,如果获取的protocol为空,则使用默认的dubbo协议,有了这个协议,就能够通过SPI动态加载具体的扩展实现类。

我们在Dubbo的dubbo-registry-api模块中找到RegistryProtocol类,如下所示。

image

找到其中的getRegistry()方法并打上断点,如下所示。

image

接下来,debug启动Dubbo的Provider示例,如下所示。

[图片上传失败...(image-6775d6-1611283308296)]

可以看到,此时使用的protocol协议为zookeeper。有关Dubbo中SPI的实现,我们后面再详细剖析,今天,小伙伴们有个大致的了解即可。

URL在服务注册中的应用

在Dubbo中的服务注册实现中,URL同样起到了非常重要的作用。这里,我使用的注册中心是Zookeeper,所以,我们在dubbo-registry-zookeeper模块中找到ZookeeperRegistry类,如下所示。

image

找到其中的doRegister()方法,打上断点,如下所示。

image

debug启动Dubbo自带的provider示例,如下所示。

image

可以看到,在注册到Zookeeper中的URL中,包含了protocol协议、host主机名、port端口号、path请求路径,parameters参数等信息。

URL在服务发现中的应用

Dubbo中服务的消费者Consumer在启动时,会向Zookeeper注册中心订阅自身需要调用的服务,那具体是如何通过URL订阅的呢?我们同样在dubbo-registry-zookeeper模块中的ZookeeperRegistry类中找到doSubscribe()方法。在doSubscribe()方法中打上断点,如下所示。

image

启动Dubbo自带的Consumer示例,如下所示。

image

我们可以看到,Dubbo的Consumer会向Zookeeper传入如下参数进行服务的订阅操作。

consumer://192.168.175.1/org.apache.dubbo.demo.DemoService?application=dubbo-demo-annotation-consumer&category=providers,configurators,routers&dubbo=2.0.2&init=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello,sayHelloAsync&pid=15184&side=consumer&sticky=false×tamp=1610860963037

其中的protocol为consumer,表示订阅协议。category表示要订阅的分类,这里是providers,configurators,routers三个分类。interface表示要订阅的接口服务,这里是org.apache.dubbo.demo.DemoService。methods表示要订阅的方法,这里是sayHello,sayHelloAsync。

还有一点需要注意的是:在服务注册的过程中,Dubbo会将URL转化为Zookeeper路径将信息注册到Zookeeper中;在服务发现的过程中,Dubbo会将URL转化为Zookeeper路径,从而监听Zookeeper目录的变化来订阅相关的服务。

总之,在Dubbo内部通过URL实现了统一的契约。你学会了吗?

来源:https://www.tuicool.com/articles/ArUju2I

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,417评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,921评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,850评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,945评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,069评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,188评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,239评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,994评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,409评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,735评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,898评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,578评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,205评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,916评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,156评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,722评论 2 363
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,781评论 2 351

推荐阅读更多精彩内容

  • 1.概述 RPC作为分布式系统中不可或缺的中间件,在业界已经具有相当成熟的技术实现,其中Dubbo应用得特别广泛,...
    prozombie阅读 1,778评论 0 4
  • 暴露服务的过程中,会涉及到两个Protocol DubboProtocol主要是做网络通信相关初始化 Regist...
    _六道木阅读 557评论 0 0
  • 目录 1 应用架构演变 2 RPC 3 Dubbo概述 4 Dubbo配置 5 Dubbo协议 6 高...
    小小千千阅读 559评论 0 0
  • 6.4Dubbo 的服务引入过程 dubbo 服务的引入过程,是在 referencebean 的实例化过程中实现...
    Curtain_call阅读 508评论 0 1
  • 夜莺2517阅读 127,717评论 1 9