12. Renewal of Registered Directory Introduced by Services and Connection of Services

Keywords: Dubbo Jedis Redis encoding

The last section talks about the configuration and parameter subscription of service introduction. After parameter subscription, the consumer obtains the provider url, the configurer url, and the routing URL under the corresponding interface classification. Then it must update its own provider directory.
And create a client that connects to the provider.

In addition to refreshing the list of providers when service introduction starts, dubbo also refreshes the list of providers when registration and cancellation events occur in the registry.

public void com.alibaba.dubbo.registry.redis.RedisRegistry.Notifier#run() {
            while (running) {
                try {
                    //Core number: 10, maximum number: 0-10 random plus 10, more than the maximum number, and from the core number of 10, so reciprocating
                    if (!isSkip()) {
                        try {
                            //Cyclic no-desk redis service connection pool
                            for (Map.Entry<String, JedisPool> entry : jedisPools.entrySet()) {
                                JedisPool jedisPool = entry.getValue();
                                try {
                                    jedis = jedisPool.getResource();
                                    try {
                                        //Check whether the service name ends with *
                                        if (service.endsWith(Constants.ANY_VALUE)) {
                                            //Is it the first time?
                                            if (!first) {
                                                //Yes, that's the first time I asked for it.
                                                first = false;
                                                //Get the key that matches the current service name
                                                Set<String> keys = jedis.keys(service);
                                                if (keys != null && !keys.isEmpty()) {
                                                    for (String s : keys) {
                                                        //Notification refresh directory, com.alibaba.dubbo.registry.redis.RedisRegistry#doNotify method logic analyzed in the previous section
                                                        doNotify(jedis, s);
                                                    }
                                                }
                                                //Reset connection count
                                                resetSkip();
                                            }
                                            //Subscription (blocking)
                                            jedis.psubscribe(new NotifySub(jedisPool), service); // blocking
                                        } else {
                                            //Is it the first time?
                                            if (!first) {
                                                first = false;
                                                //Notification refresh directory, com.alibaba.dubbo.registry.redis.RedisRegistry#doNotify method logic analyzed in the previous section
                                                doNotify(jedis, service);
                                                //Reset connection count
                                                resetSkip();
                                            }
                                            //Subscription (blocking)
                                            jedis.psubscribe(new NotifySub(jedisPool), service + Constants.PATH_SEPARATOR + Constants.ANY_VALUE); // blocking
                                        }
                                        break;
                                    } finally {
                                        jedis.close();
                                    }
                                } catch (Throwable t) { // Retry another server
                                    logger.warn("Failed to subscribe service from redis registry. registry: " + entry.getKey() + ", cause: " + t.getMessage(), t);
                                    // If you only have a single redis, you need to take a rest to avoid overtaking a lot of CPU resources
                                    sleep(reconnectPeriod);
                                }
                            }
                        } catch (Throwable t) {
                            logger.error(t.getMessage(), t);
                            sleep(reconnectPeriod);
                        }
                    }
                } catch (Throwable t) {
                    logger.error(t.getMessage(), t);
                }
            }
        }

NotifySub is notified of the subscription, and we are concerned about its onMessage method

public void NotifySub#onMessage(String key, String msg) {
        if (logger.isInfoEnabled()) {
            logger.info("redis event: " + key + " = " + msg);
        }
        //Interested in provider registration and cancellation
        if (msg.equals(Constants.REGISTER)
                || msg.equals(Constants.UNREGISTER)) {
            try {
                Jedis jedis = jedisPool.getResource();
                try {
                    //Notification, the logic of the com.alibaba.dubbo.registry.redis.RedisRegistry#doNotify method was analyzed in the previous section
                    doNotify(jedis, key);
                } finally {
                    jedis.close();
                }
            } catch (Throwable t) { // TODO Notification failure does not restore mechanism guarantee
                logger.error(t.getMessage(), t);
            }
        }
    }

Okay, let's get a general idea of how the notifier works. Now let's see how to refresh the provider directory.

