package org.apache.solr.handler.component;
/*
* 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.
*/
import java.net.MalformedURLException;
import java.util.Random;
import java.util.concurrent.*;
import org.apache.http.client.HttpClient;
import org.apache.solr.client.solrj.impl.HttpClientUtil;
import org.apache.solr.client.solrj.impl.LBHttpSolrServer;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.util.ExecutorUtil;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.core.PluginInfo;
import org.apache.solr.util.DefaultSolrThreadFactory;
import org.apache.solr.util.plugin.PluginInfoInitialized;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HttpShardHandlerFactory extends ShardHandlerFactory implements PluginInfoInitialized {
protected static Logger log = LoggerFactory.getLogger(HttpShardHandlerFactory.class);
// We want an executor that doesn't take up any resources if
// it's not used, so it could be created statically for
// the distributed search component if desired.
//
// Consider CallerRuns policy and a lower max threads to throttle
// requests at some point (or should we simply return failure?)
ThreadPoolExecutor commExecutor = new ThreadPoolExecutor(
0,
Integer.MAX_VALUE,
5, TimeUnit.SECONDS, // terminate idle threads after 5 sec
new SynchronousQueue<Runnable>(), // directly hand off tasks
new DefaultSolrThreadFactory("httpShardExecutor")
);
private HttpClient defaultClient;
LBHttpSolrServer loadbalancer;
//default values:
int soTimeout = 0;
int connectionTimeout = 0;
int maxConnectionsPerHost = 20;
int corePoolSize = 0;
int maximumPoolSize = Integer.MAX_VALUE;
int keepAliveTime = 5;
int queueSize = -1;
boolean accessPolicy = false;
public String scheme = "http://"; //current default values
final Random r = new Random();
// URL scheme to be used in distributed search.
static final String INIT_URL_SCHEME = "urlScheme";
// The core size of the threadpool servicing requests
static final String INIT_CORE_POOL_SIZE = "corePoolSize";
// The maximum size of the threadpool servicing requests
static final String INIT_MAX_POOL_SIZE = "maximumPoolSize";
// The amount of time idle threads persist for in the queue, before being killed
static final String MAX_THREAD_IDLE_TIME = "maxThreadIdleTime";
// If the threadpool uses a backing queue, what is its maximum size (-1) to use direct handoff
static final String INIT_SIZE_OF_QUEUE = "sizeOfQueue";
// Configure if the threadpool favours fairness over throughput
static final String INIT_FAIRNESS_POLICY = "fairnessPolicy";
/**
* Get {@link ShardHandler} that uses the default http client.
*/
public ShardHandler getShardHandler() {
return getShardHandler(defaultClient);
}
/**
* Get {@link ShardHandler} that uses custom http client.
*/
public ShardHandler getShardHandler(final HttpClient httpClient){
return new HttpShardHandler(this, httpClient);
}
public void init(PluginInfo info) {
NamedList args = info.initArgs;
this.soTimeout = getParameter(args, HttpClientUtil.PROP_SO_TIMEOUT, soTimeout);
this.scheme = getParameter(args, INIT_URL_SCHEME, "http://");
this.scheme = (this.scheme.endsWith("://")) ? this.scheme : this.scheme + "://";
this.connectionTimeout = getParameter(args, HttpClientUtil.PROP_CONNECTION_TIMEOUT, connectionTimeout);
this.maxConnectionsPerHost = getParameter(args, HttpClientUtil.PROP_MAX_CONNECTIONS_PER_HOST, maxConnectionsPerHost);
this.corePoolSize = getParameter(args, INIT_CORE_POOL_SIZE, corePoolSize);
this.maximumPoolSize = getParameter(args, INIT_MAX_POOL_SIZE, maximumPoolSize);
this.keepAliveTime = getParameter(args, MAX_THREAD_IDLE_TIME, keepAliveTime);
this.queueSize = getParameter(args, INIT_SIZE_OF_QUEUE, queueSize);
this.accessPolicy = getParameter(args, INIT_FAIRNESS_POLICY, accessPolicy);
BlockingQueue<Runnable> blockingQueue = (this.queueSize == -1) ?
new SynchronousQueue<Runnable>(this.accessPolicy) :
new ArrayBlockingQueue<Runnable>(this.queueSize, this.accessPolicy);
this.commExecutor = new ThreadPoolExecutor(
this.corePoolSize,
this.maximumPoolSize,
this.keepAliveTime, TimeUnit.SECONDS,
blockingQueue,
new DefaultSolrThreadFactory("httpShardExecutor")
);
ModifiableSolrParams clientParams = new ModifiableSolrParams();
clientParams.set(HttpClientUtil.PROP_MAX_CONNECTIONS_PER_HOST, maxConnectionsPerHost);
clientParams.set(HttpClientUtil.PROP_MAX_CONNECTIONS, 10000);
clientParams.set(HttpClientUtil.PROP_SO_TIMEOUT, soTimeout);
clientParams.set(HttpClientUtil.PROP_CONNECTION_TIMEOUT, connectionTimeout);
clientParams.set(HttpClientUtil.PROP_USE_RETRY, false);
this.defaultClient = HttpClientUtil.createClient(clientParams);
try {
loadbalancer = new LBHttpSolrServer(defaultClient);
} catch (MalformedURLException e) {
// should be impossible since we're not passing any URLs here
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);
}
}
private <T> T getParameter(NamedList initArgs, String configKey, T defaultValue) {
T toReturn = defaultValue;
if (initArgs != null) {
T temp = (T) initArgs.get(configKey);
toReturn = (temp != null) ? temp : defaultValue;
}
log.info("Setting {} to: {}", configKey, toReturn);
return toReturn;
}
@Override
public void close() {
try {
defaultClient.getConnectionManager().shutdown();
} catch (Throwable e) {
SolrException.log(log, e);
}
try {
loadbalancer.shutdown();
} catch (Throwable e) {
SolrException.log(log, e);
}
try {
ExecutorUtil.shutdownNowAndAwaitTermination(commExecutor);
} catch (Throwable e) {
SolrException.log(log, e);
}
}
}