URL——Dubbo 的配置总线
URL(Uniform Resource Locator
,统一资源定位符),它是互联网的统一资源定位标志,也就是指网络地址。
URL 本质上就是一个特殊格式的字符串。一个标准的 URL 格式可以包含如下的几个部分:
protocol://username:password@host:port/path?key=value&key=value
-
protocol
:URL 的协议。我们常见的就是 HTTP 协议和 HTTPS 协议,当然,还有其他协议,如 FTP 协议、SMTP 协议等。 -
username/password
:用户名/密码。 HTTP Basic Authentication 中多会使用在 URL 的协议之后直接携带用户名和密码的方式。 -
host/port
:主机/端口。在实践中一般会使用域名,而不是使用具体的 host 和 port。 -
path
:请求的路径。 -
parameters
:参数键值对。一般在 GET 请求中会将参数放到 URL 中,POST 请求会将参数放到请求体中。
Dubbo 中的 URL
Dubbo 中任意的一个实现都可以抽象为一个 URL,Dubbo 使用 URL 来统一描述了所有对象和配置信息,并贯穿在整个 Dubbo 框架之中。这里我们来看 Dubbo 中一个典型 URL 的示例,如下:
dubbo://172.17.32.91:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-api-provider&dubbo=2.0.2&interface=org.apache.dubbo.demo.DemoService&methods=sayHello,sayHelloAsync&pid=32508&release=&side=provider×tamp=1593253404714dubbo://172.17.32.91:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-api-provider&dubbo=2.0.2&interface=org.apache.dubbo.demo.DemoService&methods=sayHello,sayHelloAsync&pid=32508&release=&side=provider×tamp=1593253404714
这个 Demo Provider 注册到 ZooKeeper 上的 URL 信息,简单解析一下这个 URL 的各个部分:
-
protocol
:dubbo 协议。 -
username/password
:没有用户名和密码。 -
host/port
:172.17.32.91:20880。 -
path
:org.apache.dubbo.demo.DemoService。 -
parameters
:参数键值对,这里是问号后面的参数。
下面是 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");
}
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);
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);
}
使用 URL 的好处
- 使用 URL 这种公共契约进行上下文信息传递,最重要的就是代码更加易读、易懂,不用花大量时间去揣测传递数据的格式和含义,进而形成一个统一的规范,使得代码易写、易读。
- 使用 URL 作为方法的入参(相当于一个 Key/Value 都是 String 的 Map),它所表达的含义比单个参数更丰富,当代码需要扩展的时候,可以将新的参数以 Key/Value 的形式追加到 URL 之中,而不需要改变入参或是返回值的结构。
- 使用 URL 这种“公共的契约”可以简化沟通,人与人之间的沟通消耗是非常大的,信息传递的效率非常低,使用统一的契约、术语、词汇范围,可以省去很多沟通成本,尽可能地提高沟通效率。
URL 在服务暴露中的应用
Provider
在启动时,会将自身暴露的服务注册到ZooKeeper
上,具体是注册哪些信息到 ZooKeeper
上呢?我们来看 ZookeeperRegistry.doRegister()
方法,在其中打个断点,然后 Debug 启动 Provider
,会得到下图:
传入的 URL 中包含了 Provider
的地址(172.18.112.15:20880)、暴露的接口(org.apache.dubbo.demo.DemoService)
等信息, toUrlPath()
方法会根据传入的 URL 参数确定在 ZooKeeper
上创建的节点路径,还会通过 URL 中的 dynamic
参数值确定创建的 ZNode
是临时节点还是持久节点。
URL 在服务订阅中的应用
Consumer
启动后会向注册中心进行订阅操作,并监听自己关注的 Provider
。那 Consumer
是如何告诉注册中心自己关注哪些 Provider
呢?
我们来看 ZookeeperRegistry
这个实现类,它是由上面的 ZookeeperRegistryFactory
工厂类创建的 Registry
接口实现,其中的 doSubscribe()
方法是订阅操作的核心实现,在第 175 行打一个断点,并 Debug 启动 Demo 中 Consumer
,会得到下图所示的内容:
我们看到传入的 URL 参数如下:
consumer://...?application=dubbo-demo-api-consumer&category=providers,configurators,routers&interface=org.apache.dubbo.demo.DemoService...
其中 Protocol
为 consumer
,表示是 Consumer
的订阅协议,其中的 category
参数表示要订阅的分类,这里要订阅 providers、configurators 以及 routers
三个分类;interface
参数表示订阅哪个服务接口,这里要订阅的是暴露 org.apache.dubbo.demo.DemoService
实现的 Provider
。
通过 URL 中的上述参数,ZookeeperRegistry
会在 toCategoriesPath()
方法中将其整理成一个 ZooKeeper路径
,然后调用 zkClient
在其上添加监听。
通过上述示例,相信你已经感觉到 URL 在 Dubbo 体系中称为“总线”或是“契约”的原因了,在后面的源码分析中,我们还将看到更多关于 URL 的实现。