public synchronized void com.alibaba.dubbo.registry.integration.RegistryDirectory#notify(List<URL> urls) {
    //Provider url
    List<URL> invokerUrls = new ArrayList<URL>();
    //Routing url
    List<URL> routerUrls = new ArrayList<URL>();
    //Configurator url
    List<URL> configuratorUrls = new ArrayList<URL>();
    for (URL url : urls) {
        //Acquisition protocol
        String protocol = url.getProtocol();
        //Get the category, the default category is providers
        String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
        //Is it a routing url?
        if (Constants.ROUTERS_CATEGORY.equals(category)
                || Constants.ROUTE_PROTOCOL.equals(protocol)) {
            routerUrls.add(url);
            //Is it the configurator url?
        } else if (Constants.CONFIGURATORS_CATEGORY.equals(category)
                || Constants.OVERRIDE_PROTOCOL.equals(protocol)) {
            configuratorUrls.add(url);
            //Is it the provider url?
        } else if (Constants.PROVIDERS_CATEGORY.equals(category)) {
            invokerUrls.add(url);
        } else {
            //Prompts that the current url classification is not supported
            logger.warn("Unsupported category " + category + " in notified url: " + url + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost());
        }
    }
    // configurators
    //The URL is used as the configuration URL to build the configurator. It is mainly used to modify the original url. Some default parameters can be added.
    if (configuratorUrls != null && !configuratorUrls.isEmpty()) {
        this.configurators = toConfigurators(configuratorUrls);
    }
    // routers
    //Create routing rules and construct specific routing rules types through router parameters in url
    //For example, ConditionRouter Factory matches providers according to conditions
    //197.0.0.1 = > 192.0.0.2 = > when condition on the left and then condition on the right. Interested in reading the corresponding source code can be customized.
    if (routerUrls != null && !routerUrls.isEmpty()) {
        List<Router> routers = toRouters(routerUrls);
        if (routers != null) { // null - do nothing
            setRouters(routers);
        }
    }
    //Get the configurator that you just parsed
    List<Configurator> localConfigurators = this.configurators; // local reference
    // merge override parameters
    //This is the registry address.
    this.overrideDirectoryUrl = directoryUrl;
    //Call the configurer to configure the registry address
    if (localConfigurators != null && !localConfigurators.isEmpty()) {
        for (Configurator configurator : localConfigurators) {
            this.overrideDirectoryUrl = configurator.configure(overrideDirectoryUrl);
        }
    }
    // providers
    //Refresh Provider
    refreshInvoker(invokerUrls);
}

The above code mainly classifies URLs into provider url, configurator url, routing url. If there is a configuration url, the configurator of the corresponding protocol will be built to configure our registry address, and finally start refreshing the provider directory.

private void com.alibaba.dubbo.registry.integration.RegistryDirectory#refreshInvoker(List<URL> invokerUrls) {
    //If there is only one provider, and the provider's protocol is empty, that is, no provider is obtained.
    if (invokerUrls != null && invokerUrls.size() == 1 && invokerUrls.get(0) != null
            && Constants.EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
            //Marked as No Access
        this.forbidden = true; // Forbid to access
        //No caller, methodName - > List < invoker >
        this.methodInvokerMap = null; // Set the method invoker map to null
        //Close (close) all connected invokers and empty the urlInvokerMap (url-) invoker collection
        destroyAllInvokers(); // Close all invokers
    } else {
        //allow access to
        this.forbidden = false; // Allow to access
        //Recording old data
        Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference
        if (invokerUrls.isEmpty() && this.cachedInvokerUrls != null) {
            //If invokerUrls have no url, that is, no provider address, then retrieve it from the cache
            invokerUrls.addAll(this.cachedInvokerUrls);
        } else {
            this.cachedInvokerUrls = new HashSet<URL>();
            //cache
            this.cachedInvokerUrls.addAll(invokerUrls);//Cached invoker urls, convenient for comparison
        }
        if (invokerUrls.isEmpty()) {
            return;
        }
        //Build an Invoker for each url
        //URL - > invoker (example of Invoker Delegate)
        Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);// Translate url list to Invoker map
        //Convert to Method Name - > List < Invoker >
        Map<String, List<Invoker<T>>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap); // Change method name to map Invoker Map
        // state change
        // If the calculation is wrong, it is not processed.
        if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0) {
            logger.error(new IllegalStateException("urls to invokers error .invokerUrls.size :" + invokerUrls.size() + ", invoker.size :0. urls :" + invokerUrls.toString()));
            return;
        }
        //invoker merging multiple groups
        this.methodInvokerMap = multiGroup ? toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap;
        this.urlInvokerMap = newUrlInvokerMap;
        try {
            //Consumption invalid provider url, loop oldUrlInvokerMap collection, destroy invoker that does not exist with newUrlInvokerMap collection
            destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap); // Close the unused Invoker
        } catch (Exception e) {
            logger.warn("destroyUnusedInvokers error. ", e);
        }
    }
}

