dubbo 服务消费原理分析之服务目录

文章目录

  • 前言
  • 一、RegistryDirectory
    • 1、DynamicDirectory
    • 2、RegistryProtocol.doCreateInvoker
    • 2、RegistryProtocol.subscribe
    • 3、ListenerRegistryWrapper.subscribe
    • 4、FailbackRegistry.subscribe
    • 5、ZookeeperRegistry.doSubscribe
    • 6、RegistryDirectory.notify
    • 7、RegistryDirectory.refreshOverrideAndInvoker
    • 8、RegistryDirectory.toInvokers
  • 二、Protocol
    • 1、DubboProtocol.refer
    • 2、DubboProtocol.getClients
    • 3、NettyTransporter.connect
    • 4、NettyClient


前言

文章基于3.1.0版本进行分析

		<dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
            <version>3.1.0</version>
        </dependency>

一、RegistryDirectory

dubbo 服务消费原理分析之服务目录_第1张图片

RegistryDirectory是Dubbo中的服务目录,服务目录中存储了一些和服务提供者有关的信息。通过服务目录,服务消费者可获取到服务提供者的信息,比如 ip、端口、服务协议等。
通过这些信息,服务消费者就可通过 Netty 等客户端进行远程调用。

创建RegistryDirectory会出初始化父类DynamicDirectory ,初始化接口、分组、失败策略等,从DynamicDirectory 开始分析

1、DynamicDirectory

创建服务目录与初始化的一些信息,比如接口、分组、失败策略

	public DynamicDirectory(Class<T> serviceType, URL url) {
        super(url, true);

        ModuleModel moduleModel = url.getOrDefaultModuleModel();

        // 容错适配器,Cluster$Adaptive 默认的容错机制是失效转移 failover
		this.cluster = moduleModel.getExtensionLoader(Cluster.class).getAdaptiveExtension();
        // 路由工厂适配器RouterFactory$Adaptive
		this.routerFactory = moduleModel.getExtensionLoader(RouterFactory.class).getAdaptiveExtension();

        if (serviceType == null) {
            throw new IllegalArgumentException("service type is null.");
        }

        if (StringUtils.isEmpty(url.getServiceKey())) {
            throw new IllegalArgumentException("registry serviceKey is null.");
        }

        this.shouldRegister = !ANY_VALUE.equals(url.getServiceInterface()) && url.getParameter(REGISTER_KEY, true);
        this.shouldSimplified = url.getParameter(SIMPLIFIED_KEY, false);

        // 接口类型 org.sjl.dubbo.AsyncProvider
		this.serviceType = serviceType;
		// 接口key org.sjl.dubbo.AsyncProvider
        this.serviceKey = super.getConsumerUrl().getServiceKey();

        this.directoryUrl = consumerUrl;
        String group = directoryUrl.getGroup("");
        this.multiGroup = group != null && (ANY_VALUE.equals(group) || group.contains(","));
		// 默认快速失败
        this.shouldFailFast = Boolean.parseBoolean(ConfigurationUtils.getProperty(moduleModel, Constants.SHOULD_FAIL_FAST_KEY, "true"));
    }

2、RegistryProtocol.doCreateInvoker

ClusterInvoker的创建过程

	protected <T> ClusterInvoker<T> doCreateInvoker(DynamicDirectory<T> directory, Cluster cluster, Registry registry, Class<T> type) {
        // 初始化服务目录的注册中心和协议
		directory.setRegistry(registry);
        directory.setProtocol(protocol);
        // all attributes of REFER_KEY
        Map<String, String> parameters = new HashMap<>(directory.getConsumerUrl().getParameters());
		// urlToRegistry = consumer://192.168.0.101/org.sjl.dubbo.AsyncProvider?application=dubbo-springboot-start-consumer&background=false&dubbo=2.0.2
		// &interface=org.sjl.dubbo.AsyncProvider&methods=sayHiAsync,sayHello,sayHelloAsync&pid=15072&qos.enable=false&release=3.1.0
		// &side=consumer&sticky=false&timeout=30000×tamp=1725462541548
		URL urlToRegistry = new ServiceConfigURL(
            parameters.get(PROTOCOL_KEY) == null ? CONSUMER : parameters.get(PROTOCOL_KEY),
            parameters.remove(REGISTER_IP_KEY),
            0,
            getPath(parameters, type),
            parameters
        );
        urlToRegistry = urlToRegistry.setScopeModel(directory.getConsumerUrl().getScopeModel());
        urlToRegistry = urlToRegistry.setServiceModel(directory.getConsumerUrl().getServiceModel());
        if (directory.isShouldRegister()) {
            directory.setRegisteredConsumerUrl(urlToRegistry);
			//  消费者配置到注册中心
            registry.register(directory.getRegisteredConsumerUrl());
        }
		// 路由调用链
        directory.buildRouterChain(urlToRegistry);
		// 服务发现并订阅的逻辑 核心逻辑
        directory.subscribe(toSubscribeUrl(urlToRegistry));

        return (ClusterInvoker<T>) cluster.join(directory, true);
    }

