@Override
    protected void doCloseAsync(CompletableFuture<?> future) {
        try {
            pathChildrenCache.close();
        } catch (IOException e) {
            logger.warn("Failed to close PathChildrenCache:", e);
        } finally {
            if (internalClient) {
                closeCuratorFrameworkThreadFactory.newThread(() -> {
                    try {
                        client.close();
                    } catch (Throwable cause) {
                        logger.warn("Failed to close CuratorFramework:", cause);
                    } finally {
                        future.complete(null);
                    }
                }).start();
            } else {
                future.complete(null);
            }
        }
    }
Returns a new {@link ZooKeeperEndpointGroupBuilder} created with the specified {@link CuratorFramework}
and ZNode path. Note that the specified {@link CuratorFramework} will not be destroyed when
the {@link ZooKeeperEndpointGroup} built by the returned builder is closed.

@param client the curator framework instance
@param znodePath the ZooKeeper node to register
@param spec the {@link ZooKeeperDiscoverySpec} to find and decode the registered instances
zookeeper3/src/main/java/com/linecorp/armeria/client/zookeeper/ZooKeeperEndpointGroup.java