The above code does a few things.

  • Build the provider url into Invoker Delegate, which connects the provider when building Invoker Delegate
  • Get the method of interface from url parameter and construct methodName-"Listmap with method name key and invoker as value
  • Check if it's multi-group, and if so, group it, and merge it through cluster (if it's multi-group, the final object of this instance is Mergeable Cluster, of course, for the existence of decorative classes, which are actually MockCluster Wrapper)
  • Eliminate invalid providers

Let's look at the process of building invoker

private Map<String, Invoker<T>> com.alibaba.dubbo.registry.integration.RegistryDirectory#toInvokers(List<URL> urls) {
    Map<String, Invoker<T>> newUrlInvokerMap = new HashMap<String, Invoker<T>>();
    if (urls == null || urls.isEmpty()) {
        return newUrlInvokerMap;
    }
    Set<String> keys = new HashSet<String>();
    //Does the consumer specify an agreement?
    String queryProtocols = this.queryMap.get(Constants.PROTOCOL_KEY);
    for (URL providerUrl : urls) {
        // If protocol is configured at the reference side, only the matching protocol is selected
        //Screening URLs for service specification protocols
        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 (Constants.EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) {
            continue;
        }
        //Check whether the corresponding protocol exists or not.
        if (!ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(providerUrl.getProtocol())) {
            logger.error(new IllegalStateException("Unsupported protocol " + providerUrl.getProtocol() + " in notified url: " + providerUrl + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost()
                    + ", supported protocol: " + ExtensionLoader.getExtensionLoader(Protocol.class).getSupportedExtensions()));
            continue;
        }
        //Merge parameters, merge some parameters of the provider url, and do not merge the number of threads of the provider, etc.
        //For the same parameters, consumers have the highest priority in their own configuration.
        URL url = mergeUrl(providerUrl);
        
        String key = url.toFullString(); // The parameter urls are sorted
        //Duplicate url, skip
        if (keys.contains(key)) { // Repeated url
            continue;
        }
        keys.add(key);
        // 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
        Map<String, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference
        Invoker<T> invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(key);
        if (invoker == null) { // Not in the cache, refer again
            try {
                boolean enabled = true;
                if (url.hasParameter(Constants.DISABLED_KEY)) {
                    enabled = !url.getParameter(Constants.DISABLED_KEY, false);
                } else {
                    enabled = url.getParameter(Constants.ENABLED_KEY, true);
                }
                if (enabled) {
                    //Build invoker only if it is available
                    invoker = new InvokerDelegate<T>(protocol.refer(serviceType, url), url, providerUrl);
                }
            } catch (Throwable t) {
                logger.error("Failed to refer invoker for interface:" + serviceType + ",url:(" + url + ")" + t.getMessage(), t);
            }
            if (invoker != null) { // Put new invoker in cache
                newUrlInvokerMap.put(key, invoker);
            }
        } else {
            newUrlInvokerMap.put(key, invoker);
        }
    }
    keys.clear();
    return newUrlInvokerMap;
}

Grouping invoker s with method names