2、RegistryProtocol.subscribe

    public void subscribe(URL url) {
        setSubscribeUrl(url);
        registry.subscribe(url, this);
    }

3、ListenerRegistryWrapper.subscribe

进行包装,对订阅完成后的后置处理

	public void subscribe(URL url, NotifyListener listener) {
        try {
            if (registry != null) {
                registry.subscribe(url, listener);
            }
        } finally {
            if (CollectionUtils.isNotEmpty(listeners)) {
                RuntimeException exception = null;
                for (RegistryServiceListener registryListener : listeners) {
                    if (registryListener != null) {
                        try {
                            registryListener.onSubscribe(url, registry);
                        } catch (RuntimeException t) {
                            logger.error(t.getMessage(), t);
                            exception = t;
                        }
                    }
                }
                if (exception != null) {
                    throw exception;
                }
            }
        }
    }

4、FailbackRegistry.subscribe

failbackRegistry这个类,从名字就可以看出,它的主要作用就是实现具有故障恢复功能的服务订阅机制,简单来说就是如果在订阅服务注册中心时出现异常,会触发重试机制。

	public void subscribe(URL url, NotifyListener listener) {
        super.subscribe(url, listener);
		// 移除失效的监听
        removeFailedSubscribed(url, listener);
        try {
            // Sending a subscription request to the server side
			// 发送订阅到服务端
            doSubscribe(url, listener);
        } catch (Exception e) {
            Throwable t = e;

            // *** 异常处理

            // Record a failed registration request to a failed list, retry regularly
            addFailedSubscribed(url, listener);
        }
    }

5、ZookeeperRegistry.doSubscribe

