You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ma...@apache.org on 2020/07/22 11:53:26 UTC

[lucene-solr] branch reference_impl updated: @293 Start on correct client sharing.

This is an automated email from the ASF dual-hosted git repository.

markrmiller pushed a commit to branch reference_impl
in repository https://gitbox.apache.org/repos/asf/lucene-solr.git


The following commit(s) were added to refs/heads/reference_impl by this push:
     new 4870ff6  @293 Start on correct client sharing.
4870ff6 is described below

commit 4870ff6120add57452096b0ee7f8626da79c9301
Author: markrmiller@gmail.com <ma...@gmail.com>
AuthorDate: Wed Jul 22 06:53:02 2020 -0500

    @293 Start on correct client sharing.
---
 .../client/solrj/embedded/JettySolrRunner.java     | 13 ++++-----
 .../src/java/org/apache/solr/cloud/Overseer.java   |  6 ++---
 .../java/org/apache/solr/cloud/ZkController.java   | 14 +++++++---
 .../solr/cloud/autoscaling/sim/SimScenario.java    | 11 +++++---
 .../solr/handler/admin/MetricsHistoryHandler.java  | 28 -------------------
 .../src/java/org/apache/solr/util/SolrCLI.java     |  8 ++++--
 .../cloud/autoscaling/SearchRateTriggerTest.java   |  2 +-
 .../solr/cloud/autoscaling/TestPolicyCloud.java    |  2 +-
 .../client/solrj/impl/CloudHttp2SolrClient.java    | 19 ++++++++-----
 .../solr/client/solrj/impl/LBSolrClient.java       |  3 ++-
 .../client/solrj/impl/SolrClientCloudManager.java  | 31 +++++++++++++++++-----
 .../solrj/impl/SolrClientNodeStateProvider.java    | 20 +++++++++-----
 .../solrj/impl/ZkClientClusterStateProvider.java   | 27 +------------------
 .../client/solrj/cloud/autoscaling/TestPolicy.java |  8 +++---
 .../solrj/cloud/autoscaling/TestPolicy2.java       |  4 +--
 15 files changed, 96 insertions(+), 100 deletions(-)

diff --git a/solr/core/src/java/org/apache/solr/client/solrj/embedded/JettySolrRunner.java b/solr/core/src/java/org/apache/solr/client/solrj/embedded/JettySolrRunner.java
index a42c9bb..e8ace53 100644
--- a/solr/core/src/java/org/apache/solr/client/solrj/embedded/JettySolrRunner.java
+++ b/solr/core/src/java/org/apache/solr/client/solrj/embedded/JettySolrRunner.java
@@ -53,6 +53,7 @@ import java.util.concurrent.atomic.AtomicLong;
 import org.apache.lucene.util.Constants;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.cloud.SocketProxy;
+import org.apache.solr.client.solrj.impl.Http2SolrClient;
 import org.apache.solr.client.solrj.impl.HttpClientUtil;
 import org.apache.solr.client.solrj.impl.HttpSolrClient;
 import org.apache.solr.client.solrj.impl.SolrHttpClientScheduler;
@@ -888,15 +889,15 @@ public class JettySolrRunner implements Closeable {
   }
 
   public SolrClient newClient() {
-    return new HttpSolrClient.Builder(getBaseUrl().toString()).
-            withHttpClient(getCoreContainer().getUpdateShardHandler().getDefaultHttpClient()).build();
+    return new Http2SolrClient.Builder(getBaseUrl().toString()).
+            withHttpClient(getCoreContainer().getUpdateShardHandler().getUpdateOnlyHttpClient()).build();
   }
 
   public SolrClient newClient(int connectionTimeoutMillis, int socketTimeoutMillis) {
-    return new HttpSolrClient.Builder(getBaseUrl().toString())
-        .withConnectionTimeout(connectionTimeoutMillis)
-        .withSocketTimeout(socketTimeoutMillis)
-        .withHttpClient(getCoreContainer().getUpdateShardHandler().getDefaultHttpClient())
+    return new Http2SolrClient.Builder(getBaseUrl().toString())
+        .connectionTimeout(connectionTimeoutMillis)
+        .idleTimeout(socketTimeoutMillis)
+        .withHttpClient(getCoreContainer().getUpdateShardHandler().getUpdateOnlyHttpClient())
         .build();
   }
 