private Map<String, List<Invoker<T>>> com.alibaba.dubbo.registry.integration.RegistryDirectory#toMethodInvokers(Map<String, Invoker<T>> invokersMap) {
    Map<String, List<Invoker<T>>> newMethodInvokerMap = new HashMap<String, List<Invoker<T>>>();
    // According to the methods classification declared by the provider URL, the methods is compatible with the registry to execute the filtered methods
    List<Invoker<T>> invokersList = new ArrayList<Invoker<T>>();
    if (invokersMap != null && invokersMap.size() > 0) {
        for (Invoker<T> invoker : invokersMap.values()) {
            //Get all the public method names of the interface from the url parameters, and the method names are separated by commas
            String parameter = invoker.getUrl().getParameter(Constants.METHODS_KEY);
            if (parameter != null && parameter.length() > 0) {
                //Comma Segmentation Method
                String[] methods = Constants.COMMA_SPLIT_PATTERN.split(parameter);
                if (methods != null && methods.length > 0) {
                    for (String method : methods) {
                        if (method != null && method.length() > 0
                                && !Constants.ANY_VALUE.equals(method)) {
                            //Grouping by method name
                            List<Invoker<T>> methodInvokers = newMethodInvokerMap.get(method);
                            if (methodInvokers == null) {
                                methodInvokers = new ArrayList<Invoker<T>>();
                                newMethodInvokerMap.put(method, methodInvokers);
                            }
                            methodInvokers.add(invoker);
                        }
                    }
                }
            }
            invokersList.add(invoker);
        }
    }
    //The invoker is filtered according to the routing, assuming that conditional routing is ConditionRouter, and the rule rule value is host=192.0.0.1=> host=192.0.0.2.
    //It means that consumers with ip 192.0.0.1 can only use the services of providers with ip 192.0.0.2, so when the current consumer's ip 192.0.0.1, it only screens out invoker s with ip 192.0.0.2.
    List<Invoker<T>> newInvokersList = route(invokersList, null);
    //* -> List<Invoker>
    newMethodInvokerMap.put(Constants.ANY_VALUE, newInvokersList);
    //All public method names of service reference interfaces
    if (serviceMethods != null && serviceMethods.length > 0) {
        for (String method : serviceMethods) {
            List<Invoker<T>> methodInvokers = newMethodInvokerMap.get(method);
            if (methodInvokers == null || methodInvokers.isEmpty()) {
                methodInvokers = newInvokersList;
            }
            //Further screening according to the method
            newMethodInvokerMap.put(method, route(methodInvokers, method));
        }
    }
    // sort and unmodifiable
    for (String method : new HashSet<String>(newMethodInvokerMap.keySet())) {
        //Sort
        List<Invoker<T>> methodInvokers = newMethodInvokerMap.get(method);
        Collections.sort(methodInvokers, InvokerComparator.getComparator());
        newMethodInvokerMap.put(method, Collections.unmodifiableList(methodInvokers));
    }
    return Collections.unmodifiableMap(newMethodInvokerMap);
}

Grouping and merging

private Map<String, List<Invoker<T>>> com.alibaba.dubbo.registry.integration.RegistryDirectory#toMergeMethodInvokerMap(Map<String, List<Invoker<T>>> methodMap) {
    Map<String, List<Invoker<T>>> result = new HashMap<String, List<Invoker<T>>>();
    for (Map.Entry<String, List<Invoker<T>>> entry : methodMap.entrySet()) {
        //Method name
        String method = entry.getKey();
        List<Invoker<T>> invokers = entry.getValue();
        //group -> List<Invoker<T>>
        Map<String, List<Invoker<T>>> groupMap = new HashMap<String, List<Invoker<T>>>();
        for (Invoker<T> invoker : invokers) {
            //Get group
            String group = invoker.getUrl().getParameter(Constants.GROUP_KEY, "");
            //Grouping
            List<Invoker<T>> groupInvokers = groupMap.get(group);
            if (groupInvokers == null) {
                groupInvokers = new ArrayList<Invoker<T>>();
                groupMap.put(group, groupInvokers);
            }
            groupInvokers.add(invoker);
        }
        if (groupMap.size() == 1) {
            //If there's only one, there's no need to merge.
            result.put(method, groupMap.values().iterator().next());
        } else if (groupMap.size() > 1) {
            //If there are more than one group, merge each group into one
            List<Invoker<T>> groupInvokers = new ArrayList<Invoker<T>>();
            for (List<Invoker<T>> groupList : groupMap.values()) {
                groupInvokers.add(cluster.join(new StaticDirectory<T>(groupList)));
            }
            result.put(method, groupInvokers);
        } else {
            result.put(method, invokers);
        }
    }
    return result;
}