所有Service层发起订阅,类似于监控中心发起的订阅
指定的Service层发起的订阅,服务消费者的订阅

	@Override
    public void doSubscribe(final URL url, final NotifyListener listener) {
        try {
            checkDestroyed();
			// 针对通用服务接口
            if (ANY_VALUE.equals(url.getServiceInterface())) {
                // ***
            }
			// 针对指定服务,正常走到这里
			else {
                CountDownLatch latch = new CountDownLatch(1);

                try {
                    List<URL> urls = new ArrayList<>();

                    // ["/dubbo/org.sjl.dubbo.GreetingsProvider/providers", ".../configurators", ".../routers"]
					// 需要订阅以上三个节点下数据变化
					for (String path : toCategoriesPath(url)) {
						// 构建一个listeners集合,其中key是NotifyListener,
						// value表示针对这个RegistryDirectory注册的子节点监听。
                        ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.computeIfAbsent(url, k -> new ConcurrentHashMap<>());
                        ChildListener zkListener = listeners.computeIfAbsent(listener, k -> new RegistryChildListenerImpl(url, k, latch));

                        if (zkListener instanceof RegistryChildListenerImpl) {
                            ((RegistryChildListenerImpl) zkListener).setLatch(latch);
                        }

                        // 创建非临时节点,不存在时候会创建 
						// /dubbo/org.sjl.dubbo.GreetingsProvider/providers
                        // /dubbo/org.sjl.dubbo.GreetingsProvider/configurators
						// /dubbo/org.sjl.dubbo.GreetingsProvider/routers
						zkClient.create(path, false);
						// 服务目录创建完毕之后创建一个监听器用来监听子目录,同时要返回一个path目录的子子节点,
						// 比如providers下面的提供者节点列表,如果有多个可以返回多个
                        List<String> children = zkClient.addChildListener(path, zkListener);
                        if (children != null) {
                            urls.addAll(toUrlsWithEmpty(url, path, children));
                        }
                    }

                    // 调用notify方法触发回调通知
					notify(url, listener, urls);
                } finally {
                    // tells the listener to run only after the sync notification of main thread finishes.
                    latch.countDown();
                }
            }
        } catch (Throwable e) {
            throw new RpcException("Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
        }
    }

children里面值实例,包括ip、port、接口等参数

dubbo://192.168.0.101:20880/org.sjl.dubbo.GreetingsProvider?anyhost=true
&application=dubbo-springboot-start-provider
&background=false
&deprecated=false
&dubbo=2.0.2
&dynamic=true
&generic=false
&interface=org.sjl.dubbo.GreetingsProvider
&methods=sayHello,sayHi
&pid=30316&release=3.1.0
&service-name-mapping=true
&side=provider&timeout=30000
×tamp=1725293330893

notify回调通知,链路结构为

FailbackRegistry -> AbstractRegistry -> RegistryDirectory

6、RegistryDirectory.notify

服务地址发生变化和更新时,调用Directory.notify来更新,意味着更新后的信息,会同步到Directory

	public synchronized void notify(List<URL> urls) {
        if (isDestroyed()) {
            return;
        }
		// 过滤不需要的数据
        Map<String, List<URL>> categoryUrls = urls.stream()
            .filter(Objects::nonNull)
            .filter(this::isValidCategory)
            .filter(this::isNotCompatibleFor26x)
            .collect(Collectors.groupingBy(this::judgeCategory));

        // 假设当前进来的通知是 providers节点
		// 判断configurator是否为空,这个节点下的配置,是在dubbo-admin控制台上修改配置时,会先创建一个配置节点到这个路径下,
		// 注册中心收到这个变化时会通知服务消费者,服务消费者会根据新的配置重新构建Invoker
		List<URL> configuratorURLs = categoryUrls.getOrDefault(CONFIGURATORS_CATEGORY, Collections.emptyList());
        this.configurators = Configurator.toConfigurators(configuratorURLs).orElse(this.configurators);

        // 判断路由规则配置是否为空,如果不为空,同样将路由规则添加到url中。
		List<URL> routerURLs = categoryUrls.getOrDefault(ROUTERS_CATEGORY, Collections.emptyList());
        toRouters(routerURLs).ifPresent(this::addRouters);

        // providers
		// 得到服务提供者的地址列表
        List<URL> providerURLs = categoryUrls.getOrDefault(PROVIDERS_CATEGORY, Collections.emptyList());

        // 3.x added for extend URL address
        ExtensionLoader<AddressListener> addressListenerExtensionLoader = getUrl().getOrDefaultModuleModel().getExtensionLoader(AddressListener.class);
        List<AddressListener> supportedListeners = addressListenerExtensionLoader.getActivateExtension(getUrl(), (String[]) null);
        if (supportedListeners != null && !supportedListeners.isEmpty()) {
            for (AddressListener addressListener : supportedListeners) {
                providerURLs = addressListener.notify(providerURLs, getConsumerUrl(), this);
            }
        }
		// 刷新并覆盖注册中心里面的配置、url、Invoker,
        refreshOverrideAndInvoker(providerURLs);
    }

7、RegistryDirectory.refreshOverrideAndInvoker

路由调用refreshInvoker方法

当注册中心的服务地址发生变化时,会触发更新。而更新之后并不是直接把url地址存储到内存,而是把
url转化为invoker进行存储,这个invoker是作为通信的调用器来构建的领域对象,所以如果地址发生变
化,那么需要把老的invoker销毁,然后用心的invoker替代。

    private synchronized void refreshOverrideAndInvoker(List<URL> urls) {
        // mock zookeeper://xxx?mock=return null
        refreshInvoker(urls);
    }
	
	private void refreshInvoker(List<URL> invokerUrls) {
        Assert.notNull(invokerUrls, "invokerUrls should not be null");
		// 如果只有一个服务提供者,并且如果是空协议,那么这个时候直接返回进制访问,并且销毁所有的invokers
        if (invokerUrls.size() == 1
            && invokerUrls.get(0) != null
            && EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
            this.forbidden = true; // Forbid to access
            routerChain.setInvokers(BitList.emptyList());
            destroyAllInvokers(); // Close all invokers
        } else {
			this.forbidden = false; // Allow to access

            //  第一次初始化的时候,invokerUrls为空。
			if (invokerUrls == Collections.<URL>emptyList()) {
                invokerUrls = new ArrayList<>();
            }
            // use local reference to avoid NPE as this.cachedInvokerUrls will be set null by destroyAllInvokers().
            // 获取invoker缓存
			Set<URL> localCachedInvokerUrls = this.cachedInvokerUrls;
            if (invokerUrls.isEmpty() && localCachedInvokerUrls != null) {

                // 1-4 Empty address.
                logger.warn("1-4", "configuration ", "",
                    "Service" + serviceKey + " received empty address list with no EMPTY protocol set, trigger empty protection.");

                invokerUrls.addAll(localCachedInvokerUrls);

            } else {
                localCachedInvokerUrls = new HashSet<>();
                localCachedInvokerUrls.addAll(invokerUrls);//Cached invoker urls, convenient for comparison
                this.cachedInvokerUrls = localCachedInvokerUrls;
            }
            if (invokerUrls.isEmpty()) {
                return;
            }

            // use local reference to avoid NPE as this.urlInvokerMap will be set null concurrently at destroyAllInvokers().
            Map<URL, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap;
            // can't use local reference as oldUrlInvokerMap's mappings might be removed directly at toInvokers().
            Map<URL, Invoker<T>> oldUrlInvokerMap = null;
            if (localUrlInvokerMap != null) {
                // the initial capacity should be set greater than the maximum number of entries divided by the load factor to avoid resizing.
                oldUrlInvokerMap = new LinkedHashMap<>(Math.round(1 + localUrlInvokerMap.size() / DEFAULT_HASHMAP_LOAD_FACTOR));
                localUrlInvokerMap.forEach(oldUrlInvokerMap::put);
            }
			// 将URL转换为Invoker  这里会做一些协议的指定过滤操作
            Map<URL, Invoker<T>> newUrlInvokerMap = toInvokers(oldUrlInvokerMap, invokerUrls);// Translate url list to Invoker map

            /*
             * If the calculation is wrong, it is not processed.
             *
             * 1. The protocol configured by the client is inconsistent with the protocol of the server.
             *    eg: consumer protocol = dubbo, provider only has other protocol services(rest).
             * 2. The registration center is not robust and pushes illegal specification data.
             *
             */
            if (CollectionUtils.isEmptyMap(newUrlInvokerMap)) {

                // 3-1 - Failed to convert the URL address into Invokers.

                logger.error(
                    "3-1", "inconsistency between the client protocol and the protocol of the server",
                    "", "urls to invokers error",
                    new IllegalStateException(
                        "urls to invokers error. invokerUrls.size :" +
                            invokerUrls.size() + ", invoker.size :0. urls :" + invokerUrls.toString()));

                return;
            }

            List<Invoker<T>> newInvokers = Collections.unmodifiableList(new ArrayList<>(newUrlInvokerMap.values()));
            this.setInvokers(multiGroup ? new BitList<>(toMergeInvokerList(newInvokers)) : new BitList<>(newInvokers));
            // pre-route and build cache
            routerChain.setInvokers(this.getInvokers());
            this.urlInvokerMap = newUrlInvokerMap;

            try {
				// 销毁不使用或者已经被删除的 Invoker。例如某个提供者的代码删除了
                destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap); // Close the unused Invoker
            } catch (Exception e) {
                logger.warn("destroyUnusedInvokers error. ", e);
            }

            // notify invokers refreshed
            this.invokersChanged();
        }
    }

