以前,不少小伙伴私信我:如何才能快速的掌握Dubbo的核心原理和源码。因此,我写了一篇《我是如何在短时间内快速掌握Dubbo的原理和源码的(纯干货)?》。对于Dubbo的源码解析系列文章,我也在思考如何让源码解析的文章变得更加简单易懂,因此,我调整了写Dubbo源码解析文章的策略,力求让小伙伴们可以以更简单、易懂的方式完全掌握Dubbo源码。今天,咱们先说说Dubbo中的统一契约是如何实现的。java
文章已收录到:git
https://github.com/sunshinelyz/technology-binghegithub
https://gitee.com/binghe001/technology-bingheapache
URL全称为统一资源定位符,它可以在互联网中定位到惟一的一个网络地址。URL的格式以下所示。api
protocol://username:password@host:port/path?key=value&key=value
其中,各个部分的简要说明以下所示。bash
这就是咱们互联网中的URL的简单说明。微信
那么,在Dubbo内部,大量的方法接收的参数都是以URL进行封装的,那么,URL在Dubbo内部到底起到了什么做用呢?咱们继续往下看。网络
总的来讲,在Dubbo内部,服务提供者Provider会将自身的相关信息封装成URL注册到Zookeeper或其余注册中心中,从而对外暴露本身提供的服务。而服务消费者Consumer也会经过URL的形式向Zookeeper或其余注册中心订阅本身想要调用的服务。而在Dubbo的SPI实现中,URL又会参与扩展实现的逻辑处理。因此说,URL在Dubbo的实现中是很是重要的。也能够这么说,Dubbo中的URL就是Dubbo的统一契约。app
咱们先来看一下Dubbo中的URL具体长什么样吧,经过调试Dubbo自带Provider的示例源码,咱们能够看到在Dubbo中的URL以下所示。ide
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是向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是如何实现统一契约的?
这里,咱们主要经过三方面来简单聊聊URL在Dubbo内部的实际应用:
稍微了解过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的实现,咱们后面再详细剖析,今天,小伙伴们有个大体的了解便可。
在Dubbo中的服务注册实现中,URL一样起到了很是重要的做用。这里,我使用的注册中心是Zookeeper,因此,咱们在dubbo-registry-zookeeper模块中找到ZookeeperRegistry类,以下所示。
找到其中的doRegister()方法,打上断点,以下所示。
debug启动Dubbo自带的provider示例,以下所示。
能够看到,在注册到Zookeeper中的URL中,包含了protocol协议、host主机名、port端口号、path请求路径,parameters参数等信息。
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×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实现了统一的契约。你学会了吗?
好了,今天就到这儿吧,我是冰河,你们有啥问题能够在下方留言,也能够加我微信:sun_shine_lyz,一块儿交流技术,一块儿进阶,一块儿牛逼~~