dubbo source code analysis Directory

First, let's take a look at the implementation classes of directory interface. There are two main implementation classes: StaticDirectory and RegistryDirectory. This paper mainly analyzes RegistryDirectory.

StaticDirectory
From the Static keywords in the StaticDirectory, we can see that this will not change dynamically. From the figure below, his Invoker is passed in through the constructor, and the StaticDirectory is used less, mainly for the reference of services to multiple registries

RegistryDirectory
First look at its structure:

The notify method in the NotifyListener is the callback of the registry, which is the reason why it can dynamically change according to the registry

Above, the doList method of Directory, which is an abstract method, selects the invoker list from the Directory by calling the doList method of the subclass RegistryDirectory.

@Override
    public List<Invoker<T>> doList(Invocation invocation) {
        if (forbidden) {
            // If forbidden is true, no service provider or service provider is unavailable
            throw new RpcException(RpcException.FORBIDDEN_EXCEPTION,
                "No provider available from registry " + getUrl().getAddress() + " for service " + getConsumerUrl().getServiceKey() + " on consumer " +  NetUtils.getLocalHost()
                        + " use dubbo version " + Version.getVersion() + ", please check status of providers(disabled, not registered or in blacklist).");
        }
        List<Invoker<T>> invokers = null;
        Map<String, List<Invoker<T>>> localMethodInvokerMap = this.methodInvokerMap;
        if (localMethodInvokerMap != null && localMethodInvokerMap.size() > 0) {
            // Get method name
            String methodName = RpcUtils.getMethodName(invocation);
            // Get method parameters
            Object[] args = RpcUtils.getArguments(invocation);
            if (args != null && args.length > 0 && args[0] != null
                    && (args[0] instanceof String || args[0].getClass().isEnum())) {
                // If the first parameter is a string type or an enumeration type
                // You can enumerate routes according to the first parameter
                invokers = localMethodInvokerMap.get(methodName + "." + args[0]);
            }
            if (invokers == null) {
                // Still null, get with method name
                invokers = localMethodInvokerMap.get(methodName);
            }
            if (invokers == null) {
                // Let it still be null, use * to randomly select an invoker to implement the call
                invokers = localMethodInvokerMap.get(Constants.ANY_VALUE);
            }
            if (invokers == null) {
                // Still null, use iterator to get an invoker
                Iterator<List<Invoker<T>>> iterator = localMethodInvokerMap.values().iterator();
                if (iterator.hasNext()) {
                    invokers = iterator.next();
                }
            }
        }
        return invokers == null ? new ArrayList<Invoker<T>>(0) : invokers;
    }

It can be seen from this that the Directory obtains the invoker from the methodInvokerMap. When does the invoker write to the methodInvokerMap in the Directory? It operates when the method notify is called back.

@Override
    public synchronized void notify(List<URL> urls) {
        // URL reference array for declaring invoker
        List<URL> invokerUrls = new ArrayList<URL>();

        // Declare the URL reference array of router
        List<URL> routerUrls = new ArrayList<URL>();

        // Declare the URL reference array of the configurator
        List<URL> configuratorUrls = new ArrayList<URL>();

        for (URL url : urls) {
            // Get agreement name
            String protocol = url.getProtocol();
            // Acquisition classification
            String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
            
            if (Constants.ROUTERS_CATEGORY.equals(category)
                    || Constants.ROUTE_PROTOCOL.equals(protocol)) {
                // If it is route classification or routing protocol
                routerUrls.add(url);
            } else if (Constants.CONFIGURATORS_CATEGORY.equals(category)
                    || Constants.OVERRIDE_PROTOCOL.equals(protocol)) {
                // If it's configurator classification or protocol rewriting
                configuratorUrls.add(url);
            } else if (Constants.PROVIDERS_CATEGORY.equals(category)) {
                // If provider classification
                invokerUrls.add(url);
            } else {
                logger.warn("Unsupported category " + category + " in notified url: " + url + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost());
            }
        }
        // Get configurators through url
        if (configuratorUrls != null && !configuratorUrls.isEmpty()) {
            this.configurators = toConfigurators(configuratorUrls);
        }
        // Get routes through url
        if (routerUrls != null && !routerUrls.isEmpty()) {
            List<Router> routers = toRouters(routerUrls);
            if (routers != null) { // null - do nothing
                setRouters(routers);
            }
        }
        List<Configurator> localConfigurators = this.configurators;
        // Merge override parameters
        this.overrideDirectoryUrl = directoryUrl;
        if (localConfigurators != null && !localConfigurators.isEmpty()) {
            for (Configurator configurator : localConfigurators) {
                this.overrideDirectoryUrl = configurator.configure(overrideDirectoryUrl);
            }
        }
        // Refresh providers
        refreshInvoker(invokerUrls);
    }

/**
     * Convert the invokerUrl list to the invoker Map. The conversion rules are as follows
     * 1.If the URL has been converted to an invoker, it is no longer re referenced, and it is obtained directly from the cache, and note that any parameter changes in the URL will be re referenced.
     * 2.If the incoming invoker list is not empty, it means it is the latest call list.
     * 3.If the incoming invokerUrl list is empty, it means that the rule is only an override or routing rule, which needs to be re compared to determine whether to re reference.
     * @param invokerUrls this parameter can't be null
     */
    private void refreshInvoker(List<URL> invokerUrls) {
        if (invokerUrls != null && invokerUrls.size() == 1 && invokerUrls.get(0) != null
                && Constants.EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
            // There is only one invoker url, and the protocol is empty

            // Set no access
            this.forbidden = true;
            // Set methodInvokerMap to null
            this.methodInvokerMap = null;
            // Close all invoker s
            destroyAllInvokers();

        } else {
            // Set allow access
            this.forbidden = false;

            Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap;

            if (invokerUrls.isEmpty() && this.cachedInvokerUrls != null) {
                // If the incoming invoker url is empty, add it from the cache
                invokerUrls.addAll(this.cachedInvokerUrls);
            } else {
                // Cache the invoker url for comparison
                this.cachedInvokerUrls = new HashSet<URL>();
                this.cachedInvokerUrls.addAll(invokerUrls);
            }
            if (invokerUrls.isEmpty()) {
                return;
            }
            // Convert the invoker url list to a Map with key as url and value as invoker
            Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);
            // Convert the invoker url list to a Map with key as method and value as invoker
            Map<String, List<Invoker<T>>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap);

            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;
            }
            // If there are multiple groups, merge methodInvokerMap
            this.methodInvokerMap = multiGroup ? toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap;
            this.urlInvokerMap = newUrlInvokerMap;
            try {
                // Close without invoker
                destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap);
            } catch (Exception e) {
                logger.warn("destroyUnusedInvokers error. ", e);
            }
        }
    }

In other words, if the registry changes, update the values of methodInvokerMap and urlInvokerMap (which will be mentioned later when we talk about the service reference principle). This is why the values mentioned on the official website may be dynamic, such as the reason why the registry pushes changes

Keywords: Java less Dubbo

Added by kingsol on Sun, 08 Dec 2019 22:08:06 +0200