服务注册流程(八)
了解了服务的发布之后,我们继续来看一下服务是如何发起注册的。服务注册实际上就是把dubbo的协议url地址保存到第三方注册中心上。
final Registry registry = getRegistry(originInvoker);
getRegistry
1. 把url转化为对应配置的注册中心的具体协议
2. 根据具体协议,从registryFactory中获得指定的注册中心实现
那么这个registryFactory具体是怎么赋值的呢?
protected Registry getRegistry(final Invoker<?> originInvoker) { // 把 url 转化为对应配置的注册中心的具体协议 如:zookeeper://ip:port这样后续获得的注册中心就会是基于 //zk的实现 URL registryUrl = getRegistryUrl(originInvoker); // 根据具体协议从registryFactory获取指定的注册中心实现 ZookeeperRegistry return registryFactory.getRegistry(registryUrl); }
在RegistryProtocol中存在一段这样的代码,很明显这是通过依赖注入来实现的扩展点。
private RegistryFactory registryFactory; public void setRegistryFactory(RegistryFactory registryFactory) { this.registryFactory = registryFactory; }
按照扩展点的加载规则,我们可以先看看/META-INF/dubbo/internal路径下找到RegistryFactory的配置文件.这个factory有多个扩展点的实现。
dubbo=org.apache.dubbo.registry.dubbo.DubboRegistryFactory multicast=org.apache.dubbo.registry.multicast.MulticastRegistryFactory zookeeper=org.apache.dubbo.registry.zookeeper.ZookeeperRegistryFactory redis=org.apache.dubbo.registry.redis.RedisRegistryFactory consul=org.apache.dubbo.registry.consul.ConsulRegistryFactory etcd3=org.apache.dubbo.registry.etcd.EtcdRegistryFactory
接着,找到RegistryFactory的实现, 发现它里面有一个自适应的方法,根据url中protocol传入的值进行适配
@SPI("dubbo") public interface RegistryFactory { @Adaptive({"protocol"}) Registry getRegistry(URL url);
RegistryFactory$Adaptive
由于在前面的代码中,url中的protocol已经改成了zookeeper,那么这个时候根据zookeeper获得的spi扩展点应该是RegistryFactoryWrapper
import org.apache.dubbo.common.extension.ExtensionLoader; 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; 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])"); org.apache.dubbo.registry.RegistryFactory extension = (org.apache.dubbo.registry.RegistryFactory)ExtensionLoader.getExtensionLoader(or g.apache.dubbo.registry.RegistryFactory.class).getExtension(extName); return extension.getRegistry(arg0); } }
RegistryFactoryWrapper
而registryFactory.getRegistry(url)中,由于此时的registryFactory已经是ZookeeperRegistryFactory,所以这里会得到一个zookeeperRegistry。
public class RegistryFactoryWrapper implements RegistryFactory { private RegistryFactory registryFactory; public RegistryFactoryWrapper(RegistryFactory registryFactory) { this.registryFactory = registryFactory; } @Override public Registry getRegistry(URL url) { return new ListenerRegistryWrapper(registryFactory.getRegistry(url), Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(RegistryServiceListener.class) .getActivateExtension(url, "registry.listeners"))); } }
经过这一路跟踪可以发现final Registry registry = getRegistry(originInvoker);这句话最终返回的Registry=ListenerRegistryWrapper。
下面再返回RegistryProtocol类中的export方法看下面代码,面这段代码的含义是:
- 获得注册的服务提供者地址
- 调用register发起注册
// 获取已注册的服务提供者 URL,比如:
// dubbo://172.17.48.52:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.2&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello
final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);
// decide if we need to delay publish
// 标注是否需要延期发布
boolean register = providerUrl.getParameter(REGISTER_KEY, true);
if (register) {
//注册服务
register(registryUrl, registeredProviderUrl);
}
RegistryProtocol.register
发起注册流程,registry对象的实例是=ListenerRegistryWrapper。所以调用这个对象的register方法。
private void register(URL registryUrl, URL registeredProviderUrl) { // 获取 Registry。这里的registryFactory是RegistryFactory$Adaptive Registry registry = registryFactory.getRegistry(registryUrl); // 注册服务 registry.register(registeredProviderUrl); }
这里的registryFactory是RegistryFactory$Adaptive
做了两件事
- 获取 Registry注册中心实例
- 注册服务
创建注册中心实例
先看registryFactory.getRegistry
1.调用接口RegistryFactory
@SPI("dubbo") 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"}) Registry getRegistry(URL url); }
2.ExtensionLoader.getExtensionLoader()
入参type=interface com.alibaba.dubbo.registry.RegistryFactory
返回loader=com.alibaba.dubbo.common.extension.ExtensionLoader[com.alibaba.dubbo.registry.RegistryFactory]
3.ExtensionLoader.getExtension()
入参name=zookeeper
返回instance=ZookeeperRegistryFactory
4.AbstractRegistryFactory.getRegistry
@Override public Registry getRegistry(URL url) { if (destroyed.get()) { LOGGER.warn("All registry instances have been destroyed, failed to fetch any instance. " + "Usually, this means no need to try to do unnecessary redundant resource clearance, all registries has been taken care of."); return DEFAULT_NOP_REGISTRY; } url = URLBuilder.from(url) .setPath(RegistryService.class.getName()) .addParameter(INTERFACE_KEY, RegistryService.class.getName()) .removeParameters(EXPORT_KEY, REFER_KEY) .build(); String key = createRegistryCacheKey(url); // Lock the registry access process to ensure a single instance of the registry LOCK.lock(); try { // 访问缓存 Registry registry = REGISTRIES.get(key); if (registry != null) { return registry; } //create registry by spi/ioc // 缓存未命中,创建 Registry 实例 registry = createRegistry(url); if (registry == null) { throw new IllegalStateException("Can not create registry " + url); } // 写入缓存 REGISTRIES.put(key, registry); return registry; } finally { // Release the lock LOCK.unlock(); } }
如果缓存中没有则调用子类的createRegistry方法创建Registry
但是我debug的时候缓存中有,就直接返回了。后来再debug发现创建Registry实例是在RegistryProtocol.export方法的final Registry registry = getRegistry(originInvoker);中做的
ZookeeperRegistryFactory.createRegistry
public class ZookeeperRegistryFactory extends AbstractRegistryFactory { // zookeeperTransporter 由 SPI 在运行时注入,类型为 ZookeeperTransporter$Adaptive private ZookeeperTransporter zookeeperTransporter; /** * Invisible injection of zookeeper client via IOC/SPI * @param zookeeperTransporter */ public void setZookeeperTransporter(ZookeeperTransporter zookeeperTransporter) { this.zookeeperTransporter = zookeeperTransporter; } @Override public Registry createRegistry(URL url) { return new ZookeeperRegistry(url, zookeeperTransporter); } }
看ZookeeperRegistry的构造函数
public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) { super(url); if (url.isAnyHost()) { throw new IllegalStateException("registry address == null"); } // 获取组名,默认为 dubbo String group = url.getParameter(GROUP_KEY, DEFAULT_ROOT); if (!group.startsWith(PATH_SEPARATOR)) { // group = "/" + group group = PATH_SEPARATOR + group; } this.root = group; // 创建 Zookeeper 客户端,默认为 CuratorZookeeperTransporter zkClient = zookeeperTransporter.connect(url); // 添加状态监听器 zkClient.addStateListener((state) -> { if (state == StateListener.RECONNECTED) { logger.warn("Trying to fetch the latest urls, in case there're provider changes during connection loss.\n" + " Since ephemeral ZNode will not get deleted for a connection lose, " + "there's no need to re-register url of this instance."); ZookeeperRegistry.this.fetchLatestAddresses(); } else if (state == StateListener.NEW_SESSION_CREATED) { logger.warn("Trying to re-register urls and re-subscribe listeners of this instance to registry..."); try { ZookeeperRegistry.this.recover(); } catch (Exception e) { logger.error(e.getMessage(), e); } } else if (state == StateListener.SESSION_LOST) { logger.warn("Url of this instance will be deleted from registry soon. " + "Dubbo client will try to re-register once a new session is created."); } else if (state == StateListener.SUSPENDED) { } else if (state == StateListener.CONNECTED) { } }); }
做了几件事
- .获取组名,默认为 dubbo
- zookeeperTransporter.connect(url)创建 Zookeeper 客户端,默认为 CuratorZookeeperTransporter
- 添加状态监听器
@SPI("curator") public interface ZookeeperTransporter { @Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY}) ZookeeperClient connect(URL url); }
ExtensionLoader.getExtensionLoader()
入参type=interface com.alibaba.dubbo.remoting.zookeeper.ZookeeperTransporter
出参loader=com.alibaba.dubbo.common.extension.ExtensionLoader[com.alibaba.dubbo.remoting.zookeeper.ZookeeperTransporter]
ExtensionLoader.getExtension()
入参name=curator
出参instance=CuratorZookeeperTransporter
zookeeperTransporter.connect()
package com.alibaba.dubbo.remoting.zookeeper.curator; import com.alibaba.dubbo.common.URL; import com.alibaba.dubbo.remoting.zookeeper.ZookeeperClient; import com.alibaba.dubbo.remoting.zookeeper.ZookeeperTransporter; public class CuratorZookeeperTransporter implements ZookeeperTransporter { @Override public ZookeeperClient connect(URL url) { return new CuratorZookeeperClient(url); } }
CuratorZookeeperClient的构造函数
public CuratorZookeeperClient(URL url) { super(url); try { // 创建 CuratorFramework 构造器 CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder() .connectString(url.getBackupAddress()) .retryPolicy(new RetryNTimes(1, 1000)) .connectionTimeoutMs(5000); String authority = url.getAuthority(); if (authority != null && authority.length() > 0) { builder = builder.authorization("digest", authority.getBytes()); } // 构建 CuratorFramework 实例 client = builder.build(); // 添加监听器 client.getConnectionStateListenable().addListener(new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState state) { if (state == ConnectionState.LOST) { CuratorZookeeperClient.this.stateChanged(StateListener.DISCONNECTED); } else if (state == ConnectionState.CONNECTED) { CuratorZookeeperClient.this.stateChanged(StateListener.CONNECTED); } else if (state == ConnectionState.RECONNECTED) { CuratorZookeeperClient.this.stateChanged(StateListener.RECONNECTED); } } }); // 启动客户端 client.start(); } catch (Exception e) { throw new IllegalStateException(e.getMessage(), e); } }
做了几件事:
- 创建 CuratorFramework 构造器
- 构建 CuratorFramework 实例
- 添加监听器
- 启动客户端
现在注册中心实例创建好了,接下来要做的事情是向注册中心注册服务。
向注册中心注册服务
RegistryProtocol.register方法中的registry.register(registedProviderUrl);
这里的registry是ListenerRegistryWrapper
ListenerRegistryWrapper.register()
public void register(URL url) { try { //这里的register应该是找zookeeperRegistry的register,但在zookeeperRegistry中并没有找到 //所以找zookeeperRegistry的父类FailbackRegistry registry.register(url); } finally { //注册监听 if (CollectionUtils.isNotEmpty(listeners)) { RuntimeException exception = null; for (RegistryServiceListener listener : listeners) { if (listener != null) { try { listener.onRegister(url); } catch (RuntimeException t) { logger.error(t.getMessage(), t); exception = t; } } } if (exception != null) { throw exception; } } } }
FailbackRegistry.register()
@Override public void register(URL url) { if (!acceptable(url)) { logger.info("URL " + url + " will not be registered to Registry. Registry " + url + " 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. // 获取 check 参数,若 check = true 将会直接抛出异常 boolean check = getUrl().getParameter(Constants.CHECK_KEY, true) && url.getParameter(Constants.CHECK_KEY, true) && !CONSUMER_PROTOCOL.equals(url.getProtocol()); 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); } }
ZookeeperRegistry.doRegister()
@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); } }
做了一件事
通过 Zookeeper 客户端创建节点,节点路径由 toUrlPath 方法生成。
路径格式如下:
/dubbo/org.apache.dubbo.samples.api.client.HelloService/providers/dubbo%3A%2F%2FXXX.XXX.XXX.XXX%3A20880%2Forg.apache.dubbo.samples.api.client.HelloService
%3Fanyhost%3Dtrue%26application%3Ddubbo-annotation-provider%26bean.name%3DServiceBean%3Aorg.apache.dubbo.samples.api.client.HelloService%26default.timeout%3D1000%26dubbo
%3D2.0.2%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.samples.api.client.HelloService%26methods%3DsayHello%26pid%3D11324%26side%3Dprovider%26timestamp%3D1603701475951
AbstractZookeeperClient.create()方法
@Override public void create(String path, boolean ephemeral) { if (!ephemeral) { if(persistentExistNodePath.contains(path)){ return; } // 如果要创建的节点类型非临时节点,那么这里要检测节点是否存在 if (checkExists(path)) { persistentExistNodePath.add(path); return; } } int i = path.lastIndexOf('/'); if (i > 0) { // 递归创建上一级路径 create(path.substring(0, i), false); } // 根据 ephemeral 的值创建临时或持久节点 if (ephemeral) { createEphemeral(path); } else { createPersistent(path); persistentExistNodePath.add(path); } }
做了几件事
- 先是通过递归创建当前节点的上一级路径
- 然后再根据 ephemeral 的值决定创建临时还是持久节点。