org.apache.solr.client.solrj.impl.LBHttpSolrClient.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.solr.client.solrj.impl.LBHttpSolrClient.java

Source

/*
 * 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.List;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.http.client.HttpClient;
import org.apache.solr.client.solrj.ResponseParser;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.common.params.ModifiableSolrParams;

/**
 * LBHttpSolrClient or "LoadBalanced HttpSolrClient" is a load balancing wrapper around
 * {@link HttpSolrClient}. This is useful when you
 * have multiple Solr servers and the requests need to be Load Balanced among them.
 *
 * Do <b>NOT</b> use this class for indexing in master/slave scenarios since documents must be sent to the
 * correct master; no inter-node routing is done.
 *
 * In SolrCloud (leader/replica) scenarios, it is usually better to use
 * {@link CloudSolrClient}, but this class may be used
 * for updates because the server will forward them to the appropriate leader.
 *
 * <p>
 * It offers automatic failover when a server goes down and it detects when the server comes back up.
 * <p>
 * Load balancing is done using a simple round-robin on the list of servers.
 * <p>
 * If a request to a server fails by an IOException due to a connection timeout or read timeout then the host is taken
 * off the list of live servers and moved to a 'dead server list' and the request is resent to the next live server.
 * This process is continued till it tries all the live servers. If at least one server is alive, the request succeeds,
 * and if not it fails.
 * <blockquote><pre>
 * SolrClient lbHttpSolrClient = new LBHttpSolrClient("http://host1:8080/solr/", "http://host2:8080/solr", "http://host2:8080/solr");
 * //or if you wish to pass the HttpClient do as follows
 * httpClient httpClient = new HttpClient();
 * SolrClient lbHttpSolrClient = new LBHttpSolrClient(httpClient, "http://host1:8080/solr/", "http://host2:8080/solr", "http://host2:8080/solr");
 * </pre></blockquote>
 * This detects if a dead server comes alive automatically. The check is done in fixed intervals in a dedicated thread.
 * This interval can be set using {@link #setAliveCheckInterval} , the default is set to one minute.
 * <p>
 * <b>When to use this?</b><br> This can be used as a software load balancer when you do not wish to setup an external
 * load balancer. Alternatives to this code are to use
 * a dedicated hardware load balancer or using Apache httpd with mod_proxy_balancer as a load balancer. See <a
 * href="http://en.wikipedia.org/wiki/Load_balancing_(computing)">Load balancing on Wikipedia</a>
 *
 * @since solr 1.4
 */
public class LBHttpSolrClient extends LBSolrClient {

    private final HttpClient httpClient;
    private final boolean clientIsInternal;
    private final ConcurrentHashMap<String, HttpSolrClient> urlToClient = new ConcurrentHashMap<>();
    private final HttpSolrClient.Builder httpSolrClientBuilder;

    private Integer connectionTimeout;
    private volatile Integer soTimeout;

    /**
     * @deprecated use {@link LBSolrClient.Req} instead
     */
    @Deprecated
    public static class Req extends LBSolrClient.Req {
        public Req(SolrRequest request, List<String> servers) {
            super(request, servers);
        }

        public Req(SolrRequest request, List<String> servers, Integer numServersToTry) {
            super(request, servers, numServersToTry);
        }
    }

    /**
     * @deprecated use {@link LBSolrClient.Rsp} instead
     */
    @Deprecated
    public static class Rsp extends LBSolrClient.Rsp {

    }

    /**
     * The provided httpClient should use a multi-threaded connection manager
     *
     * @deprecated use {@link LBHttpSolrClient#LBHttpSolrClient(Builder)} instead, as it is a more extension/subclassing-friendly alternative
     */
    @Deprecated
    protected LBHttpSolrClient(HttpSolrClient.Builder httpSolrClientBuilder, HttpClient httpClient,
            String... solrServerUrl) {
        this(new Builder().withHttpSolrClientBuilder(httpSolrClientBuilder).withHttpClient(httpClient)
                .withBaseSolrUrls(solrServerUrl));
    }