Next, I'll continue to analyze the client connection provider's logic, which was built when Invoker Delegate was built.

invoker = new InvokerDelegate<T>(protocol.refer(serviceType, url), url, providerUrl);

This protocol is a class generated with javassist, which eventually calls DubboProtocol's refer method. DubboProtocol is decorated with two wrapper classes that we analyzed when we analyzed the export of the service.

//type: Interface Class Object, url: Provider Address
public <T> Invoker<T> com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper#refer(Class<T> type, URL url) throws RpcException {
    if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
        return protocol.refer(type, url);
    }
    //Build the interceptor chain. This time, unlike the service export, the filter grouping required is consumers, and the logic is no longer exhaustive.
    return buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);
}
                                                    |
                                                    V
public <T> Invoker<T> com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper#refer(Class<T> type, URL url) throws RpcException {
    if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
        return protocol.refer(type, url);
    }
    //Obtaining listeners is the same logic as exporting services, no more details.
    return new ListenerInvokerWrapper<T>(protocol.refer(type, url),
            Collections.unmodifiableList(
                    ExtensionLoader.getExtensionLoader(InvokerListener.class)
                            .getActivateExtension(url, Constants.INVOKER_LISTENER_KEY)));
}
                                                    |
                                                    |
                                                    V
public <T> Invoker<T> com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol#refer(Class<T> serviceType, URL url) throws RpcException {
    //url takes the optimizer parameter, which specifies the optimized serialized class name
    optimizeSerialization(url);
    // create rpc invoker.
    //Create rpc invoker
    DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
    invokers.add(invoker);
    return invoker;
}

Initialize client

private ExchangeClient[] com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol#getClients(URL url) {
    // whether to share connection
    boolean service_share_connect = false;
    //Connection number
    int connections = url.getParameter(Constants.CONNECTIONS_KEY, 0);
    // if not configured, connection is shared, otherwise, one connection for one service
    //If no connection is set, then the shared client is set
    if (connections == 0) {
        service_share_connect = true;
        connections = 1;
    }

    ExchangeClient[] clients = new ExchangeClient[connections];
    for (int i = 0; i < clients.length; i++) {
        if (service_share_connect) {
            //Usually the shared client is created, and the initClient method is also called internally.
            clients[i] = getSharedClient(url);
        } else {
            clients[i] = initClient(url);
        }
    }
    return clients;
}

Getting Shared Client

private ExchangeClient com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol#getSharedClient(URL url) {
    //Get address
    String key = url.getAddress();
    //Get from the cache
    ReferenceCountExchangeClient client = referenceClientMap.get(key);
    if (client != null) {
        if (!client.isClosed()) {
            //count
            client.incrementAndGetCount();
            return client;
        } else {
            //If the connection is closed, remove it
            referenceClientMap.remove(key);
        }
    }

    locks.putIfAbsent(key, new Object());
    synchronized (locks.get(key)) {
        //Double lock-in test
        if (referenceClientMap.containsKey(key)) {
            return referenceClientMap.get(key);
        }
        //Initialize client
        ExchangeClient exchangeClient = initClient(url);
        //Create a reference computing client, because it is a shared client, so this class can record how many service introductions have been created.
        client = new ReferenceCountExchangeClient(exchangeClient, ghostClientMap);
        //Record to Cache
        referenceClientMap.put(key, client);
        ghostClientMap.remove(key);
        locks.remove(key);
        return client;
    }
}

Initialize client

