All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.solr.client.solrj.impl.CloudHttp2SolrClient Maven / Gradle / Ivy

There is a newer version: 9.7.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.solr.client.solrj.impl;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;

import org.apache.solr.client.solrj.request.UpdateRequest;
import org.apache.solr.common.SolrException;

/**
 * SolrJ client class to communicate with SolrCloud using Http2SolrClient.
 * Instances of this class communicate with Zookeeper to discover
 * Solr endpoints for SolrCloud collections, and then use the
 * {@link LBHttp2SolrClient} to issue requests.
 *
 * This class assumes the id field for your documents is called
 * 'id' - if this is not the case, you must set the right name
 * with {@link #setIdField(String)}.
 *
 * @lucene.experimental
 * @since solr 8.0
 */
@SuppressWarnings("serial")
public class CloudHttp2SolrClient  extends BaseCloudSolrClient {

  private final ClusterStateProvider stateProvider;
  private final LBHttp2SolrClient lbClient;
  private final Http2SolrClient myClient;
  private final boolean clientIsInternal;

  /**
   * Create a new client object that connects to Zookeeper and is always aware
   * of the SolrCloud state. If there is a fully redundant Zookeeper quorum and
   * SolrCloud has enough replicas for every shard in a collection, there is no
   * single point of failure. Updates will be sent to shard leaders by default.
   *
   * @param builder a {@link Http2SolrClient.Builder} with the options used to create the client.
   */
  protected CloudHttp2SolrClient(Builder builder) {
    super(builder.shardLeadersOnly, builder.parallelUpdates, builder.directUpdatesToLeadersOnly);
    if (builder.httpClient == null) {
      this.clientIsInternal = true;
      if (builder.internalClientBuilder == null) {
        this.myClient = new Http2SolrClient.Builder().build();
      } else {
        this.myClient = builder.internalClientBuilder.build();
      }
    } else {
      this.clientIsInternal = false;
      this.myClient = builder.httpClient;
    }
    if (builder.stateProvider == null) {
      if (builder.zkHosts != null && builder.solrUrls != null) {
        throw new IllegalArgumentException("Both zkHost(s) & solrUrl(s) have been specified. Only specify one.");
      }
      if (builder.zkHosts != null) {
        this.stateProvider = new ZkClientClusterStateProvider(builder.zkHosts, builder.zkChroot, builder.canUseZkACLs);
      } else if (builder.solrUrls != null && !builder.solrUrls.isEmpty()) {
        try {
          this.stateProvider = new Http2ClusterStateProvider(builder.solrUrls, builder.httpClient);
        } catch (Exception e) {
          throw new RuntimeException("Couldn't initialize a HttpClusterStateProvider (is/are the "
              + "Solr server(s), "  + builder.solrUrls + ", down?)", e);
        }
      } else {
        throw new IllegalArgumentException("Both zkHosts and solrUrl cannot be null.");
      }
    } else {
      this.stateProvider = builder.stateProvider;
    }
    this.lbClient = new LBHttp2SolrClient(myClient);

  }


  @Override
  public void close() throws IOException {
    stateProvider.close();
    lbClient.close();

    if (clientIsInternal && myClient!=null) {
      myClient.close();
    }

    super.close();
  }

  public LBHttp2SolrClient getLbClient() {
    return lbClient;
  }

  @Override
  public ClusterStateProvider getClusterStateProvider() {
    return stateProvider;
  }

  public Http2SolrClient getHttpClient() {
    return myClient;
  }

  @Override
  protected boolean wasCommError(Throwable rootCause) {
    return false;
  }

  /**
   * Constructs {@link CloudHttp2SolrClient} instances from provided configuration.
   */
  public static class Builder {
    protected Collection zkHosts = new ArrayList<>();
    protected List solrUrls = new ArrayList<>();
    protected String zkChroot;
    protected Http2SolrClient httpClient;
    protected boolean shardLeadersOnly = true;
    protected boolean directUpdatesToLeadersOnly = false;
    protected boolean parallelUpdates = true;
    protected ClusterStateProvider stateProvider;
    protected Http2SolrClient.Builder internalClientBuilder;
    private boolean canUseZkACLs = true;