将invokerURL列表转换为调用器映射。转换规则如下:

  • 如果URL已转换为invoker,它将不再被重新引用并直接从缓存中获取,请注意,URL中的任何参数更改都将被重新引用。
  • 如果传入调用器列表不为空,则表示它是最新的调用器列表。
  • 如果传入invokerUrl的列表为空,则意味着该规则只是一个覆盖规则或路由规则,需要重新对比以决定是否重新引用。

8、RegistryDirectory.toInvokers

协议过滤,配置合并都会在这里面去做。url转换为调用程序,如果url已被引用,则不会重新引用。将放入newUrlInvokeMap的项将从OldUrlInvoceMap中删除。

    private Map<URL, Invoker<T>> toInvokers(Map<URL, Invoker<T>> oldUrlInvokerMap, List<URL> urls) {
        Map<URL, Invoker<T>> newUrlInvokerMap = new ConcurrentHashMap<>(urls == null ? 1 : (int) (urls.size() / 0.75f + 1));
        if (urls == null || urls.isEmpty()) {
            return newUrlInvokerMap;
        }
		// 获取指定的协议
        String queryProtocols = this.queryMap.get(PROTOCOL_KEY);
		// 遍历所有提供者列表进行转换
        for (URL providerUrl : urls) {
            // If protocol is configured at the reference side, only the matching protocol is selected
            if (queryProtocols != null && queryProtocols.length() > 0) {
                boolean accept = false;
                String[] acceptProtocols = queryProtocols.split(",");
                for (String acceptProtocol : acceptProtocols) {
                    if (providerUrl.getProtocol().equals(acceptProtocol)) {
                        accept = true;
                        break;
                    }
                }
                if (!accept) {
                    continue;
                }
            }

            // 空协议 直接跳过
			if (EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) {
                continue;
            }
			//  查询扩展是否存在,这个要注意如果是自定义扩展或者像webservice这样的扩展一般需要额外引入扩展包才可以
            if (!getUrl().getOrDefaultFrameworkModel().getExtensionLoader(Protocol.class).hasExtension(providerUrl.getProtocol())) {

                // 4-1 - Unsupported protocol

                logger.error("4-1", "typo in URL", "", "Unsupported protocol.",
                    new IllegalStateException("Unsupported protocol " + providerUrl.getProtocol() +
                    " in notified url: " + providerUrl + " from registry " + getUrl().getAddress() +
                    " to consumer " + NetUtils.getLocalHost() + ", supported protocol: " +
                    getUrl().getOrDefaultFrameworkModel().getExtensionLoader(Protocol.class).getSupportedExtensions()));

                continue;
            }
			// 合并url参数。顺序是:覆盖协议配置(比如禁用服务有时候就这样做)> -D(JVM参数) > 消费者 > 提供者
            URL url = mergeUrl(providerUrl);

            // Cache key is url that does not merge with consumer side parameters,
            // regardless of how the consumer combines parameters,
            // if the server url changes, then refer again
            Invoker<T> invoker = oldUrlInvokerMap == null ? null : oldUrlInvokerMap.remove(url);
            if (invoker == null) { // Not in the cache, refer again
                try {
                    boolean enabled = true;
                    if (url.hasParameter(DISABLED_KEY)) {
                        enabled = !url.getParameter(DISABLED_KEY, false);
                    } else {
                        enabled = url.getParameter(ENABLED_KEY, true);
                    }
                    if (enabled) {
						// 消费者引用服务提供者
                        invoker = protocol.refer(serviceType, url);
                    }
                } catch (Throwable t) {

                    // Thrown by AbstractProtocol.optimizeSerialization()
                    if (t instanceof RpcException && t.getMessage().contains("serialization optimizer")) {
                        // 4-2 - serialization optimizer class initialization failed.
                        logger.error("4-2", "typo in optimizer class", "",
                            "Failed to refer invoker for interface:" + serviceType + ",url:(" + url + ")" + t.getMessage(), t);

                    } else {
                        // 4-3 - Failed to refer invoker by other reason.
                        logger.error("4-3", "", "",
                            "Failed to refer invoker for interface:" + serviceType + ",url:(" + url + ")" + t.getMessage(), t);
                    }
                }
                if (invoker != null) { // Put new invoker in cache
                    newUrlInvokerMap.put(url, invoker);
                }
            } else {
                newUrlInvokerMap.put(url, invoker);
            }
        }
        return newUrlInvokerMap;
    }

