1. URL
URL(Uniform Resource Locator,统一资源定位符),它是互联网的统一资源定位标志,也就是指网络地址。URL 本质上就是一个特殊格式的字符串。一个标准的 URL 格式可以包含如下的几个部分:
protocol://username:password@host:port/path?key=value&key=value
2. dubbo中的URL
在 dubbo 中,也使用了类似的 URL,主要用于在各个扩展点之间传递数据,组成此 URL 对象的具体参数如下:
- protocol:一般是 dubbo 中的各种协议 如:dubbo thrift http zk
- username/password:用户名/密码
- host/port:主机/端口
- path:接口名称
- parameters:参数键值对
任意的一个领域中的一个实现都可以认为是一类 URL,dubbo 使用 URL 来统一描述了元数据,配置信息,贯穿在整个框架之中。
// URL中主要的构造函数
public URL(String protocol,
String username,
String password,
String host,
int port,
String path,
Map<String, String> parameters) {
if (StringUtils.isEmpty(username)
&& StringUtils.isNotEmpty(password)) {
throw new IllegalArgumentException("Invalid url, password without username!");
}
this.urlAddress = new PathURLAddress(protocol, username, password, path, host, port);
this.urlParam = URLParam.parse(parameters);
this.attributes = null;
}
# 3.0之后新增的构造函数
public URL(URLAddress urlAddress, URLParam urlParam) {
this(urlAddress, urlParam, null);
}
在 dubbo 3.0 中新增了 URLAddress 和 URLParam 两个类。原来的 parameters 属性被移动到了 URLParam 中的 params,其他的属性则移动到了 URLAddress 及其子类中。
dubbo 3.0之后新增了3个 URL 子类,其中 InstanceAddressURL 属于应用级接口地址。 而 ServiceConfigURL 及 ServiceAddressURL 主要的差别就是,ServiceConfigURL 是程序读取配置文件时生成的 URL;而 ServiceAddressURL 则是注册中心推送一些信息(如 providers)过来时生成的 URL。
在 dubbo-common 包中还提供了 URL 的辅助类:URLBuilder, 辅助构造 URL;URLStrParser, 将字符串解析成 URL 对象。
3. dubbo中URL示例
3.1 URL 在 SPI 中的应用
dubbo SPI 中有一个依赖 URL 的重要场景——适配器方法,是被 @Adaptive 注解标注的 。URL 一个很重要的作用就是与 @Adaptive 注解一起选择合适的扩展实现类。
例如,在 dubbo-registry-api 模块中我们可以看到 RegistryFactory 这个接口,其中的 getRegistry() 方法上有 @Adaptive({“protocol”}) 注解,说明这是一个适配器方法,Dubbo 在运行时会为其动态生成相应的 “$Adaptive” 类型,如下所示:
@SPI(scope = APPLICATION)
public interface RegistryFactory {
/**
* Connect to the registry
* <p>
* Connecting the registry needs to support the contract: <br>
* 1. When the check=false is set, the connection is not checked, otherwise the exception is thrown when disconnection <br>
* 2. Support username:password authority authentication on URL.<br>
* 3. Support the backup=10.20.153.10 candidate registry cluster address.<br>
* 4. Support file=registry.cache local disk file cache.<br>
* 5. Support the timeout=1000 request timeout setting.<br>
* 6. Support session=60000 session timeout or expiration settings.<br>
*
* @param url Registry address, is not allowed to be empty
* @return Registry reference, never return empty value
*/
// PROTOCOL_KEY = "protocol";
@Adaptive({PROTOCOL_KEY})
Registry getRegistry(URL url);
}
public class RegistryFactory$Adaptive implements org.apache.dubbo.registry.RegistryFactory {
public org.apache.dubbo.registry.Registry getRegistry(org.apache.dubbo.common.URL arg0) {
if (arg0 == null)
throw new IllegalArgumentException("url == null");
org.apache.dubbo.common.URL url = arg0;
// // 尝试获取URL的Protocol,如果Protocol为空,则使用默认值"dubbo"
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null)
throw new IllegalStateException("Failed to get extension (org.apache.dubbo.registry.RegistryFactory) name from url (" + url.toString() + ") use keys([protocol])");
ScopeModel scopeModel =
ScopeModelUtil.getOrDefault(
url.getScopeModel(), org.apache.dubbo.registry.RegistryFactory.class);
// 根据扩展名选择相应的扩展实现
org.apache.dubbo.registry.RegistryFactory extension =
(org.apache.dubbo.registry.RegistryFactory)
scopeModel.getExtensionLoader(
org.apache.dubbo.registry.RegistryFactory.class)
.getExtension(extName);
return extension.getRegistry(arg0);
}
}
如上在生成的 RegistryFactory$Adaptive 类中会自动实现 getRegistry() 方法,其中会根据 URL 的 Protocol 确定扩展名称,从而确定使用的具体扩展实现类。
3.2 URL在服务订阅和暴露中的应用
Provider 在启动时,会将自身暴露的服务信息封装成URL注册到 ZooKeeper 上,URL 中包含了 Provider 的地址(如:192.168.0.12:20880)、暴露的接口(org.apache.dubbo.demo.DemoService)等信息, 也会根据传入的 URL 参数确定在 ZooKeeper 上创建的节点路径,还会通过 URL 中的 dynamic 参数值确定创建的 ZNode 是临时节点还是持久节点。
Consumer 启动后会向注册中心进行订阅操作,将订阅的Privder信息封装成一个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 中的上述参数将其整理成一个 ZooKeeper 路径,然后调用 zkClient 在其上添加监听。