服务注册流程(八)

了解了服务的发布之后,我们继续来看一下服务是如何发起注册的。服务注册实际上就是把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 的值决定创建临时还是持久节点。
posted @ 2021-12-22 23:09  童话述说我的结局  阅读(236)  评论(0编辑  收藏  举报