到这里我们可以看到核心功能已经展示出来了

  • 指定注册中心的三个路径的子节点变更事件
  • 触发时间变更之后,把变更的节点信息转化为Invoker
    这时候得到的Invoker是最新的服务提供者地址,远程访问得到我们实际的结果

二、Protocol

根据协议创建通信连接
调用链
QosProtocolWrapper -> ProtocolFilterWrapper -> ProtocolListenerWrapper -> DubboProtocol

1、DubboProtocol.refer

    @Override
    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        checkDestroyed();
        return protocolBindingRefer(type, url);
    }

    @Override
    public <T> Invoker<T> protocolBindingRefer(Class<T> serviceType, URL url) throws RpcException {
        checkDestroyed();
        optimizeSerialization(url);

        // create rpc invoker.
		// 创建通信client,封装成DubboInvoker
        DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
        invokers.add(invoker);

        return invoker;
    }

2、DubboProtocol.getClients

创建通信客户端

  • 判断是否为共享连接,默认是共享同一个连接进行通信
  • 是否配置了多个连接通道 connections,默认只有一个共享连接
	private ExchangeClient[] getClients(URL url) {
        int connections = url.getParameter(CONNECTIONS_KEY, 0);
        // whether to share connection
        // if not configured, connection is shared, otherwise, one connection for one service
        // 如果没有配置的情况下,默认是采用共享连接,否则,就是针对一个服务提供一个连接。
		// 共享长连接,多路复用
		if (connections == 0) {
            /*
             * The xml configuration should have a higher priority than properties.
             */
            String shareConnectionsStr = StringUtils.isBlank(url.getParameter(SHARE_CONNECTIONS_KEY, (String) null))
                ? ConfigurationUtils.getProperty(url.getOrDefaultApplicationModel(), SHARE_CONNECTIONS_KEY, DEFAULT_SHARE_CONNECTIONS)
                : url.getParameter(SHARE_CONNECTIONS_KEY, (String) null);
            connections = Integer.parseInt(shareConnectionsStr);

            // 返回共享连接
			List<ReferenceCountExchangeClient> shareClients = getSharedClient(url, connections);
            ExchangeClient[] clients = new ExchangeClient[connections];
            Arrays.setAll(clients, shareClients::get);
            return clients;
        }
		// 如果不是使用共享连接,则初始化一个新的客户端连接进行返回
        ExchangeClient[] clients = new ExchangeClient[connections];
        for (int i = 0; i < clients.length; i++) {
            clients[i] = initClient(url);
        }
        return clients;
    }

