dubbo源码解析-服务暴露与发现
Dubbo是一款高性能、轻量级的分布式服务框架,其中服务暴露与发现是其核心功能之一。在本篇文章中,我们将介绍Dubbo在服务暴露和发现方面的实现原理。
服务暴露
服务暴露的过程是将服务接口以及实现类发布到注册中心,以便其他应用程序可以发现并进行调用。Dubbo的服务暴露过程通常涉及到以下几个步骤:
创建服务接口代理对象:Dubbo使用Java Proxy机制为服务接口创建代理对象。
封装服务暴露信息:将服务的接口名称、版本号、实现类、调用协议、序列化方式等信息封装到URL对象中,并通过Dubbo SPI机制获取相应的协议实现。
启动协议服务:将封装好的URL对象传递给协议实现并启动服务器,协议实现负责监听端口并接收来自其他应用程序的调用请求。
将服务暴露到注册中心:将服务暴露所需的信息注册到注册中心,供其他应用程序查找和调用。
以下是服务暴露的核心代码片段:
public void export() {
// 1. 创建服务代理对象
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryUrl.addParameterAndEncoded(REFER_KEY, URL.encode(url.toFullString())));
// 2. 创建URL,并将服务暴露信息封装到URL中
URL monitorUrl = ConfigValidationUtils.loadMonitor(this, registryUrl);
if (monitorUrl != null) {
url = url.addParameterIfAbsent(MONITOR_KEY, monitorUrl.toString());
}
// 将服务的接口名称、版本号、实现类、调用协议、序列化方式等信息封装到URL对象中
Map<String, String> map = new HashMap<String, String>();
map.put(SIDE_KEY, PROVIDER_SIDE);
appendRuntimeParameters(map);
// 添加服务协议配置
appendProtocolParameters(map);
// 添加服务元数据配置
appendParameters(map, getMetrics());
// 封装服务暴露信息到URL中
if (CollectionUtils.isNotEmpty(getMethods())) {
for (MethodConfig methodConfig : getMethods()) {
appendParameters(map, methodConfig, methodConfig.getName());
}
}
// 将URL参数封装到URL中
url = url.addParameters(map)
.addParameterAndEncoded(EXPORT_KEY, invoker.getUrl().toFullString());
if (StringUtils.isNotEmpty(token)) {
if (ConfigUtils.isDefault(token)) {
token = UUID.randomUUID().toString();
}
url = url.addParameter(TOKEN_KEY, token);
}
// 3. 启动协议服务
String scope = url.getParameter(SCOPE_KEY);
// 通过SPI机制获取协议实现
Protocol protocol = extensionLoader.getExtension(scope != null && scope.length() > 0 ? scope : getDefaultProtocol()).getAdaptiveProtocol();
// 启动协议服务
exporter = protocol.export(invoker);
// 4. 将服务暴露到注册中心
// 获取注册中心实现
Registry registry = registryFactory.getRegistry(registryUrl);
// 注册服务
registry.register(getRegisterUrl());
}
服务发现
在Dubbo中,服务发现是用来查找远程服务的过程。它通过从注册中心查找已注册的服务,获得该服务的地址列表,进而构建服务代理对象,以便进行调用。Dubbo服务发现的流程通常可以分为以下几个步骤:
解析服务URL:Dubbo将服务URL解析为接口名称、版本号以及协议等相关信息。
获取注册中心:Dubbo通过配置文件获取注册中心的地址,并通过Dubbo SPI机制获取相应的注册中心实现。
从注册中心获取服务地址列表:Dubbo通过注册中心获取服务的地址列表,并对地址列表进行缓存处理,以避免频繁的网络调用。
构建服务代理对象:Dubbo使用Java Proxy机制为服务接口创建代理对象,并通过封装好的地址列表构造Invoker对象。
调用远程服务:Dubbo将调用请求发送到远程服务,获取调用结果并将其返回给调用方。
以下是服务发现的核心代码片段:
public T refer(Class type, URL url) throws RpcException {
// 解析出接口名称、版本号以及协议等相关信息
url = URLBuilder.from(url)
.setProtocol(Constants.REGISTRY_PROTOCOL) // 设置协议为注册中心协议
.addParameterAndEncoded(REFER_KEY, url.toFullString()) // 添加引用配置
.build();
// 获取注册中心对象
Registry registry = registryFactory.getRegistry(url);
// 获取服务代理对象
if (RegistryService.class.equals(type)) {
return proxyFactory.getProxy(invokerFactory.getInvoker(registry, type, url));
}
// 将调用的服务接口名称和版本号封装到URL中
Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(REFER_KEY));
String group = qs.get(Constants.GROUP_KEY);
String version = qs.get(Constants.VERSION_KEY);
if (group != null && group.length() > 0) {
url = url.addParameter(Constants.GROUP_KEY, group);
}
if (version != null && version.length() > 0) {
url = url.addParameter(Constants.VERSION_KEY, version);
}
// 从缓存中获取服务地址列表
String key = url.toServiceString();
ReferenceConfigCache cache = ReferenceConfigCache.getCache();
T ref = (T) cache.get(key);
if (ref == null) {
// 获取服务地址列表
List urls = new ArrayList();
// 参数省略...
if (urls == null || urls.isEmpty()) {
throw new RpcException("No provider available for the service "
+ type.getName() + " from registry " + url.getRegistryAddress()
+ " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version "
+ Version.getVersion() + ". Please check if the providers have been started and registered.");
}
// 构建服务代理对象
List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
for (URL u : urls) {
invokers.add(proxyFactory.getInvoker((T) type, u));
}
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invokers.get(0), type, urls.get(0));
ref = proxyFactory.getProxy(wrapperInvoker);
cache.put(key, ref);
}
return ref;
}
总结
服务暴露与发现是Dubbo的核心功能之一,也是基于Dubbo构建其他应用程序的核心能力。在服务暴露的过程中,Dubbo需要将服务信息封装到URL中,并通过SPI机制获取相应的协议实现,启动协议服务,并将服务信息注册到注册中心中。而在服务发现的过程中,Dubbo需要通过解析服务URL获取相关信息,获取注册中心实例,从注册中心获取服务地址列表,构建服务代理对象并进行调用。掌握Dubbo的服务暴露与发现过程,有助于我们更加深入理解Dubbo的实现原理。