Java tutorial
/* * 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; } } }