最后创建的共享客户端是netty客户端
org.apache.dubbo.remoting.transport.netty4.NettyTransporter,外层由HeaderExchanger包装

3、NettyTransporter.connect

    @Override
    public Client connect(URL url, ChannelHandler handler) throws RemotingException {
        return new NettyClient(url, handler);
    }

4、NettyClient

    public NettyClient(final URL url, final ChannelHandler handler) throws RemotingException {
        // you can customize name and type of client thread pool by THREAD_NAME_KEY and THREADPOOL_KEY in CommonConstants.
        // the handler will be wrapped: MultiMessageHandler->HeartbeatHandler->handler
        super(url, wrapChannelHandler(url, handler));
    }
	public AbstractClient(URL url, ChannelHandler handler) throws RemotingException {
        // 初始化父类构造器,指定编码、超时时间等参数
		super(url, handler);
        // set default needReconnect true when channel is not connected
        needReconnect = url.getParameter(Constants.SEND_RECONNECT_KEY, true);
		// 初始化线程池 默认为FixedThreadPool
        initExecutor(url);

        try {
			// 启动netty的核心代码初始化Bootstrap
            // 默认走的是NioSocketChannel
            // 初始化默认连接超时时间为3秒
            doOpen();
        } catch (Throwable t) {
            close();
            throw new RemotingException(url.toInetSocketAddress(), null,
                "Failed to start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress()
                    + " connect to the server " + getRemoteAddress() + ", cause: " + t.getMessage(), t);
        }

        try {
            // connect.
            // 前面是初始化netty的客户端启动类Bootstrap 这里是执行连接的代码:bootstrap.connect(getConnectAddress());
            // 等待3秒连接失败则抛出异常
            connect();
            if (logger.isInfoEnabled()) {
                logger.info("Start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress() + " connect to the server " + getRemoteAddress());
            }
        } catch (RemotingException t) {
            // ** 异常处理
        } catch (Throwable t) {
            // ** 异常处理
        }
    }

RegistryProtocol.refer过程中有一个关键步骤,即在监听到服务提供者url时触发RegistryDirectory.notify()方法。
RegistryDirectory.notify()方法调用 refreshInvoker()方法将服务提供者urls转换为对应的远程invoker,最终调用到 DubboProtocol.refer()方法生成对应的 DubboInvoker。
DubboInvoker的构造方法中有一项入参 ExchangeClient[] clients,对应网络客户端 Client,通过调用 client.request()方法完成网络通信的请求发送和响应接收功能。

你可能感兴趣的:(dubbo,java,架构,后端,spring,boot)