服务注册与发现
深入理解Dubbo服务注册与发现的实现机制,掌握服务暴露和引用的完整流程,理解注册中心的作用,掌握服务发现、服务路由的实现原理。
📋 服务注册发现概览
服务注册与发现是微服务架构的核心功能,Dubbo通过注册中心实现服务的注册和发现。
🎯 一、服务暴露
1.1 服务暴露流程
1.2 ServiceConfig.export
ServiceConfig.export是服务暴露的入口:
java
public synchronized void export() {
if (provider != null) {
if (export == null) {
export = provider.getExport();
}
if (delay == null) {
delay = provider.getDelay();
}
}
if (export != null && !export) {
return;
}
if (delay != null && delay > 0) {
delayExportExecutor.schedule(() -> {
doExport();
}, delay, TimeUnit.MILLISECONDS);
} else {
doExport();
}
}doExport方法:
java
protected synchronized void doExport() {
if (unexported) {
throw new IllegalStateException("Already unexported!");
}
if (exported) {
return;
}
exported = true;
if (interfaceName == null || interfaceName.length() == 0) {
throw new IllegalStateException("interface not allow null!");
}
// 检查配置
checkDefault();
// 延迟暴露
if (delay != null && delay > 0) {
delayExportExecutor.schedule(() -> {
doExportUrls();
}, delay, TimeUnit.MILLISECONDS);
} else {
doExportUrls();
}
}1.3 注册到注册中心
RegistryProtocol.export:
java
@Override
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
URL registryUrl = getRegistryUrl(originInvoker);
URL providerUrl = getProviderUrl(originInvoker);
// 注册到注册中心
final Registry registry = getRegistry(originInvoker);
final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
registry.register(registedProviderUrl);
// 暴露服务
final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);
ProviderInvokerWrapper<T> providerInvokerWrapper = ProviderConsumerRegTable.registerProvider(
originInvoker, registryUrl, registedProviderUrl);
return new DestroyableExporter<>(exporter, () -> {
try {
providerInvokerWrapper.setReg(false);
registry.unregister(registedProviderUrl);
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
});
}🎯 二、服务引用
2.1 服务引用流程
2.2 ReferenceConfig.get
ReferenceConfig.get是服务引用的入口:
java
public synchronized T get() {
if (destroyed) {
throw new IllegalStateException("Already destroyed!");
}
if (ref == null) {
init();
}
return ref;
}init方法:
java
private void init() {
// 检查配置
checkDefault();
// 创建代理
ref = createProxy(map);
}2.3 从注册中心订阅
RegistryProtocol.refer:
java
@Override
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
url = url.setProtocol(url.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY))
.removeParameter(REGISTRY_KEY);
Registry registry = registryFactory.getRegistry(url);
if (RegistryService.class.equals(type)) {
return proxyFactory.getInvoker((T) registry, type, url);
}
// 获取组和版本
String group = url.getParameter(GROUP_KEY);
String version = url.getParameter(VERSION_KEY);
// 创建目录
return doRefer(cluster, registry, type, url, group, version);
}doRefer方法:
java
private <T> Invoker<T> doRefer(Cluster cluster, Registry registry,
Class<T> type, URL url, String group, String version) {
RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
directory.setRegistry(registry);
directory.setProtocol(protocol);
// 订阅服务
URL subscribeUrl = new URL(CONSUMER_PROTOCOL, NetUtils.getLocalHost(),
0, type.getName(), url.getParameters());
if (!ANY_VALUE.equals(url.getServiceInterface()) && url.getParameter(REGISTER_KEY, true)) {
directory.setRegisteredConsumerUrl(subscribeUrl);
registry.register(subscribeUrl);
}
directory.subscribe(subscribeUrl.addParameter(CATEGORY_KEY,
PROVIDERS_CATEGORY + "," + CONFIGURATORS_CATEGORY + "," + ROUTERS_CATEGORY));
// 构建Invoker
Invoker<T> invoker = cluster.join(directory);
ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
return invoker;
}🎯 三、注册中心
3.1 注册中心接口
Registry定义了注册中心的核心功能:
java
public interface Registry extends Node {
void register(URL url);
void unregister(URL url);
void subscribe(URL url, NotifyListener listener);
void unsubscribe(URL url, NotifyListener listener);
List<URL> lookup(URL url);
}3.2 Zookeeper注册中心
ZookeeperRegistry.register:
java
@Override
public void doRegister(URL url) {
try {
zkClient.create(toUrlPath(url), url.getParameter(DYNAMIC_KEY, true));
} catch (Throwable e) {
throw new RpcException("Failed to register " + url + " to zookeeper " +
getUrl() + ", cause: " + e.getMessage(), e);
}
}3.3 Nacos注册中心
NacosRegistry.register:
java
@Override
public void doRegister(URL url) {
String serviceName = getServiceName(url);
Instance instance = createInstance(url);
try {
namingService.registerInstance(serviceName, instance);
} catch (NacosException e) {
throw new RpcException("Failed to register " + url + " to nacos, cause: " +
e.getMessage(), e);
}
}🎯 四、服务发现
4.1 服务列表更新
当注册中心的服务列表发生变化时,会通知Directory更新:
RegistryDirectory.notify:
java
@Override
public synchronized void notify(List<URL> urls) {
Map<String, List<URL>> categoryUrls = urls.stream()
.filter(Objects::nonNull)
.filter(this::isValidCategory)
.filter(this::isNotCompatibleFor26x)
.collect(Collectors.groupingBy(url -> {
if (UrlUtils.isConfigurator(url)) {
return CONFIGURATORS_CATEGORY;
} else if (UrlUtils.isRoute(url)) {
return ROUTERS_CATEGORY;
} else if (UrlUtils.isProvider(url)) {
return PROVIDERS_CATEGORY;
}
return "";
}));
List<URL> configuratorUrls = categoryUrls.getOrDefault(CONFIGURATORS_CATEGORY,
Collections.emptyList());
this.configurators = Configurator.toConfigurators(configuratorUrls).orElse(
this.configurators);
List<URL> routerUrls = categoryUrls.getOrDefault(ROUTERS_CATEGORY,
Collections.emptyList());
toRouters(routerUrls).ifPresent(this::addRouters);
List<URL> providerUrls = categoryUrls.getOrDefault(PROVIDERS_CATEGORY,
Collections.emptyList());
refreshOverrideAndInvoker(providerUrls);
}4.2 Invoker列表刷新
refreshInvoker方法:
java
private void refreshInvoker(List<URL> invokerUrls) {
if (invokerUrls != null && invokerUrls.size() == 1
&& invokerUrls.get(0) != null
&& EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
this.forbidden = true;
this.invokers = Collections.emptyList();
routerChain.setInvokers(this.invokers);
destroyAllInvokers();
} else {
this.forbidden = false;
Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap;
if (invokerUrls == null || invokerUrls.isEmpty()) {
return;
}
Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);
this.urlInvokerMap = newUrlInvokerMap;
this.invokers = new ArrayList<>(newUrlInvokerMap.values());
routerChain.setInvokers(this.invokers);
destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap);
}
}📖 学习资源
视频教程
书籍推荐
- 《深度剖析Apache Dubbo核心技术内幕》 - 华仔
- 《分布式服务框架原理与实践》 - 李林锋
网站资源
✅ 学习检查清单
- [ ] 已理解服务暴露流程
- [ ] 已理解服务引用流程
- [ ] 已理解注册中心的作用
- [ ] 已理解服务发现机制
- [ ] 已理解服务列表更新机制
- [ ] 已绘制服务注册发现流程图
- [ ] 已总结核心知识点
上一章节:RPC原理深度解析
下一章节:负载均衡机制
返回目录:Dubbo源码深度解析