@Override
    public void doSubscribe(final URL url, final NotifyListener listener) {
        try {
            checkDestroyed();
            if (ANY_VALUE.equals(url.getServiceInterface())) {
                String root = toRootPath();
                boolean check = url.getParameter(CHECK_KEY, false);
                ConcurrentMap<NotifyListener, ChildListener> listeners =
                        ConcurrentHashMapUtils.computeIfAbsent(zkListeners, url, k -> new ConcurrentHashMap<>());

                ChildListener zkListener = ConcurrentHashMapUtils.computeIfAbsent(
                        listeners, listener, k -> (parentPath, currentChildren) -> {
                            for (String child : currentChildren) {
                                try {
                                    child = URL.decode(child);
                                    if (!(JsonUtils.checkJson(child))) {
                                        throw new Exception("dubbo-admin subscribe " + child + " failed,beacause "
                                                + child + "is root path in " + url);
                                    }
                                } catch (Exception e) {
                                    logger.warn(PROTOCOL_ERROR_DESERIALIZE, "", "", e.getMessage());
                                }
                                if (!anyServices.contains(child)) {
                                    anyServices.add(child);
                                    subscribe(
                                            url.setPath(child)
                                                    .addParameters(
                                                            INTERFACE_KEY,
                                                            child,
                                                            Constants.CHECK_KEY,
                                                            String.valueOf(check)),
                                            k);
                                }
                            }
                        });

                zkClient.create(root, false, true);

                List<String> services = zkClient.addChildListener(root, zkListener);
                if (CollectionUtils.isNotEmpty(services)) {
                    for (String service : services) {
                        service = URL.decode(service);
                        anyServices.add(service);
                        subscribe(
                                url.setPath(service)
                                        .addParameters(
                                                INTERFACE_KEY, service, Constants.CHECK_KEY, String.valueOf(check)),
                                listener);
                    }
                }
            } else {
                CountDownLatch latch = new CountDownLatch(1);

                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 = ConcurrentHashMapUtils.computeIfAbsent(
                                zkListeners, url, k -> new ConcurrentHashMap<>());
                        ChildListener zkListener = ConcurrentHashMapUtils.computeIfAbsent(
                                listeners, listener, k -> new RegistryChildListenerImpl(url, k, latch));

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

                        // create "directories".
                        zkClient.create(path, false, true);

                        // 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));
                        }
                    }

                    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);
        }
    }
@Test
    void testDoSubscribeWithException() {
        Assertions.assertThrows(RpcException.class, () -> zookeeperRegistry.doSubscribe(anyUrl, listener));
    }