    /**
     * The provided httpClient should use a multi-threaded connection manager
     *
     * @deprecated use {@link LBHttpSolrClient#LBHttpSolrClient(Builder)} instead, as it is a more extension/subclassing-friendly alternative
     */
    @Deprecated
    protected LBHttpSolrClient(HttpClient httpClient, ResponseParser parser, String... solrServerUrl) {
        this(new Builder().withBaseSolrUrls(solrServerUrl).withResponseParser(parser).withHttpClient(httpClient));
    }

    protected LBHttpSolrClient(Builder builder) {
        super(builder.baseSolrUrls);
        this.clientIsInternal = builder.httpClient == null;
        this.httpSolrClientBuilder = builder.httpSolrClientBuilder;
        this.httpClient = builder.httpClient == null
                ? constructClient(builder.baseSolrUrls.toArray(new String[builder.baseSolrUrls.size()]))
                : builder.httpClient;
        this.connectionTimeout = builder.connectionTimeoutMillis;
        this.soTimeout = builder.socketTimeoutMillis;
        this.parser = builder.responseParser;
        for (String baseUrl : builder.baseSolrUrls) {
            urlToClient.put(baseUrl, makeSolrClient(baseUrl));
        }
    }

    private HttpClient constructClient(String[] solrServerUrl) {
        ModifiableSolrParams params = new ModifiableSolrParams();
        if (solrServerUrl != null && solrServerUrl.length > 1) {
            // we prefer retrying another server
            params.set(HttpClientUtil.PROP_USE_RETRY, false);
        } else {
            params.set(HttpClientUtil.PROP_USE_RETRY, true);
        }
        return HttpClientUtil.createClient(params);
    }

    protected HttpSolrClient makeSolrClient(String server) {
        HttpSolrClient client;
        if (httpSolrClientBuilder != null) {
            synchronized (this) {
                httpSolrClientBuilder.withBaseSolrUrl(server).withHttpClient(httpClient);
                if (connectionTimeout != null) {
                    httpSolrClientBuilder.withConnectionTimeout(connectionTimeout);
                }
                if (soTimeout != null) {
                    httpSolrClientBuilder.withSocketTimeout(soTimeout);
                }
                client = httpSolrClientBuilder.build();
            }
        } else {
            final HttpSolrClient.Builder clientBuilder = new HttpSolrClient.Builder(server)
                    .withHttpClient(httpClient).withResponseParser(parser);
            if (connectionTimeout != null) {
                clientBuilder.withConnectionTimeout(connectionTimeout);
            }
            if (soTimeout != null) {
                clientBuilder.withSocketTimeout(soTimeout);
            }
            client = clientBuilder.build();
        }
        if (requestWriter != null) {
            client.setRequestWriter(requestWriter);
        }
        if (queryParams != null) {
            client.setQueryParams(queryParams);
        }
        return client;
    }

    /**
     * @deprecated since 7.0  Use {@link Builder} methods instead. 
     */
    @Deprecated
    public void setConnectionTimeout(int timeout) {
        this.connectionTimeout = timeout;
        this.urlToClient.values().forEach(client -> client.setConnectionTimeout(timeout));
    }

    /**
     * set soTimeout (read timeout) on the underlying HttpConnectionManager. This is desirable for queries, but probably
     * not for indexing.
     *
     * @deprecated since 7.0  Use {@link Builder} methods instead. 
     */
    @Deprecated
    public void setSoTimeout(int timeout) {
        this.soTimeout = timeout;
        this.urlToClient.values().forEach(client -> client.setSoTimeout(timeout));
    }

    /**
     * @deprecated use {@link LBSolrClient#request(LBSolrClient.Req)} instead
     */
    @Deprecated
    public Rsp request(Req req) throws SolrServerException, IOException {
        LBSolrClient.Rsp rsp = super.request(req);
        // for backward-compatibility support
        Rsp result = new Rsp();
        result.rsp = rsp.rsp;
        result.server = rsp.server;
        return result;
    }

