写在前面

之前,很多小伙伴私信我:如何才能快速的掌握Dubbo的核心原理和源码。所以,我写了一篇《我是如何在短期内快速掌握Dubbo的原理和源码的(纯干货)?》。对于Dubbo的源码解析系列文章,我也在思考如何让源码解析的文章变得更加简单易懂,所以,我调整了写Dubbo源码解析文章的策略,力求让小伙伴们能够以更简单、易懂的方式彻底掌握Dubbo源码。今天,我们先说说Dubbo中的统一契约是如何实现的。

文章已收录到:

https://github.com/sunshinelyz/technology-binghe

https://gitee.com/binghe001/technology-binghe

不得不说的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&timestamp=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&timestamp=1610857629484。

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

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

在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。如下所示。

这两个类的实现还是比较简单的,小伙伴们可以自行阅读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"})注解修饰。如下所示。

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

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类,如下所示。

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

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

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

URL在服务注册中的应用

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

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

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

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

URL在服务发现中的应用

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

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

我们可以看到,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&timestamp=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实现了统一的契约。你学会了吗?

推荐阅读

好了,今天就到这儿吧,我是冰河,大家有啥问题可以在下方留言,也可以加我微信:sun_shine_lyz,一起交流技术,一起进阶,一起牛逼~~

Dubbo中的统一契约是如何实现的?的更多相关文章

  1. Dubbo(五):Dubbo中的URL统一资源模型与Dubbo协议

    一.URL简介 URL也就是Uniform Resource Locator,中文叫统一资源定位符.Dubbo中无论是服务消费方,或者服务提供方,或者注册中心.都是通过URL进行定位资源的.所以今天来 ...

  2. 服务化改造实践 | 如何在 Dubbo 中支持 REST

    什么是 REST REST 是 Roy Thomas Fielding [[1]](#fn1) 在 2000 年他的博士论文 [[2]](#fn2) “架构风格以及基于网络的软件架构设计” 中提出来的 ...

  3. Dubbo中对Spring配置标签扩展

    Spring提供了可扩展Schema的支持,完成一个自定义配置一般需要以下步骤: 设计配置属性和JavaBean 编写XSD文件 编写NamespaceHandler和BeanDefinitionPa ...

  4. 架构师之路-在Dubbo中开发REST风格的远程调用

    架构师之路:从无到有搭建中小型互联网公司后台服务架构与运维架构 http://www.roncoo.com/course/view/ae1dbb70496349d3a8899b6c68f7d10b 概 ...

  5. Dubbo中编码和解码的解析

    (这里做的解析不是很详细,等到走完整个流程再来解析)Dubbo中编解码的工作由Codec2接口的实现来处理,回想一下第一次接触到Codec2相关的内容是在服务端暴露服务的时候,根据具体的协议去暴露服务 ...

  6. Dubbo中暴露服务的过程解析

    dubbo暴露服务有两种情况,一种是设置了延迟暴露(比如delay="5000"),另外一种是没有设置延迟暴露或者延迟设置为-1(delay="-1"): 设置 ...

  7. 【Rest】在Dubbo中开发REST风格的远程调用(RESTful Remoting)

    目录 概述 REST的优点 应用场景 快速入门 标准Java REST API:JAX-RS简介 REST服务提供端详解 HTTP POST/GET的实现 Annotation放在接口类还是实现类 J ...

  8. 在Dubbo中开发REST风格的远程调用(RESTful Remoting)

    rest 在Dubbo中开发REST风格的远程调用(RESTful Remoting)

  9. Dubbo中订阅和通知解析

    Dubbo中关于服务的订阅和通知主要发生在服务提供方暴露服务的过程和服务消费方初始化时候引用服务的过程中. 2345678910111213141516171819 public <T> ...

随机推荐

  1. Tokyo 五年 IT 生活

    今天阳光甚好,在家中小屋,闲来无事,回顾一下这五年的历程.我想从来东京的缘由.东京的环境.生活.IT这四个方面介绍一下. 首先,说一下为什么我会来到东京. 电子信息专业毕业,大学实验室学习IT,毕业后 ...

  2. centos 7 让脚本开机运行

    按照网上搜索的文章指导,有三种方式可以实现,个人觉得比较简单的是修改/etc/rc.d/rc.local,添加要开机执行的命令. 修改后,重启,发现要运行的服务没有启动. 搜索发现下面这篇博客解释了原 ...

  3. 面试 09-02.js运行机制:异步和单线程

    09-02.js运行机制:异步和单线程 #前言 面试时,关于同步和异步,可能会问以下问题: 同步和异步的区别是什么?分别举一个同步和异步的例子 一个关于 setTimeout 的笔试题 前端使用异步的 ...

  4. [日常摸鱼]poj1741Tree-点分治

    还有两天就要去FJWC啦- 题意:一颗无根树,$k$为给定常数,求树上距离不超过$k$的点对的数量,多组数据,$n \leq 10^4$. 应该是点分治经典题~ 一般对于无根树我们都可以把它转变成有根 ...

  5. kali2020创建root用户

    自kali更新到2020版后,默认取消了root用户的登录权限.只能用普通用户登录,这样做的优点在于对于kali的新手,在不懂的部分命令的情况下对系统的损害有所降低,也就说安全性提高了.但是普通用户权 ...

  6. vue API 知识点(3) --- 实例 总结

    一.实例 property 1.vm.$data Vue 实例观察的数据对象,Vue 实例代理了对其 data 对象 property 的的访问 2.vm.$props 当前组件接收到的 props ...

  7. k8s的kube-proxy

    kube-proxy 运行在每个节点上,监听 API Server 中服务对象的变化,再通过管理 IPtables 来实现网络的转发. Kube-Proxy 不同的版本可支持三种工作模式:   Use ...

  8. Autofac的基本使用---2、普通类型

    Autofac的基本使用---目录 准备 使用的表是Student,创建相关的IDAL.DAL.IBLL.BLL层. 使用EF,创建一个Model层,存放edmx文件. 控制台程序的使用 using ...

  9. Windows 系列GVLK密钥

    以下是GVLK密钥版本对照表,可配合KMS服务器进行使用. Windows 系列GVLK密钥 Windows Server 2019 Operating system edition KMS Clie ...

  10. positive-unlabeled (PU) learning

    PULearning的应用场景是,我们可以清晰地确定正样本,但是不能确定负样本,因为它有可能是正样本,只是我们还没有证明. 这时我们可以把这部分不确定的样本称为无标签样本U,加上正样本P来建立模型. ...