diff --git a/solr/core/src/java/org/apache/solr/cloud/Overseer.java b/solr/core/src/java/org/apache/solr/cloud/Overseer.java
index e0e4c2f..38a823b 100644
--- a/solr/core/src/java/org/apache/solr/cloud/Overseer.java
+++ b/solr/core/src/java/org/apache/solr/cloud/Overseer.java
@@ -43,6 +43,7 @@ import org.apache.lucene.util.Version;
 import org.apache.solr.client.solrj.SolrServerException;
 import org.apache.solr.client.solrj.cloud.SolrCloudManager;
 import org.apache.solr.client.solrj.cloud.autoscaling.AlreadyExistsException;
+import org.apache.solr.client.solrj.impl.CloudHttp2SolrClient;
 import org.apache.solr.client.solrj.impl.CloudSolrClient;
 import org.apache.solr.client.solrj.impl.ClusterStateProvider;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
@@ -725,9 +726,8 @@ public class Overseer implements SolrCloseable {
     } else {
       return;
     }
-
-    try (CloudSolrClient client = new CloudSolrClient.Builder(    getCoreContainer().getZkController().getZkStateReader())
-            .withHttpClient(updateShardHandler.getDefaultHttpClient()).build()) {
+    try {
+      CloudHttp2SolrClient client = getCoreContainer().getZkController().getCloudSolrClient();
       CollectionAdminRequest.ColStatus req = CollectionAdminRequest.collectionStatus(CollectionAdminParams.SYSTEM_COLL)
           .setWithSegments(true)
           .setWithFieldInfo(true);
diff --git a/solr/core/src/java/org/apache/solr/cloud/ZkController.java b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
index 0a52937..2ddf746 100644
--- a/solr/core/src/java/org/apache/solr/cloud/ZkController.java
+++ b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
@@ -63,8 +63,10 @@ import com.google.common.base.Strings;
 import org.apache.commons.io.output.StringBuilderWriter;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.curator.framework.api.transaction.CuratorTransactionResult;
+import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.cloud.DistributedLock;
 import org.apache.solr.client.solrj.cloud.SolrCloudManager;
+import org.apache.solr.client.solrj.impl.CloudHttp2SolrClient;
 import org.apache.solr.client.solrj.impl.CloudSolrClient;
 import org.apache.solr.client.solrj.impl.HttpSolrClient;
 import org.apache.solr.client.solrj.impl.HttpSolrClient.Builder;
@@ -251,7 +253,7 @@ public class ZkController implements Closeable {
   private volatile SolrZkClient zkClient;
   public volatile ZkStateReader zkStateReader;
   private volatile SolrCloudManager cloudManager;
-  private volatile CloudSolrClient cloudSolrClient;
+  private volatile CloudHttp2SolrClient cloudSolrClient;
 
   private final String zkServerAddress;          // example: 127.0.0.1:54062/solr
 
@@ -724,18 +726,22 @@ public class ZkController implements Closeable {
       if (cloudManager != null) {
         return cloudManager;
       }
-      cloudSolrClient = new CloudSolrClient.Builder(new ZkClientClusterStateProvider(zkStateReader))
-          .withHttpClient(cc.getUpdateShardHandler().getDefaultHttpClient())
+      cloudSolrClient = new CloudHttp2SolrClient.Builder(zkStateReader)
+          .withHttpClient(cc.getUpdateShardHandler().getUpdateOnlyHttpClient())
           .build();
       cloudManager = new SolrClientCloudManager(
           new ZkDistributedQueueFactory(zkClient),
           cloudSolrClient,
-          cc.getObjectCache());
+          cc.getObjectCache(), cc.getUpdateShardHandler().getDefaultHttpClient());
       cloudManager.getClusterStateProvider().connect();
     }
     return cloudManager;
   }
 
+  public CloudHttp2SolrClient getCloudSolrClient() {
+    return  cloudSolrClient;
+  }
+
   /**
    * Returns config file data (in bytes)
    */
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/sim/SimScenario.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/sim/SimScenario.java
index ae52355..4ef2b47 100644
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/sim/SimScenario.java
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/sim/SimScenario.java
@@ -43,6 +43,7 @@ import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.http.client.HttpClient;
 import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrResponse;
 import org.apache.solr.client.solrj.cloud.autoscaling.AutoScalingConfig;
@@ -53,7 +54,9 @@ import org.apache.solr.client.solrj.cloud.autoscaling.ReplicaInfo;
 import org.apache.solr.client.solrj.cloud.autoscaling.Suggester;
 import org.apache.solr.client.solrj.cloud.autoscaling.TriggerEventProcessorStage;
 import org.apache.solr.client.solrj.cloud.autoscaling.Variable;
+import org.apache.solr.client.solrj.impl.CloudHttp2SolrClient;
 import org.apache.solr.client.solrj.impl.CloudSolrClient;
+import org.apache.solr.client.solrj.impl.HttpClientUtil;
 import org.apache.solr.client.solrj.impl.SolrClientCloudManager;
 import org.apache.solr.client.solrj.request.GenericSolrRequest;
 import org.apache.solr.client.solrj.request.RequestWriter;
@@ -382,12 +385,14 @@ public class SimScenario implements AutoCloseable {
         if (zkHost == null) {
           throw new IOException(SimAction.LOAD_SNAPSHOT + " must specify 'path' or 'zkHost'");
         } else {
-
-          try (CloudSolrClient cloudSolrClient = new CloudSolrClient.Builder(Collections.singletonList(zkHost), Optional.empty()).build()) {
+          HttpClient client = HttpClientUtil.createClient(new ModifiableSolrParams());
+          try (CloudHttp2SolrClient cloudSolrClient = new CloudHttp2SolrClient.Builder(Collections.singletonList(zkHost), Optional.empty()).build()) {
             cloudSolrClient.connect();
-            try (SolrClientCloudManager realCloudManager = new SolrClientCloudManager(NoopDistributedQueueFactory.INSTANCE, cloudSolrClient)) {
+            try (SolrClientCloudManager realCloudManager = new SolrClientCloudManager(NoopDistributedQueueFactory.INSTANCE, cloudSolrClient, client)) {
               snapshotCloudManager = new SnapshotCloudManager(realCloudManager, null);
             }
+          } finally {
+            HttpClientUtil.close(client);
           }
         }
       } else {
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/MetricsHistoryHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/MetricsHistoryHandler.java
index 2b1afb4..05234a3 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/MetricsHistoryHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/MetricsHistoryHandler.java
@@ -808,34 +808,6 @@ public class MetricsHistoryHandler extends RequestHandlerBase implements Permiss
     rsp.getResponseHeader().add("zkConnected", cloudManager != null);
   }
 
-  @SuppressWarnings({"unchecked"})
-  private NamedList<Object> handleRemoteRequest(String nodeName, SolrQueryRequest req) {
-    String baseUrl = Utils.getBaseUrlForNodeName(nodeName, overseerUrlScheme);
-    String url;
-    try {
-      URL u = new URL(baseUrl);
-      u = new URL(u.getProtocol(), u.getHost(), u.getPort(), "/api/cluster/metrics/history");
-      url = u.toString();
-    } catch (MalformedURLException e) {
-      log.warn("Invalid Overseer url '{}', unable to fetch remote metrics history", baseUrl, e);
-      return null;
-    }
-    // always use javabin
-    ModifiableSolrParams params = new ModifiableSolrParams(req.getParams());
-    params.set(CommonParams.WT, "javabin");
-    url = url + "?" + params.toString();
-    try {
-      byte[] data = cloudManager.httpRequest(url, SolrRequest.METHOD.GET, null, null, HttpClientUtil.DEFAULT_CONNECT_TIMEOUT, true);
-      // response is always a NamedList
-      try (JavaBinCodec codec = new JavaBinCodec()) {
-        return (NamedList<Object>)codec.unmarshal(new ByteArrayInputStream(data));
-      }
-    } catch (IOException e) {
-      log.warn("Exception forwarding request to Overseer at {}", url, e);
-      return null;
-    }
-  }
-
   @SuppressWarnings({"unchecked", "rawtypes"})
   private void mergeRemoteRes(SolrQueryResponse rsp, NamedList<Object> remoteRes) {
     if (remoteRes == null || remoteRes.get("metrics") == null) {
diff --git a/solr/core/src/java/org/apache/solr/util/SolrCLI.java b/solr/core/src/java/org/apache/solr/util/SolrCLI.java
index 3a9df10..94ca548 100755
--- a/solr/core/src/java/org/apache/solr/util/SolrCLI.java
+++ b/solr/core/src/java/org/apache/solr/util/SolrCLI.java
@@ -103,6 +103,7 @@ import org.apache.solr.client.solrj.cloud.autoscaling.PolicyHelper;
 import org.apache.solr.client.solrj.cloud.autoscaling.ReplicaInfo;
 import org.apache.solr.client.solrj.cloud.autoscaling.Suggester;
 import org.apache.solr.client.solrj.cloud.autoscaling.Variable;
+import org.apache.solr.client.solrj.impl.CloudHttp2SolrClient;
 import org.apache.solr.client.solrj.impl.CloudSolrClient;
 import org.apache.solr.client.solrj.impl.HttpClientUtil;
 import org.apache.solr.client.solrj.impl.HttpSolrClient;
@@ -988,19 +989,22 @@ public class SolrCLI implements CLIO {
         String zkHost = cli.getOptionValue("zkHost", ZK_HOST);
 
         log.debug("Connecting to Solr cluster: {}", zkHost);
-        try (CloudSolrClient cloudSolrClient = new CloudSolrClient.Builder(Collections.singletonList(zkHost), Optional.empty()).build()) {
+        try (CloudHttp2SolrClient cloudSolrClient = new CloudHttp2SolrClient.Builder(Collections.singletonList(zkHost), Optional.empty()).build()) {
 
           String collection = cli.getOptionValue("collection");
           if (collection != null)
             cloudSolrClient.setDefaultCollection(collection);
 
           cloudSolrClient.connect();
-          try (SolrClientCloudManager realCloudManager = new SolrClientCloudManager(NoopDistributedQueueFactory.INSTANCE, cloudSolrClient)) {
+          CloseableHttpClient httpClient = getHttpClient();
+          try (SolrClientCloudManager realCloudManager = new SolrClientCloudManager(NoopDistributedQueueFactory.INSTANCE, cloudSolrClient, httpClient)) {
             if (config == null) {
               CLIO.err("- reading autoscaling config from the cluster.");
               config = realCloudManager.getDistribStateManager().getAutoScalingConfig();
             }
             cloudManager = new SnapshotCloudManager(realCloudManager, config);
+          } finally {
+            closeHttpClient(httpClient);
           }
         }
       }
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/SearchRateTriggerTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/SearchRateTriggerTest.java
index b9051ea..387f5c9 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/SearchRateTriggerTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/SearchRateTriggerTest.java
@@ -223,7 +223,7 @@ public class SearchRateTriggerTest extends SolrCloudTestCase {
     SolrCloudManager cloudManager = new SolrClientCloudManager(new ZkDistributedQueueFactory(zkClient), solrClient) {
       @Override
       public NodeStateProvider getNodeStateProvider() {
-        return new SolrClientNodeStateProvider(solrClient) {
+        return new SolrClientNodeStateProvider(solrClient, null) {
           @Override
           public Map<String, Object> getNodeValues(String node, Collection<String> tags) {
             Map<String, Object> values = super.getNodeValues(node, tags);
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/TestPolicyCloud.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/TestPolicyCloud.java
index 3bab7e2..795aaaa 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/TestPolicyCloud.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/TestPolicyCloud.java
@@ -155,7 +155,7 @@ public class TestPolicyCloud extends SolrCloudTestCase {
         "}";
     AutoScalingConfig config = new AutoScalingConfig((Map<String, Object>) Utils.fromJSONString(autoScaleJson));
     AtomicInteger count = new AtomicInteger(0);
-    try (SolrCloudManager cloudManager = new SolrClientCloudManager(new ZkDistributedQueueFactory(cluster.getZkClient()), cluster.getSolrClient())) {
+    try (SolrCloudManager cloudManager = new SolrClientCloudManager(new ZkDistributedQueueFactory(cluster.getZkClient()), cluster.getSolrClient(), cluster.getSolrClient().getHttpClient())) {
       String nodeName = cloudManager.getClusterStateProvider().getLiveNodes().iterator().next();
       SolrClientNodeStateProvider nodeStateProvider = (SolrClientNodeStateProvider) cloudManager.getNodeStateProvider();
       Map<String, Map<String, List<ReplicaInfo>>> result = nodeStateProvider.getReplicaInfo(nodeName, Collections.singleton("UPDATE./update.requests"));
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudHttp2SolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudHttp2SolrClient.java
index 5c457f9..1617b80 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudHttp2SolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudHttp2SolrClient.java
@@ -27,6 +27,7 @@ import org.apache.solr.client.solrj.request.UpdateRequest;
 import org.apache.solr.common.ParWork;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.cloud.ZkStateReader;
+import org.apache.solr.common.util.ObjectReleaseTracker;
 
 /**
  * SolrJ client class to communicate with SolrCloud using Http2SolrClient.
@@ -60,6 +61,7 @@ public class CloudHttp2SolrClient  extends BaseCloudSolrClient {
    */
   protected CloudHttp2SolrClient(Builder builder) {
     super(builder.shardLeadersOnly, builder.parallelUpdates, builder.directUpdatesToLeadersOnly);
+    assert ObjectReleaseTracker.track(this);
     this.clientIsInternal = builder.httpClient == null;
     this.myClient = (builder.httpClient == null) ? new Http2SolrClient.Builder().build() : builder.httpClient;
     if (builder.stateProvider == null) {
@@ -91,14 +93,14 @@ public class CloudHttp2SolrClient  extends BaseCloudSolrClient {
 
   @Override
   public void close() throws IOException {
-    stateProvider.close();
-    lbClient.close();
-
-    if (clientIsInternal && myClient!=null) {
-      myClient.close();
+    try (ParWork closer = new ParWork(this, true)) {
+      closer.add("CloudHttp2SolrClient#close", stateProvider, lbClient);
+      if (clientIsInternal && myClient!=null) {
+        closer.add("http2Client", myClient);
+      }
     }
-
     super.close();
+    assert ObjectReleaseTracker.release(this);
   }
 
   public LBHttp2SolrClient getLbClient() {
@@ -178,6 +180,11 @@ public class CloudHttp2SolrClient  extends BaseCloudSolrClient {
       if (zkChroot.isPresent()) this.zkChroot = zkChroot.get();
     }
 
+    public Builder(ZkStateReader zkStateReader) {
+      ZkClientClusterStateProvider stateProvider = new ZkClientClusterStateProvider(zkStateReader, false);
+      this.stateProvider = stateProvider;
+    }
+
     /**
      * Tells {@link CloudHttp2SolrClient.Builder} that created clients should send direct updates to shard leaders only.
      *
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBSolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBSolrClient.java
index fa48073..c9e0a78 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBSolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBSolrClient.java
@@ -198,7 +198,7 @@ public abstract class LBSolrClient extends SolrClient {
   }
 
   public LBSolrClient(List<String> baseSolrUrls) {
-    ObjectReleaseTracker.track(this);
+    assert ObjectReleaseTracker.track(this);
     if (!baseSolrUrls.isEmpty()) {
       for (String s : baseSolrUrls) {
         ServerWrapper wrapper = createServerWrapper(s);
@@ -710,5 +710,6 @@ public abstract class LBSolrClient extends SolrClient {
 
     if (aliveCheckExecutor != null) aliveCheckExecutor.shutdownNow();
     ParWork.close(aliveCheckExecutor);
+    assert ObjectReleaseTracker.release(this);
   }
 }
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrClientCloudManager.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrClientCloudManager.java
index 7aacd42..cfbef61 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrClientCloudManager.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrClientCloudManager.java
@@ -55,23 +55,42 @@ import org.slf4j.LoggerFactory;
 public class SolrClientCloudManager implements SolrCloudManager {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
-  protected final CloudSolrClient solrClient;
+  protected final BaseCloudSolrClient solrClient;
   private final ZkDistribStateManager stateManager;
   private final DistributedQueueFactory queueFactory;
   private final ZkStateReader zkStateReader;
   private final SolrZkClient zkClient;
   private final ObjectCache objectCache;
   private final boolean closeObjectCache;
+  private final HttpClient httpClient;
   private volatile boolean isClosed;
 
-  public SolrClientCloudManager(DistributedQueueFactory queueFactory, CloudSolrClient solrClient) {
-    this(queueFactory, solrClient, null);
+  public SolrClientCloudManager(DistributedQueueFactory queueFactory, BaseCloudSolrClient solrClient) {
+    this(queueFactory, solrClient, null, null);
   }
 
-  public SolrClientCloudManager(DistributedQueueFactory queueFactory, CloudSolrClient solrClient,
+  public SolrClientCloudManager(DistributedQueueFactory queueFactory, BaseCloudSolrClient solrClient, HttpClient httpClient) {
+    this(queueFactory, solrClient, null, httpClient);
+  }
+
+  public SolrClientCloudManager(DistributedQueueFactory queueFactory, BaseCloudSolrClient solrClient,
                                 ObjectCache objectCache) {
+    this(queueFactory, solrClient, objectCache, null);
+  }
+
+  public SolrClientCloudManager(DistributedQueueFactory queueFactory, BaseCloudSolrClient solrClient,
+                                ObjectCache objectCache, HttpClient httpClient) {
     this.queueFactory = queueFactory;
     this.solrClient = solrClient;
+
+    if (httpClient == null && solrClient instanceof  CloudSolrClient) {
+      this.httpClient = ((CloudSolrClient) solrClient).getHttpClient();
+    } else if (httpClient == null) {
+      throw new IllegalArgumentException("Must specify apache httpclient with non CloudSolrServer impls");
+    } else {
+      this.httpClient = httpClient;
+    }
+
     this.zkStateReader = solrClient.getZkStateReader();
     this.zkClient = zkStateReader.getZkClient();
     this.stateManager = new ZkDistribStateManager(zkClient);
@@ -115,7 +134,7 @@ public class SolrClientCloudManager implements SolrCloudManager {
 
   @Override
   public NodeStateProvider getNodeStateProvider() {
-    return new SolrClientNodeStateProvider(solrClient);
+    return new SolrClientNodeStateProvider(solrClient, httpClient);
   }
 
   @Override
@@ -136,7 +155,7 @@ public class SolrClientCloudManager implements SolrCloudManager {
 
   @Override
   public byte[] httpRequest(String url, SolrRequest.METHOD method, Map<String, String> headers, String payload, int timeout, boolean followRedirects) throws IOException {
-    HttpClient client = solrClient.getHttpClient();
+    HttpClient client = httpClient;
     final HttpRequestBase req;
     HttpEntity entity = null;
     if (payload != null) {
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrClientNodeStateProvider.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrClientNodeStateProvider.java
index 00f0cb0..8747de4 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrClientNodeStateProvider.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrClientNodeStateProvider.java
@@ -31,6 +31,7 @@ import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
+import org.apache.http.client.HttpClient;
 import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrServerException;
 import org.apache.solr.client.solrj.cloud.NodeStateProvider;
@@ -76,14 +77,16 @@ public class SolrClientNodeStateProvider implements NodeStateProvider, MapWriter
 
 
 
-  private final CloudSolrClient solrClient;
+  private final BaseCloudSolrClient solrClient;
   protected final Map<String, Map<String, Map<String, List<ReplicaInfo>>>> nodeVsCollectionVsShardVsReplicaInfo = new HashMap<>();
+  private final HttpClient httpClient;
   private Map<String, Object> snitchSession = new HashMap<>();
   private Map<String, Map> nodeVsTags = new HashMap<>();
   private Map<String, String> withCollectionsMap = new HashMap<>();
 
-  public SolrClientNodeStateProvider(CloudSolrClient solrClient) {
+  public SolrClientNodeStateProvider(BaseCloudSolrClient solrClient, HttpClient httpClient) {
     this.solrClient = solrClient;
+    this.httpClient = httpClient;
     try {
       readReplicaDetails();
     } catch (IOException e) {
@@ -136,7 +139,7 @@ public class SolrClientNodeStateProvider implements NodeStateProvider, MapWriter
 
   protected Map<String, Object> fetchTagValues(String node, Collection<String> tags) {
     AutoScalingSnitch snitch = new AutoScalingSnitch();
-    ClientSnitchCtx ctx = new ClientSnitchCtx(null, node, snitchSession, solrClient);
+    ClientSnitchCtx ctx = new ClientSnitchCtx(null, node, snitchSession, solrClient, httpClient);
     snitch.getTags(node, new HashSet<>(tags), ctx);
     return ctx.getTags();
   }
@@ -183,7 +186,7 @@ public class SolrClientNodeStateProvider implements NodeStateProvider, MapWriter
   protected Map<String, Object> fetchReplicaMetrics(String node, Map<String, Pair<String, ReplicaInfo>> metricsKeyVsTagReplica) {
     Map<String, Object> collect = metricsKeyVsTagReplica.entrySet().stream()
         .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getKey));
-    ClientSnitchCtx ctx = new ClientSnitchCtx(null, null, emptyMap(), solrClient);
+    ClientSnitchCtx ctx = new ClientSnitchCtx(null, null, emptyMap(), solrClient, httpClient);
     fetchReplicaMetrics(node, ctx, collect);
     return ctx.getTags();
 
@@ -319,7 +322,9 @@ public class SolrClientNodeStateProvider implements NodeStateProvider, MapWriter
     private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
     ZkClientClusterStateProvider zkClientClusterStateProvider;
-    CloudSolrClient solrClient;
+    BaseCloudSolrClient solrClient;
+
+    HttpClient httpClient;
 
     public boolean isNodeAlive(String node) {
       if (zkClientClusterStateProvider != null) {
@@ -329,9 +334,10 @@ public class SolrClientNodeStateProvider implements NodeStateProvider, MapWriter
     }
     public ClientSnitchCtx(SnitchInfo perSnitch,
                            String node, Map<String, Object> session,
-                           CloudSolrClient solrClient) {
+                           BaseCloudSolrClient solrClient, HttpClient httpClient) {
       super(perSnitch, node, session);
       this.solrClient = solrClient;
+      this.httpClient = httpClient;
       this.zkClientClusterStateProvider = (ZkClientClusterStateProvider) solrClient.getClusterStateProvider();
     }
 
@@ -382,7 +388,7 @@ public class SolrClientNodeStateProvider implements NodeStateProvider, MapWriter
 
       GenericSolrRequest request = new GenericSolrRequest(SolrRequest.METHOD.POST, path, params);
       try (HttpSolrClient client = new HttpSolrClient.Builder()
-          .withHttpClient(solrClient.getHttpClient())
+          .withHttpClient(httpClient)
           .withBaseSolrUrl(url)
           .withResponseParser(new BinaryResponseParser())
           .markInternalRequest()
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ZkClientClusterStateProvider.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ZkClientClusterStateProvider.java
index 1e85b29..4b8c3fc 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ZkClientClusterStateProvider.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ZkClientClusterStateProvider.java
@@ -41,7 +41,7 @@ public class ZkClientClusterStateProvider implements ClusterStateProvider {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
 
-  volatile ZkStateReader zkStateReader;
+  final ZkStateReader zkStateReader;
   private boolean closeZkStateReader = false;
   final String zkHost;
   int zkConnectTimeout = 15000;
@@ -159,30 +159,6 @@ public class ZkClientClusterStateProvider implements ClusterStateProvider {
   }
   
   public ZkStateReader getZkStateReader() {
-    if (isClosed) { // quick check...
-      throw new AlreadyClosedException();
-    }
-    if (zkStateReader == null) {
-      synchronized (this) {
-        if (isClosed) { // while we were waiting for sync lock another thread may have closed
-          throw new AlreadyClosedException();
-        }
-        if (zkStateReader == null) {
-          ZkStateReader zk = null;
-          try {
-            zk = new ZkStateReader(zkHost, zkClientTimeout, zkConnectTimeout);
-            zk.createClusterStateWatchersAndUpdate();
-            log.info("Cluster at {} ready", zkHost);
-            zkStateReader = zk;
-          } catch (Exception e) {
-            ParWork.propegateInterrupt(e);
-            if (zk != null) zk.close();
-            // do not wrap because clients may be relying on the underlying exception being thrown
-            throw e;
-          }
-        }
-      }
-    }
     return zkStateReader;
   }
   
@@ -195,7 +171,6 @@ public class ZkClientClusterStateProvider implements ClusterStateProvider {
         // force zkStateReader to null first so that any parallel calls drop into the synch block 
         // getZkStateReader() as soon as possible.
         final ZkStateReader zkToClose = zkStateReader;
-        zkStateReader = null;
         if (closeZkStateReader) {
           zkToClose.close();
         }
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/cloud/autoscaling/TestPolicy.java b/solr/solrj/src/test/org/apache/solr/client/solrj/cloud/autoscaling/TestPolicy.java
index c2bbd4d..36c9664 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/cloud/autoscaling/TestPolicy.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/cloud/autoscaling/TestPolicy.java
@@ -158,7 +158,7 @@ public class TestPolicy extends SolrTestCaseJ4 {
       }
     };
 
-    SolrClientNodeStateProvider solrClientNodeStateProvider = new SolrClientNodeStateProvider(null) {
+    SolrClientNodeStateProvider solrClientNodeStateProvider = new SolrClientNodeStateProvider(null, null) {
       @Override
       protected Map<String, Object> fetchTagValues(String node, Collection<String> tags) {
         Map<String, Object> result = new HashMap<>();
@@ -250,7 +250,7 @@ public class TestPolicy extends SolrTestCaseJ4 {
       }
     };
 
-    SolrClientNodeStateProvider solrClientNodeStateProvider = new SolrClientNodeStateProvider(null) {
+    SolrClientNodeStateProvider solrClientNodeStateProvider = new SolrClientNodeStateProvider(null, null) {
       @Override
       protected Map<String, Object> fetchTagValues(String node, Collection<String> tags) {
         Map<String, Object> result = new HashMap<>();
@@ -449,7 +449,7 @@ public class TestPolicy extends SolrTestCaseJ4 {
       }
     };
 
-    SolrClientNodeStateProvider solrClientNodeStateProvider = new SolrClientNodeStateProvider(null) {
+    SolrClientNodeStateProvider solrClientNodeStateProvider = new SolrClientNodeStateProvider(null, null) {
       @Override
       protected Map<String, Object> fetchTagValues(String node, Collection<String> tags) {
         Map<String, Object> result = new HashMap<>();
@@ -824,7 +824,7 @@ public class TestPolicy extends SolrTestCaseJ4 {
       }
     };
 
-    SolrClientNodeStateProvider solrClientNodeStateProvider = new SolrClientNodeStateProvider(null) {
+    SolrClientNodeStateProvider solrClientNodeStateProvider = new SolrClientNodeStateProvider(null, null) {
       @Override
       protected Map<String, Object> fetchTagValues(String node, Collection<String> tags) {
         Map<String, Object> result = new HashMap<>();
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/cloud/autoscaling/TestPolicy2.java b/solr/solrj/src/test/org/apache/solr/client/solrj/cloud/autoscaling/TestPolicy2.java
index 63b7da4..ce3d8d4 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/cloud/autoscaling/TestPolicy2.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/cloud/autoscaling/TestPolicy2.java
@@ -209,7 +209,7 @@ public class TestPolicy2 extends SolrTestCaseJ4 {
       @Override
       public NodeStateProvider getNodeStateProvider() {
 
-        return new SolrClientNodeStateProvider(null) {
+        return new SolrClientNodeStateProvider(null, null) {
           @Override
           protected ClusterStateProvider getClusterStateProvider() {
             return delegatingClusterStateProvider;
@@ -263,7 +263,7 @@ public class TestPolicy2 extends SolrTestCaseJ4 {
   static SolrCloudManager createCloudManagerFromDiagnostics(Map<String, Object> m) {
     List<Map> sortedNodes = (List<Map>) getObjectByPath(m, false, "diagnostics/sortedNodes");
     Set<String> liveNodes = new HashSet<>();
-    SolrClientNodeStateProvider nodeStateProvider = new SolrClientNodeStateProvider(null) {
+    SolrClientNodeStateProvider nodeStateProvider = new SolrClientNodeStateProvider(null, null) {
       @Override
       protected void readReplicaDetails() {
         for (Object o : sortedNodes) {