diff --git a/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandlerFactory.java b/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandlerFactory.java index 5b227359701..79e0ad48681 100644 --- a/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandlerFactory.java +++ b/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandlerFactory.java @@ -287,7 +287,7 @@ public void init(PluginInfo info) { .withMaxConnectionsPerHost(maxConnectionsPerHost) .build(); this.defaultClient.addListenerFactory(this.httpListenerFactory); - this.loadbalancer = new LBHttp2SolrClient.Builder(defaultClient).build(); + this.loadbalancer = new LBHttp2SolrClient.Builder(defaultClient, new String[0]).build(); initReplicaListTransformers(getParameter(args, "replicaRouting", null, sb)); 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 5eab2dea39b..6838a575755 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 @@ -83,7 +83,7 @@ protected CloudHttp2SolrClient(Builder builder) { // locks. this.locks = objectList(builder.parallelCacheRefreshesLocks); - this.lbClient = new LBHttp2SolrClient.Builder(myClient).build(); + this.lbClient = new LBHttp2SolrClient.Builder(myClient, new String[0]).build(); } @Override diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBHttp2SolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBHttp2SolrClient.java index 6cfada08541..f971e21fcd7 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBHttp2SolrClient.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBHttp2SolrClient.java @@ -101,11 +101,20 @@ private LBHttp2SolrClient(Builder builder) { this.defaultCollection = builder.defaultCollection; } + /** + * @deprecated Use {@link #getClient(Endpoint)} instead. + */ + @Deprecated @Override protected SolrClient getClient(String baseUrl) { return solrClient; } + @Override + protected SolrClient getClient(Endpoint endpoint) { + return solrClient; + } + /** * Note: This setter method is not thread-safe. * @@ -352,12 +361,55 @@ public static class Builder { * In this case the client is more flexible and can be used to send requests to any cores. Users * can still provide a "default" collection if desired through use of {@link * #withDefaultCollection(String)}. + * + * @deprecated use {@link #Builder(Http2SolrClient, Endpoint...)} instead */ + @Deprecated public Builder(Http2SolrClient http2Client, String... baseSolrUrls) { this.http2SolrClient = http2Client; this.baseSolrUrls = baseSolrUrls; } + /** + * Create a Builder object, based on the provided solrClient and endpoint objects. + * + *
Endpoint instances come in two main flavors: + * + *
1) Endpoints representing a particular core or collection + * + *
+ * SolrClient client = new LBHttp2SolrClient.Builder( + * client, new LBSolrClient.Endpoint("http://my-solr-server:8983/solr", "core1")) + * .build(); + * QueryResponse resp = client.query(new SolrQuery("*:*")); + *+ * + * Note that when a core is provided in the endpoint, queries and other requests can be made + * without mentioning the core explicitly. However, the client can only send requests to that + * core. Attempts to make core-agnostic requests, or requests for other cores will fail. + * + *
2) Endpoints representing the root Solr path (i.e. "/solr") + * + *
+ * SolrClient client = new LBHttp2SolrClient.Builder( + * client, new LBSolrClient.Endpoint("http://my-solr-server:8983/solr")) + * .build(); + * QueryResponse resp = client.query("core1", new SolrQuery("*:*")); + *+ * + * In this case the client is more flexible and can be used to send requests to any cores. Users + * can still provide a "default" collection if desired through use of {@link + * #withDefaultCollection(String)}. + */ + public Builder(Http2SolrClient http2Client, Endpoint... endpoints) { + this.http2SolrClient = http2Client; + + this.baseSolrUrls = new String[endpoints.length]; + for (int i = 0; i < endpoints.length; i++) { + this.baseSolrUrls[i] = endpoints[i].getUrl(); + } + } + /** * LBHttpSolrServer keeps pinging the dead servers at fixed interval to find if it is alive. Use * this to set that interval diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBHttpSolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBHttpSolrClient.java index b595701aec5..dd86f170669 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBHttpSolrClient.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBHttpSolrClient.java @@ -25,6 +25,7 @@ import org.apache.http.client.HttpClient; import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.util.URLUtil; /** * LBHttpSolrClient or "LoadBalanced HttpSolrClient" is a load balancing wrapper around {@link @@ -117,6 +118,14 @@ private HttpClient constructClient(String[] solrServerUrl) { return HttpClientUtil.createClient(params); } + protected HttpSolrClient makeSolrClient(Endpoint endpoint) { + return makeSolrClient(endpoint.getUrl()); + } + + /** + * @deprecated use {@link #makeSolrClient(Endpoint)} instead + */ + @Deprecated protected HttpSolrClient makeSolrClient(String server) { HttpSolrClient client; if (httpSolrClientBuilder != null) { @@ -159,6 +168,10 @@ protected HttpSolrClient makeSolrClient(String server) { return client; } + /** + * @deprecated use {@link #getClient(Endpoint)} instead + */ + @Deprecated @Override protected SolrClient getClient(String baseUrl) { SolrClient client = urlToClient.get(baseUrl); @@ -169,12 +182,26 @@ protected SolrClient getClient(String baseUrl) { } } + @Override + protected SolrClient getClient(Endpoint endpoint) { + return getClient(endpoint.getUrl()); + } + + /** + * @deprecated use {@link #removeSolrEndpoint(Endpoint)} instead + */ + @Deprecated @Override public String removeSolrServer(String server) { urlToClient.remove(server); return super.removeSolrServer(server); } + @Override + public String removeSolrEndpoint(Endpoint endpoint) { + return removeSolrServer(endpoint.getUrl()); + } + @Override public void close() { super.close(); @@ -256,12 +283,91 @@ public HttpSolrClient.Builder getHttpSolrClientBuilder() { * * In this case the client is more flexible and can be used to send requests to any cores. This * flexibility though requires that the core is specified on all requests. + * + * @deprecated use {@link #withBaseEndpoint(String)} or {@link #withCollectionEndpoint(String, + * String)} instead, based on the type of URL string currently being supplied */ + @Deprecated public Builder withBaseSolrUrl(String baseSolrUrl) { this.baseSolrUrls.add(baseSolrUrl); return this; } + /** + * Provide a "base" Solr URL to be used when configuring {@link LBHttpSolrClient} instances. + * + *
Method may be called multiple times. All provided values will be used. However, all + * endpoints must be of the same type: providing a mix of "base" endpoints via this method and + * core/collection endpoints via {@link #withCollectionEndpoint(String, String)} is prohibited. + * + *
Users who use this method to provide base Solr URLs may specify a "default collection" for + * their requests using {@link #withDefaultCollection(String)} if they wish to avoid needing to + * specify a collection or core on relevant requests. + * + * @param rootUrl the base URL for a Solr node, in the form "http[s]://hostname:port/solr" + */ + public Builder withBaseEndpoint(String rootUrl) { + this.baseSolrUrls.add(rootUrl); + return this; + } + + /** + * Provide multiple "base" Solr URLs to be used when configuring {@link LBHttpSolrClient} + * instances. + * + *
Method may be called multiple times. All provided values will be used. However, all + * endpoints must be of the same type: providing a mix of"base" endpoints via this method and + * core/collection endpoints via {@link #withCollectionEndpoint(String, String)} is prohibited. + * + *
Users who use this method to provide base Solr URLs may specify a "default collection" for + * their requests using {@link #withDefaultCollection(String)} if they wish to avoid needing to + * specify a collection or core on relevant requests. + * + * @param baseSolrUrls Solr base URLs, in the form "http[s]://hostname:port/solr" + */ + public Builder withBaseEndpoints(String... baseSolrUrls) { + for (String baseSolrUrl : baseSolrUrls) { + this.baseSolrUrls.add(baseSolrUrl); + } + return this; + } + + /** + * Provide a core/collection Solr endpoint to be used when configuring {@link LBHttpSolrClient} + * instances. + * + *
Method may be called multiple times. All provided values will be used. However, all + * endpoints must be of the same type: providing a mix of "core" endpoints via this method and + * base endpoints via {@link #withBaseEndpoint(String)} is prohibited. + * + * @param rootUrl the base URL for a Solr node, in the form "http[s]://hostname:port/solr" + * @param collection the Solr core or collection to target + */ + public Builder withCollectionEndpoint(String rootUrl, String collection) { + this.baseSolrUrls.add(URLUtil.buildCoreUrl(rootUrl, collection)); + return this; + } + + /** + * Provide multiple core/collection endpoints to be used when configuring {@link + * LBHttpSolrClient} instances. + * + *
Method may be called multiple times. All provided values will be used. However, all + * endpoints must be of the same type: providing a mix of "core" endpoints via this method and + * base endpoints via {@link #withBaseEndpoint(String)} is prohibited. + * + * @param endpoints endpoint instances pointing to distinct cores/collections + */ + public Builder withCollectionEndpoints(Endpoint... endpoints) { + if (endpoints != null) { + for (Endpoint e : endpoints) { + this.baseSolrUrls.add(URLUtil.buildCoreUrl(e.getBaseUrl(), e.getCore())); + } + } + + return this; + } + /** * Provide Solr endpoints to be used when configuring {@link LBHttpSolrClient} instances. * @@ -294,7 +400,11 @@ public Builder withBaseSolrUrl(String baseSolrUrl) { * In this case the client is more flexible and can be used to send requests to any cores. Users * can still provide a "default" collection if desired through use of {@link * #withDefaultCollection(String)}. + * + * @deprecated use either {@link #withBaseEndpoints(String...)} or {@link + * #withCollectionEndpoints(Endpoint...)}, based on the type of URL strings currently used. */ + @Deprecated public Builder withBaseSolrUrls(String... solrUrls) { for (String baseSolrUrl : solrUrls) { this.baseSolrUrls.add(baseSolrUrl); 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 9202768bcf6..87e203b1cab 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 @@ -28,18 +28,21 @@ import java.net.URL; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; import org.apache.solr.client.solrj.ResponseParser; import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.SolrQuery; @@ -100,6 +103,82 @@ public abstract class LBSolrClient extends SolrClient { solrQuery.setDistrib(false); } + /** + * A Solr endpoint for {@link LBSolrClient} to include in its load-balancing + * + *
Used in many places instead of the more common String URL to allow {@link LBSolrClient} to
+ * more easily determine whether a URL is a "base" or "core-aware" URL.
+ */
+ public static class Endpoint {
+ private final String baseUrl;
+ private final String core;
+
+ /**
+ * Creates an {@link Endpoint} representing a "base" URL of a Solr node
+ *
+ * @param baseUrl a base Solr URL, in the form "http[s]://host:port/solr"
+ */
+ public Endpoint(String baseUrl) {
+ this(baseUrl, null);
+ }
+
+ /**
+ * Create an {@link Endpoint} representing a Solr core or collection
+ *
+ * @param baseUrl a base Solr URL, in the form "http[s]://host:port/solr"
+ * @param core the name of a Solr core or collection
+ */
+ public Endpoint(String baseUrl, String core) {
+ this.baseUrl = normalize(baseUrl);
+ this.core = core;
+ }
+
+ /**
+ * Return the base URL of the Solr node this endpoint represents
+ *
+ * @return a base Solr URL, in the form "http[s]://host:port/solr"
+ */
+ public String getBaseUrl() {
+ return baseUrl;
+ }
+
+ /**
+ * The core or collection this endpoint represents
+ *
+ * @return a core/collection name, or null if this endpoint doesn't represent a particular core.
+ */
+ public String getCore() {
+ return core;
+ }
+
+ /** Get the full URL, possibly including the collection/core if one was provided */
+ public String getUrl() {
+ if (core == null) {
+ return baseUrl;
+ }
+ return baseUrl + "/" + core;
+ }
+
+ @Override
+ public String toString() {
+ return getUrl();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(baseUrl, core);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (!(obj instanceof Endpoint)) return false;
+ final Endpoint rhs = (Endpoint) obj;
+
+ return Objects.equals(baseUrl, rhs.baseUrl) && Objects.equals(core, rhs.core);
+ }
+ }
+
protected static class ServerWrapper {
final String baseUrl;
@@ -245,10 +324,24 @@ public static class Req {
protected int numDeadServersToTry;
private final Integer numServersToTry;
+ /**
+ * @deprecated use {@link #Req(SolrRequest, Collection)} instead
+ */
+ @Deprecated
public Req(SolrRequest> request, List