    @Override
    protected SolrClient getClient(String baseUrl) {
        HttpSolrClient client = urlToClient.get(baseUrl);
        if (client == null) {
            return makeSolrClient(baseUrl);
        } else {
            return client;
        }
    }

    @Override
    public String removeSolrServer(String server) {
        urlToClient.remove(server);
        return super.removeSolrServer(server);
    }

    @Override
    public void close() {
        super.close();
        if (clientIsInternal) {
            HttpClientUtil.close(httpClient);
        }
    }

    /**
     * Return the HttpClient this instance uses.
     */
    public HttpClient getHttpClient() {
        return httpClient;
    }

    /**
     * Constructs {@link LBHttpSolrClient} instances from provided configuration.
     */
    public static class Builder extends SolrClientBuilder<Builder> {
        protected final List<String> baseSolrUrls;
        protected HttpSolrClient.Builder httpSolrClientBuilder;

        public Builder() {
            this.baseSolrUrls = new ArrayList<>();
            this.responseParser = new BinaryResponseParser();
        }

        public HttpSolrClient.Builder getHttpSolrClientBuilder() {
            return httpSolrClientBuilder;
        }

        /**
         * Provide a Solr endpoint to be used when configuring {@link LBHttpSolrClient} instances.
         * 
         * Method may be called multiple times.  All provided values will be used.
         * 
         * Two different paths can be specified as a part of the URL:
         * 
         * 1) A path pointing directly at a particular core
         * <pre>
         *   SolrClient client = builder.withBaseSolrUrl("http://my-solr-server:8983/solr/core1").build();
         *   QueryResponse resp = client.query(new SolrQuery("*:*"));
         * </pre>
         * Note that when a core is provided in the base URL, queries and other requests can be made without mentioning the
         * core explicitly.  However, the client can only send requests to that core.
         * 
         * 2) The path of the root Solr path ("/solr")
         * <pre>
         *   SolrClient client = builder.withBaseSolrUrl("http://my-solr-server:8983/solr").build();
         *   QueryResponse resp = client.query("core1", new SolrQuery("*:*"));
         * </pre>
         * 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.
         */
        public Builder withBaseSolrUrl(String baseSolrUrl) {
            this.baseSolrUrls.add(baseSolrUrl);
            return this;
        }

        /**
         * Provide Solr endpoints to be used when configuring {@link LBHttpSolrClient} instances.
         * 
         * Method may be called multiple times.  All provided values will be used.
         * 
         * Two different paths can be specified as a part of each URL:
         * 
         * 1) A path pointing directly at a particular core
         * <pre>
         *   SolrClient client = builder.withBaseSolrUrls("http://my-solr-server:8983/solr/core1").build();
         *   QueryResponse resp = client.query(new SolrQuery("*:*"));
         * </pre>
         * Note that when a core is provided in the base URL, queries and other requests can be made without mentioning the
         * core explicitly.  However, the client can only send requests to that core.
         * 
         * 2) The path of the root Solr path ("/solr")
         * <pre>
         *   SolrClient client = builder.withBaseSolrUrls("http://my-solr-server:8983/solr").build();
         *   QueryResponse resp = client.query("core1", new SolrQuery("*:*"));
         * </pre>
         * 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.
         */
        public Builder withBaseSolrUrls(String... solrUrls) {
            for (String baseSolrUrl : solrUrls) {
                this.baseSolrUrls.add(baseSolrUrl);
            }
            return this;
        }

        /**
         * Provides a {@link HttpSolrClient.Builder} to be used for building the internally used clients.
         */
        public Builder withHttpSolrClientBuilder(HttpSolrClient.Builder builder) {
            this.httpSolrClientBuilder = builder;
            return this;
        }

        /**
         * Create a {@link HttpSolrClient} based on provided configuration.
         */
        public LBHttpSolrClient build() {
            return new LBHttpSolrClient(this);
        }

        @Override
        public Builder getThis() {
            return this;
        }
    }
}