文章目录
前言
Dubbo的服务注册原理主要基于注册中心来实现服务的动态发布和引用,其中Zookeeper是常用的注册中心之一
本文是基于 dubbo-3.1.0 版本,zookeeper注册中心进行分析
一、getRegistry
1、RegistryProtocol.export
@Override
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
// url to registry 获取注册中心
final Registry registry = getRegistry(registryUrl);
final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);
// decide if we need to delay publish (provider itself and registry should both need to register)
// 是否延迟发布 提供者本身和注册表都需要注册
boolean register = providerUrl.getParameter(REGISTER_KEY, true) && registryUrl.getParameter(REGISTER_KEY, true);
if (register) {
// 存在注册中心,需要注册
register(registry, registeredProviderUrl);
}
}
2、RegistryProtocol.getRegistry
- 获取对应的registryFactory
- 根据具体协议,从registryFactory中获得指定的注册中心实现
从org.apache.dubbo.registry.RegistryFactory,可以看到扩展点有
service-discovery-registry=org.apache.dubbo.registry.client.ServiceDiscoveryRegistryFactory
wrapper=org.apache.dubbo.registry.RegistryFactoryWrapper
multicast=org.apache.dubbo.registry.multicast.MulticastRegistryFactory
zookeeper=org.apache.dubbo.registry.zookeeper.ZookeeperRegistryFactory
nacos=org.apache.dubbo.registry.nacos.NacosRegistryFactory
multiple=org.apache.dubbo.registry.multiple.MultipleRegistryFactory
protected Registry getRegistry(final URL registryUrl) {
// 获取registryFactory扩展点
RegistryFactory registryFactory = ScopeModelUtil.getExtensionLoader(RegistryFactory.class, registryUrl.getScopeModel()).getAdaptiveExtension();
// 通过扩展点获取对应的注册中心
return registryFactory.getRegistry(registryUrl);
}
3、RegistryFactory.getRegistry
RegistryFactory的实现, 发现它里面有一个自适应的方法,根据url中protocol传入的值进行适配
@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
*/
@Adaptive({PROTOCOL_KEY})
Registry getRegistry(URL url);
}
代理类为RegistryFactory$Adaptive
如果没有特殊配置,那从发布的原理我们知道,url中的protocol默认为zookeeper,那么这个时候根据zookeeper获得的spi扩展点应该是RegistryFactoryWrapper->ZookeeperRegistryFactory
通过RegistryFactoryWrapper的装饰,最后返回ListenerRegistryWrapper的包装类
4、AbstractRegistryFactory.getRegistry
通过锁保证只创建一个registry
public abstract class AbstractRegistryFactory implements RegistryFactory, ScopeModelAware {
@Override
public Registry getRegistry(URL url) {
// 省略部分代码***
// Lock the registry access process to ensure a single instance of the registry
registryManager.getRegistryLock().lock();
try {
// double check
// fix https://github.com/apache/dubbo/issues/7265.
defaultNopRegistry = registryManager.getDefaultNopRegistryIfDestroyed();
if (null != defaultNopRegistry) {
return defaultNopRegistry;
}
// 获取缓存
registry = registryManager.getRegistry(key);
if (registry != null) {
return registry;
}
// 创建registry ,交由具体的扩展点去实现,这里是ZookeeperRegistryFactory
// create registry by spi/ioc
registry = createRegistry(url);
if (check && registry == null) {
throw new IllegalStateException("Can not create registry " + url);
}
if (registry != null) {
registryManager.putRegistry(key, registry);
}
} catch (Exception e) {
if (check) {
throw new RuntimeException("Can not create registry " + url, e);
} else {
// 1-11 Failed to obtain or create registry (service) object.
LOGGER.warn("1-11", "", "",
"Failed to obtain or create registry ", e);
}
} finally {
// Release the lock
registryManager.getRegistryLock().unlock();
}
return registry;
}
}
一、getRegistry
1、RegistryProtocol.export
@Override
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
// url to registry 获取注册中心
final Registry registry = getRegistry(registryUrl);
final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);
// decide if we need to delay publish (provider itself and registry should both need to register)
// 是否延迟发布 提供者本身和注册表都需要注册
boolean register = providerUrl.getParameter(REGISTER_KEY, true) && registryUrl.getParameter(REGISTER_KEY, true);
if (register) {
// 存在注册中心,需要注册
register(registry, registeredProviderUrl);
}
}
private void register(Registry registry, URL registeredProviderUrl) {
registry.register(registeredProviderUrl);
}
2、ListenerRegistryWrapper.register
通过上面我们可以知道返回的是ListenerRegistryWrapper的包装类,目的是增加了一个监听器的处理过程。
public class ListenerRegistryWrapper implements Registry {
@Override
public void register(URL url) {
try {
if (registry != null) {
registry.register(url);
}
} finally {
if (CollectionUtils.isNotEmpty(listeners) && !UrlUtils.isConsumer(url)) {
RuntimeException exception = null;
for (RegistryServiceListener listener : listeners) {
if (listener != null) {
try {
// 后置处理 监控拓展
listener.onRegister(url, registry);
} catch (RuntimeException t) {
logger.error(t.getMessage(), t);
exception = t;
}
}
}
if (exception != null) {
throw exception;
}
}
}
}
}
3、FailbackRegistry.register
public abstract class FailbackRegistry extends AbstractRegistry {
@Override
public void register(URL url) {
if (!acceptable(url)) {
logger.info("URL " + url + " will not be registered to Registry. Registry " + this.getUrl() + " does not accept service of this protocol type.");
return;
}
super.register(url);
removeFailedRegistered(url);
removeFailedUnregistered(url);
try {
// Sending a registration request to the server side
// 开始注册
doRegister(url);
} catch (Exception e) {
Throwable t = e;
// If the startup detection is opened, the Exception is thrown directly.
boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
&& url.getParameter(Constants.CHECK_KEY, true)
&& (url.getPort() != 0);
boolean skipFailback = t instanceof SkipFailbackWrapperException;
if (check || skipFailback) {
if (skipFailback) {
t = t.getCause();
}
throw new IllegalStateException("Failed to register " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t);
} else {
logger.error("Failed to register " + url + ", waiting for retry, cause: " + t.getMessage(), t);
}
// Record a failed registration request to a failed list, retry regularly
addFailedRegistered(url);
}
}
}
4、ZookeeperRegistry.doRegister
public class ZookeeperRegistry extends CacheableFailbackRegistry {
@Override
public void doRegister(URL url) {
try {
checkDestroyed();
// 想zk中创建节点 实现zk监听的机制
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);
}
}
}
通过zk节点的注册,就完成了服务注册了
总结
Dubbo的服务注册原理基于注册中心实现服务的动态发布和引用。服务提供者在启动时将自己的服务信息注册到注册中心,服务消费者在需要调用服务时从注册中心查询服务提供者列表,并进行服务调用。注册中心在Dubbo系统中占据了重要的地位,它实现了服务的动态发布和引用、服务健康检查、负载均衡和配置管理等功能,为Dubbo服务的稳定运行提供了有力保障。