    /**
     * Provide a series of Solr URLs to be used when configuring {@link CloudHttp2SolrClient} instances.
     * The solr client will use these urls to understand the cluster topology, which solr nodes are active etc.
     *
     * Provided Solr URLs are expected to point to the root Solr path ("http://hostname:8983/solr"); they should not
     * include any collections, cores, or other path components.
     *
     * Usage example:
     *
     * 
     *   final List<String> solrBaseUrls = new ArrayList<String>();
     *   solrBaseUrls.add("http://solr1:8983/solr"); solrBaseUrls.add("http://solr2:8983/solr"); solrBaseUrls.add("http://solr3:8983/solr");
     *   final SolrClient client = new CloudHttp2SolrClient.Builder(solrBaseUrls).build();
     * 
*/ public Builder(List solrUrls) { this.solrUrls = solrUrls; } /** * Provide a series of ZK hosts which will be used when configuring {@link CloudHttp2SolrClient} instances. * * Usage example when Solr stores data at the ZooKeeper root ('/'): * *
     *   final List<String> zkServers = new ArrayList<String>();
     *   zkServers.add("zookeeper1:2181"); zkServers.add("zookeeper2:2181"); zkServers.add("zookeeper3:2181");
     *   final SolrClient client = new CloudHttp2SolrClient.Builder(zkServers, Optional.empty()).build();
     * 
* * Usage example when Solr data is stored in a ZooKeeper chroot: * *
     *    final List<String> zkServers = new ArrayList<String>();
     *    zkServers.add("zookeeper1:2181"); zkServers.add("zookeeper2:2181"); zkServers.add("zookeeper3:2181");
     *    final SolrClient client = new CloudHttp2SolrClient.Builder(zkServers, Optional.of("/solr")).build();
     *  
* * @param zkHosts a List of at least one ZooKeeper host and port (e.g. "zookeeper1:2181") * @param zkChroot the path to the root ZooKeeper node containing Solr data. Provide {@code java.util.Optional.empty()} if no ZK chroot is used. */ public Builder(List zkHosts, Optional zkChroot) { this.zkHosts = zkHosts; if (zkChroot.isPresent()) this.zkChroot = zkChroot.get(); } /** Whether or not to use the default ZK ACLs when building a ZK Client. */ public Builder canUseZkACLs(boolean canUseZkACLs) { this.canUseZkACLs = canUseZkACLs; return this; } /** * Tells {@link CloudHttp2SolrClient.Builder} that created clients should send direct updates to shard leaders only. * * UpdateRequests whose leaders cannot be found will "fail fast" on the client side with a {@link SolrException} */ public Builder sendDirectUpdatesToShardLeadersOnly() { directUpdatesToLeadersOnly = true; return this; } /** * Tells {@link CloudHttp2SolrClient.Builder} that created clients can send updates to any shard replica (shard leaders and non-leaders). * * Shard leaders are still preferred, but the created clients will fallback to using other replicas if a leader * cannot be found. */ public Builder sendDirectUpdatesToAnyShardReplica() { directUpdatesToLeadersOnly = false; return this; } /** * Tells {@link CloudHttp2SolrClient.Builder} whether created clients should send shard updates serially or in parallel * * When an {@link UpdateRequest} affects multiple shards, {@link CloudHttp2SolrClient} splits it up and sends a request * to each affected shard. This setting chooses whether those sub-requests are sent serially or in parallel. *

* If not set, this defaults to 'true' and sends sub-requests in parallel. */ public Builder withParallelUpdates(boolean parallelUpdates) { this.parallelUpdates = parallelUpdates; return this; } public Builder withHttpClient(Http2SolrClient httpClient) { if (this.internalClientBuilder != null) { throw new IllegalStateException("The builder can't accept an httpClient AND an internalClientBuilder, only one of those can be provided"); } this.httpClient = httpClient; return this; } /** * If provided, the CloudHttp2SolrClient will build it's internal Http2SolrClient using this builder * (instead of the empty default one). Providing this builder allows users to configure the internal * clients (authentication, timeouts, etc). * @param internalClientBuilder the builder to use for creating the internal http client. * @return this */ public Builder withInternalClientBuilder(Http2SolrClient.Builder internalClientBuilder) { if (this.httpClient != null) { throw new IllegalStateException("The builder can't accept an httpClient AND an internalClientBuilder, only one of those can be provided"); } this.internalClientBuilder = internalClientBuilder; return this; } /** * Create a {@link CloudHttp2SolrClient} based on the provided configuration. */ public CloudHttp2SolrClient build() { if (stateProvider == null) { if (!zkHosts.isEmpty()) { stateProvider = new ZkClientClusterStateProvider(zkHosts, zkChroot, canUseZkACLs); } else if (!this.solrUrls.isEmpty()) { try { stateProvider = new Http2ClusterStateProvider(solrUrls, httpClient); } catch (Exception e) { throw new RuntimeException("Couldn't initialize a HttpClusterStateProvider (is/are the " + "Solr server(s), " + solrUrls + ", down?)", e); } } else { throw new IllegalArgumentException("Both zkHosts and solrUrl cannot be null."); } } return new CloudHttp2SolrClient(this); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy