Add some code about error code 1-1, 4-1 with code optimization. (#10500)

* Add code of showing error code 4-1.

* Add some comments about error code 1-1 with code optimization.
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java
index f3b4f5d..9c283ff 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/RouterChain.java
@@ -241,6 +241,7 @@
         RouterSnapshotNode<T> commonRouterNode = new RouterSnapshotNode<T>("CommonRouter", resultInvokers.clone());
         parentNode.appendNode(commonRouterNode);
         List<Invoker<T>> commonRouterResult = resultInvokers;
+
         // 2. route common router
         for (Router router : routers) {
             // Copy resultInvokers to a arrayList. BitList not support
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java b/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
index 29d9df9..cb98b66 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
@@ -146,7 +146,12 @@
     public URL(URLAddress urlAddress, URLParam urlParam, Map<String, Object> attributes) {
         this.urlAddress = urlAddress;
         this.urlParam = null == urlParam ? URLParam.parse(new HashMap<>()) : urlParam;
-        this.attributes = (attributes != null ? attributes.isEmpty() ? null : attributes : null);
+
+        if (attributes != null && !attributes.isEmpty()) {
+            this.attributes = attributes;
+        } else {
+            this.attributes = null;
+        }
     }
 
     public URL(String protocol, String host, int port) {
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java
index 2d84934..474305c 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java
@@ -524,8 +524,9 @@
     }
 
     /**
-     * Find the extension with the given name. If the specified name is not found, then {@link IllegalStateException}
-     * will be thrown.
+     * Find the extension with the given name.
+     *
+     * @throws IllegalStateException If the specified extension is not found.
      */
     public T getExtension(String name) {
         T extension = getExtension(name, true);
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java
index 05deabe..243e1d4 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/url/component/URLParam.java
@@ -978,7 +978,7 @@
      *
      * @param params   params map added into URLParam
      * @param rawParam original rawParam string, directly add to rawParam field,
-     *                 will not effect real key-pairs store in URLParam.
+     *                 will not affect real key-pairs store in URLParam.
      *                 Please make sure it can correspond with params or will
      *                 cause unexpected result when calling {@link URLParam#getRawParam()}
      *                 and {@link URLParam#toString()} ()}. If you not sure, you can call
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java
index 11b1e3f..0167438 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java
@@ -412,7 +412,7 @@
         // If the category of provider URL does not match the category of consumer URL.
         // Usually, the provider URL's category is empty, and the default category ('providers') is present.
         // Hence, the category of the provider URL is 'providers'.
-        // Through observing of the Zookeeper registry, I found that the category of the consumer URL is 'consumers'.
+        // Through observing of debugging process, I found that the category of the consumer URL is 'providers,configurators,routers'.
         if (!isMatchCategory(providerUrl.getCategory(DEFAULT_CATEGORY), consumerUrl.getCategory(DEFAULT_CATEGORY))) {
             return false;
         }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
index 30daeb7..bdcf62d 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
@@ -294,10 +294,15 @@
                 continue;
             }
             if (!getUrl().getOrDefaultFrameworkModel().getExtensionLoader(Protocol.class).hasExtension(instanceAddressURL.getProtocol())) {
-                logger.error(new IllegalStateException("Unsupported protocol " + instanceAddressURL.getProtocol() +
+
+                // 4-1 - Unsupported protocol
+
+                logger.error("4-1", "protocol extension does not installed", "", "Unsupported protocol.",
+                    new IllegalStateException("Unsupported protocol " + instanceAddressURL.getProtocol() +
                     " in notified url: " + instanceAddressURL + " from registry " + getUrl().getAddress() +
                     " to consumer " + NetUtils.getLocalHost() + ", supported protocol: " +
                     getUrl().getOrDefaultFrameworkModel().getExtensionLoader(Protocol.class).getSupportedExtensions()));
+
                 continue;
             }
 
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java
index e3573de..afe1948 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java
@@ -361,37 +361,10 @@
         }
         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())) {
+            if (!checkProtocolValid(queryProtocols, providerUrl)) {
                 continue;
             }
 
-            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 url = mergeUrl(providerUrl);
 
             // Cache key is url that does not merge with consumer side parameters,
@@ -433,6 +406,44 @@
         return newUrlInvokerMap;
     }
 
+    private boolean checkProtocolValid(String queryProtocols, URL providerUrl) {
+        // 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) {
+                return false;
+            }
+        }
+
+        if (EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) {
+            return false;
+        }
+
+        if (!getUrl().getOrDefaultFrameworkModel().getExtensionLoader(Protocol.class).hasExtension(providerUrl.getProtocol())) {
+
+            // 4-1 - Unsupported protocol
+
+            logger.error("4-1", "protocol extension does not installed", "", "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()));
+
+            return false;
+        }
+
+        return true;
+    }
+
     /**
      * Merge url parameters. the order is: override > -D >Consumer > Provider
      *
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/CacheableFailbackRegistry.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/CacheableFailbackRegistry.java
index b1cccf2..f747a30 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/CacheableFailbackRegistry.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/CacheableFailbackRegistry.java
@@ -158,15 +158,21 @@
 
         // create new urls
         Map<String, ServiceAddressURL> newURLs = new HashMap<>((int) (providers.size() / 0.75f + 1));
+
+        // remove 'release', 'dubbo', 'methods', timestamp, 'dubbo.tag' parameter
+        // in consumer URL.
         URL copyOfConsumer = removeParamsFromConsumer(consumer);
 
         if (oldURLs == null) {
             for (String rawProvider : providers) {
+                // remove timestamp in provider url.
                 rawProvider = stripOffVariableKeys(rawProvider);
+
+                // create DubboServiceAddress object using provider url, consumer url, and extra parameters.
                 ServiceAddressURL cachedURL = createURL(rawProvider, copyOfConsumer, getExtraParameters());
                 if (cachedURL == null) {
                     // 1-1: Address invalid.
-                    logger.warn("1-1", "", "",
+                    logger.warn("1-1", "mismatch of service group and version settings", "",
                         "Invalid address, failed to parse into URL " + rawProvider);
 
                     continue;
@@ -174,14 +180,14 @@
                 newURLs.put(rawProvider, cachedURL);
             }
         } else {
-            // maybe only default , or "env" + default
+            // maybe only default, or "env" + default
             for (String rawProvider : providers) {
                 rawProvider = stripOffVariableKeys(rawProvider);
                 ServiceAddressURL cachedURL = oldURLs.remove(rawProvider);
                 if (cachedURL == null) {
                     cachedURL = createURL(rawProvider, copyOfConsumer, getExtraParameters());
                     if (cachedURL == null) {
-                        logger.warn("1-1", "", "",
+                        logger.warn("1-1", "mismatch of service group and version settings", "",
                             "Invalid address, failed to parse into URL " + rawProvider);
 
                         continue;
@@ -233,16 +239,31 @@
         return urls;
     }
 
+    /**
+     * Create DubboServiceAddress object using provider url, consumer url, and extra parameters.
+     *
+     * @param rawProvider provider url string
+     * @param consumerURL URL object of consumer
+     * @param extraParameters extra parameters
+     * @return created DubboServiceAddressURL object
+     */
     protected ServiceAddressURL createURL(String rawProvider, URL consumerURL, Map<String, String> extraParameters) {
+
         boolean encoded = true;
 
         // use encoded value directly to avoid URLDecoder.decode allocation.
         int paramStartIdx = rawProvider.indexOf(ENCODED_QUESTION_MARK);
-        if (paramStartIdx == -1) {// if ENCODED_QUESTION_MARK does not show, mark as not encoded.
+
+        if (paramStartIdx == -1) {
+            // if ENCODED_QUESTION_MARK does not show, mark as not encoded.
             encoded = false;
         }
 
+        // split by (encoded) question mark.
+        // part[0] => protocol + ip address + interface.
+        // part[1] => parameters (metadata).
         String[] parts = URLStrParser.parseRawURLToArrays(rawProvider, paramStartIdx);
+
         if (parts.length <= 1) {
             // 1-5 Received URL without any parameters.
             logger.warn("1-5", "", "",
@@ -257,16 +278,18 @@
         // Workaround for 'effectively final': duplicate the encoded variable.
         boolean isEncoded = encoded;
 
+        // PathURLAddress if it's using dubbo protocol.
         URLAddress address = stringAddress.computeIfAbsent(rawAddress, k -> URLAddress.parse(k, getDefaultURLProtocol(), isEncoded));
         address.setTimestamp(System.currentTimeMillis());
 
         URLParam param = stringParam.computeIfAbsent(rawParams, k -> URLParam.parse(k, isEncoded, extraParameters));
         param.setTimestamp(System.currentTimeMillis());
 
-        ServiceAddressURL cachedURL = createServiceURL(address, param, consumerURL);
+        // create service URL using cached address, param, and consumer URL.
+        ServiceAddressURL cachedServiceAddressURL = createServiceURL(address, param, consumerURL);
 
-        if (isMatch(consumerURL, cachedURL)) {
-            return cachedURL;
+        if (isMatch(consumerURL, cachedServiceAddressURL)) {
+            return cachedServiceAddressURL;
         }
 
         return null;
diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistry.java b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistry.java
index e4af8f8..48b3005 100644
--- a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistry.java
+++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistry.java
@@ -185,6 +185,14 @@
                 try {
                     List<URL> urls = new ArrayList<>();
 
+                    /*
+                        Iterate over the category value in URL.
+                        With default settings, the path variable can be when url is a consumer URL:
+
+                            /dubbo/[service name]/providers,
+                            /dubbo/[service name]/configurators
+                            /dubbo/[service name]/routers
+                    */
                     for (String path : toCategoriesPath(url)) {
                         ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.computeIfAbsent(url, k -> new ConcurrentHashMap<>());
                         ChildListener zkListener = listeners.computeIfAbsent(listener, k -> new RegistryChildListenerImpl(url, k, latch));
@@ -193,9 +201,13 @@
                             ((RegistryChildListenerImpl) zkListener).setLatch(latch);
                         }
 
+                        // create "directories".
                         zkClient.create(path, false);
+
+                        // Add children (i.e. service items).
                         List<String> children = zkClient.addChildListener(path, zkListener);
                         if (children != null) {
+                            // The invocation point that may cause 1-1.
                             urls.addAll(toUrlsWithEmpty(url, path, children));
                         }
                     }
@@ -321,6 +333,11 @@
         return UrlUtils.isMatch(subscribeUrl, providerUrl);
     }
 
+    /**
+     * Triggered when children get changed. It will be invoked by implementation of CuratorWatcher.
+     *
+     * 'org.apache.dubbo.remoting.zookeeper.curator5.Curator5ZookeeperClient.CuratorWatcherImpl' (Curator 5)
+     */
     private class RegistryChildListenerImpl implements ChildListener {
         private final ZookeeperRegistryNotifier notifier;
         private volatile CountDownLatch latch;
@@ -336,11 +353,13 @@
 
         @Override
         public void childChanged(String path, List<String> children) {
+            // Notify 'notifiers' one by one.
             try {
                 latch.await();
             } catch (InterruptedException e) {
                 logger.warn("Zookeeper children listener thread was interrupted unexpectedly, may cause race condition with the main thread.");
             }
+
             notifier.notify(path, children);
         }
     }