写在后面

之前,很多小伙伴私信我:如何能力疾速的把握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实现了对立的契约。你学会了吗?

举荐浏览

  • 冰河开始对Dubbo下手了!
  • 鸟瞰Dubbo全局,浏览源码前必须把握这些!!
  • 我是如何在短期内疾速把握Dubbo的原理和源码的(纯干货)?

好了,明天就到这儿吧,我是冰河,大家有啥问题能够在下方留言,一起交换技术,一起进阶,一起牛逼~~