private ExchangeClient com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol#initClient(URL url) {

    // client type setting.
    //Get the client parameter value from the url to determine which client to use and specify netty or mima
    String str = url.getParameter(Constants.CLIENT_KEY, url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_CLIENT));
    //Add encoding parameters, default is codec - > Dubbo
    url = url.addParameter(Constants.CODEC_KEY, DubboCodec.NAME);
    // enable heartbeat by default
    //Add heartbeat cycle, default is 60s
    url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));

    // BIO is not allowed since it has severe performance issue.
    //Check whether the specified client exists
    if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
        throw new RpcException("Unsupported client type: " + str + "," +
                " supported client type is " + StringUtils.join(ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions(), " "));
    }

    ExchangeClient client;
    try {
        // connection should be lazy
        //If lazy loading is required, create a lazy loading client
        if (url.getParameter(Constants.LAZY_CONNECT_KEY, false)) {
            client = new LazyConnectExchangeClient(url, requestHandler);
        } else {
            //Create a client to connect to the provider
            client = Exchangers.connect(url, requestHandler);
        }
    } catch (RemotingException e) {
        throw new RpcException("Fail to create remoting client for service(" + url + "): " + e.getMessage(), e);
    }
    return client;
}

dubbo creates the NettyClient client, and the logic to connect the provider is in the constructor of this class.

protected void com.alibaba.dubbo.remoting.transport.netty4.NettyClient#doOpen() throws Throwable {
    final NettyClientHandler nettyClientHandler = new NettyClientHandler(getUrl(), this);
    bootstrap = new Bootstrap();
    bootstrap.group(nioEventLoopGroup)
            .option(ChannelOption.SO_KEEPALIVE, true)
            .option(ChannelOption.TCP_NODELAY, true)
            .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
            //.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, getTimeout())
            .channel(NioSocketChannel.class);

    if (getConnectTimeout() < 3000) {
        bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000);
    } else {
        bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, getConnectTimeout());
    }

    bootstrap.handler(new ChannelInitializer() {

        @Override
        protected void initChannel(Channel ch) throws Exception {
            NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyClient.this);
            ch.pipeline()//.addLast("logging",new LoggingHandler(LogLevel.INFO))//for debug
                    .addLast("decoder", adapter.getDecoder())
                    .addLast("encoder", adapter.getEncoder())
                    .addLast("handler", nettyClientHandler);
        }
    });
}

As you can see, dubbo's creation of clients is similar to that of servers, and there's not much to say here. Finally, summarize what classes invoker is packaged into

MockClusterInvoker
    invoker -> FailoverClusterInvoker(If the consumer sets it up group,So this will become MergeableClusterInvoker)
                    directory -> RegistryDirectory(With the function of dynamically updating the provider directory)
                                    urlInvokerMap -> This is a Map,key Provider url,The value is InvokeDelegate

        
InvokeDelegate
    invoker -> ProtocolFilterWrapper
                invoker -> ListenerInvokerWrapper
                            invoker -> DubboInvoker
                                        clients -> This is an array whose number of elements depends on how many connections are allowed when the configuration service is introduced. ReferenceCountExchangeClient
                                        
                                        
ReferenceCountExchangeClient
    client -> HeaderExchangeClient
                client -> NettyClient
                            channel -> NioSocketChannel
                            handler -> MultiMessageHandler
                                            handler -> HeartbeatHandler
                                                           handler -> AllChannelHandler
                                                                            handler -> DecodeHandler
                                                                                            handler -> HeaderExchangeChannel
                                                                                                            handler -> DubboProtocol#RequHandler (internal class)

From the ChannelHandler packaging level, the channel processor involved in message encoding and decoding is the same as the channel processing created by the server, so the processing logic of the channel can be seen in the article of requestor response data processing, which is not elaborated here.

From the packaging level of Invoker, except that ProtocolFilter Wrapper and Listener Invoker Wrapper are the same as the server (code logic is similar, but the grouping of listeners and filters is different), the other is completely different, so we need to study its differences. We will go to the next section. To analyze.

Posted by rtown on Mon, 16 Sep 2019 01:24:09 -0700