SOLR-9948: Add a way to configure granularity of metrics for http connections

This commit is contained in:
Shalin Shekhar Mangar 2017-01-12 13:00:00 +05:30
parent 7435ab1878
commit d2664b1004
8 changed files with 143 additions and 25 deletions

View File

@ -209,7 +209,9 @@ New Features
* SOLR-9725: Substitute properties into JdbcDataSource configuration ( Jamie Jackson, Yuri Sashevsky via Mikhail Khludnev)
* SOLR-9877: SOLR-9923: Use instrumented http client and connection pool. (shalin)
* SOLR-9877: SOLR-9923: SOLR-9948: Use instrumented http client and connection pool in HttpShardHandler and
UpdateShardHandler. The metrics are collected per query-less URL and method by default but it can be configured
to host/method and per-method as well. (shalin)
* SOLR-9880: Add Ganglia, Graphite and SLF4J metrics reporters. (ab)

View File

@ -282,6 +282,7 @@ public class SolrXmlConfig {
int maxUpdateConnectionsPerHost = UpdateShardHandlerConfig.DEFAULT_MAXUPDATECONNECTIONSPERHOST;
int distributedSocketTimeout = UpdateShardHandlerConfig.DEFAULT_DISTRIBUPDATESOTIMEOUT;
int distributedConnectionTimeout = UpdateShardHandlerConfig.DEFAULT_DISTRIBUPDATECONNTIMEOUT;
String metricNameStrategy = UpdateShardHandlerConfig.DEFAULT_METRICNAMESTRATEGY;
Object muc = nl.remove("maxUpdateConnections");
if (muc != null) {
@ -307,10 +308,16 @@ public class SolrXmlConfig {
defined = true;
}
Object mns = nl.remove("metricNameStrategy");
if (mns != null) {
metricNameStrategy = mns.toString();
defined = true;
}
if (!defined && !alwaysDefine)
return null;
return new UpdateShardHandlerConfig(maxUpdateConnections, maxUpdateConnectionsPerHost, distributedSocketTimeout, distributedConnectionTimeout);
return new UpdateShardHandlerConfig(maxUpdateConnections, maxUpdateConnectionsPerHost, distributedSocketTimeout, distributedConnectionTimeout, metricNameStrategy);
}

View File

@ -25,6 +25,7 @@ import org.apache.solr.client.solrj.impl.LBHttpSolrClient;
import org.apache.solr.client.solrj.impl.LBHttpSolrClient.Builder;
import org.apache.solr.client.solrj.request.QueryRequest;
import org.apache.solr.cloud.ZkController;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.cloud.Replica;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.ModifiableSolrParams;
@ -41,6 +42,7 @@ import org.apache.solr.metrics.SolrMetricProducer;
import org.apache.solr.update.UpdateShardHandlerConfig;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.util.DefaultSolrThreadFactory;
import org.apache.solr.util.stats.HttpClientMetricNameStrategy;
import org.apache.solr.util.stats.InstrumentedHttpRequestExecutor;
import org.apache.solr.util.stats.InstrumentedPoolingHttpClientConnectionManager;
import org.apache.solr.util.stats.MetricUtils;
@ -61,6 +63,8 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
import static org.apache.solr.util.stats.InstrumentedHttpRequestExecutor.KNOWN_METRIC_NAME_STRATEGIES;
public class HttpShardHandlerFactory extends ShardHandlerFactory implements org.apache.solr.util.plugin.PluginInfoInitialized, SolrMetricProducer {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@ -97,6 +101,8 @@ public class HttpShardHandlerFactory extends ShardHandlerFactory implements org.
private String scheme = null;
private HttpClientMetricNameStrategy metricNameStrategy;
private final Random r = new Random();
private final ReplicaListTransformer shufflingReplicaListTransformer = new ShufflingReplicaListTransformer(r);
@ -148,6 +154,13 @@ public class HttpShardHandlerFactory extends ShardHandlerFactory implements org.
this.scheme = StringUtils.removeEnd(this.scheme, "://");
}
String strategy = getParameter(args, "metricNameStrategy", UpdateShardHandlerConfig.DEFAULT_METRICNAMESTRATEGY, sb);
this.metricNameStrategy = KNOWN_METRIC_NAME_STRATEGIES.get(strategy);
if (this.metricNameStrategy == null) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
"Unknown metricNameStrategy: " + strategy + " found. Must be one of: " + KNOWN_METRIC_NAME_STRATEGIES.keySet());
}
this.connectionTimeout = getParameter(args, HttpClientUtil.PROP_CONNECTION_TIMEOUT, connectionTimeout, sb);
this.maxConnectionsPerHost = getParameter(args, HttpClientUtil.PROP_MAX_CONNECTIONS_PER_HOST, maxConnectionsPerHost,sb);
this.maxConnections = getParameter(args, HttpClientUtil.PROP_MAX_CONNECTIONS, maxConnections,sb);
@ -177,7 +190,7 @@ public class HttpShardHandlerFactory extends ShardHandlerFactory implements org.
);
ModifiableSolrParams clientParams = getClientParams();
httpRequestExecutor = new InstrumentedHttpRequestExecutor();
httpRequestExecutor = new InstrumentedHttpRequestExecutor(this.metricNameStrategy);
clientConnectionManager = new InstrumentedPoolingHttpClientConnectionManager(HttpClientUtil.getSchemaRegisteryProvider().getSchemaRegistry());
this.defaultClient = HttpClientUtil.createClient(clientParams, clientConnectionManager, false, httpRequestExecutor);
this.loadbalancer = createLoadbalancer(defaultClient);

View File

@ -34,11 +34,14 @@ import org.apache.solr.common.util.SolrjNamedThreadFactory;
import org.apache.solr.core.SolrInfoMBean;
import org.apache.solr.metrics.SolrMetricManager;
import org.apache.solr.metrics.SolrMetricProducer;
import org.apache.solr.util.stats.HttpClientMetricNameStrategy;
import org.apache.solr.util.stats.InstrumentedHttpRequestExecutor;
import org.apache.solr.util.stats.InstrumentedPoolingHttpClientConnectionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.apache.solr.util.stats.InstrumentedHttpRequestExecutor.KNOWN_METRIC_NAME_STRATEGIES;
public class UpdateShardHandler implements SolrMetricProducer, SolrInfoMBean {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@ -74,7 +77,16 @@ public class UpdateShardHandler implements SolrMetricProducer, SolrInfoMBean {
clientParams.set(HttpClientUtil.PROP_SO_TIMEOUT, cfg.getDistributedSocketTimeout());
clientParams.set(HttpClientUtil.PROP_CONNECTION_TIMEOUT, cfg.getDistributedConnectionTimeout());
}
httpRequestExecutor = new InstrumentedHttpRequestExecutor();
HttpClientMetricNameStrategy metricNameStrategy = KNOWN_METRIC_NAME_STRATEGIES.get(UpdateShardHandlerConfig.DEFAULT_METRICNAMESTRATEGY);
if (cfg != null) {
metricNameStrategy = KNOWN_METRIC_NAME_STRATEGIES.get(cfg.getMetricNameStrategy());
if (metricNameStrategy == null) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
"Unknown metricNameStrategy: " + cfg.getMetricNameStrategy() + " found. Must be one of: " + KNOWN_METRIC_NAME_STRATEGIES.keySet());
}
}
httpRequestExecutor = new InstrumentedHttpRequestExecutor(metricNameStrategy);
client = HttpClientUtil.createClient(clientParams, clientConnectionManager, false, httpRequestExecutor);
// following is done only for logging complete configuration.

View File

@ -22,10 +22,12 @@ public class UpdateShardHandlerConfig {
public static final int DEFAULT_DISTRIBUPDATESOTIMEOUT = 600000;
public static final int DEFAULT_MAXUPDATECONNECTIONS = 100000;
public static final int DEFAULT_MAXUPDATECONNECTIONSPERHOST = 100000;
public static final String DEFAULT_METRICNAMESTRATEGY = "queryLessURLAndMethod";
public static final UpdateShardHandlerConfig DEFAULT
= new UpdateShardHandlerConfig(DEFAULT_MAXUPDATECONNECTIONS, DEFAULT_MAXUPDATECONNECTIONSPERHOST,
DEFAULT_DISTRIBUPDATESOTIMEOUT, DEFAULT_DISTRIBUPDATECONNTIMEOUT);
DEFAULT_DISTRIBUPDATESOTIMEOUT, DEFAULT_DISTRIBUPDATECONNTIMEOUT,
DEFAULT_METRICNAMESTRATEGY);
private final int maxUpdateConnections;
@ -35,11 +37,15 @@ public class UpdateShardHandlerConfig {
private final int distributedConnectionTimeout;
public UpdateShardHandlerConfig(int maxUpdateConnections, int maxUpdateConnectionsPerHost, int distributedSocketTimeout, int distributedConnectionTimeout) {
private final String metricNameStrategy;
public UpdateShardHandlerConfig(int maxUpdateConnections, int maxUpdateConnectionsPerHost, int distributedSocketTimeout, int distributedConnectionTimeout,
String metricNameStrategy) {
this.maxUpdateConnections = maxUpdateConnections;
this.maxUpdateConnectionsPerHost = maxUpdateConnectionsPerHost;
this.distributedSocketTimeout = distributedSocketTimeout;
this.distributedConnectionTimeout = distributedConnectionTimeout;
this.metricNameStrategy = metricNameStrategy;
}
public int getMaxUpdateConnectionsPerHost() {
@ -57,4 +63,8 @@ public class UpdateShardHandlerConfig {
public int getDistributedConnectionTimeout() {
return distributedConnectionTimeout;
}
public String getMetricNameStrategy() {
return metricNameStrategy;
}
}

View File

@ -0,0 +1,28 @@
/*
* 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.util.stats;
import org.apache.http.HttpRequest;
/**
* Strategy for creating metric names for HttpClient
* Copied from metrics-httpclient library
*/
public interface HttpClientMetricNameStrategy {
String getNameFor(String scope, HttpRequest request);
}

View File

@ -19,7 +19,9 @@ package org.apache.solr.util.stats;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
@ -35,13 +37,72 @@ import org.apache.http.protocol.HttpRequestExecutor;
import org.apache.solr.metrics.SolrMetricManager;
import org.apache.solr.metrics.SolrMetricProducer;
import static org.apache.solr.metrics.SolrMetricManager.mkName;
/**
* Sub-class of HttpRequestExecutor which tracks metrics interesting to solr
* Inspired and partially copied from dropwizard httpclient library
*/
public class InstrumentedHttpRequestExecutor extends HttpRequestExecutor implements SolrMetricProducer {
public static final HttpClientMetricNameStrategy QUERYLESS_URL_AND_METHOD =
(scope, request) -> {
try {
final RequestLine requestLine = request.getRequestLine();
String schemeHostPort = null;
if (request instanceof HttpRequestWrapper) {
HttpRequestWrapper wrapper = (HttpRequestWrapper) request;
if (wrapper.getTarget() != null) {
schemeHostPort = wrapper.getTarget().getSchemeName() + "://" + wrapper.getTarget().getHostName() + ":" + wrapper.getTarget().getPort();
}
}
final URIBuilder url = new URIBuilder(requestLine.getUri());
return mkName((schemeHostPort != null ? schemeHostPort : "") + url.removeQuery().build().toString() + "." + methodNameString(request), scope);
} catch (URISyntaxException e) {
throw new IllegalArgumentException(e);
}
};
public static final HttpClientMetricNameStrategy METHOD_ONLY =
(scope, request) -> mkName(methodNameString(request), scope);
public static final HttpClientMetricNameStrategy HOST_AND_METHOD =
(scope, request) -> {
try {
final RequestLine requestLine = request.getRequestLine();
String schemeHostPort = null;
if (request instanceof HttpRequestWrapper) {
HttpRequestWrapper wrapper = (HttpRequestWrapper) request;
if (wrapper.getTarget() != null) {
schemeHostPort = wrapper.getTarget().getSchemeName() + "://" + wrapper.getTarget().getHostName() + ":" + wrapper.getTarget().getPort();
}
}
final URIBuilder url = new URIBuilder(requestLine.getUri());
return mkName((schemeHostPort != null ? schemeHostPort : "") + "." + methodNameString(request), scope);
} catch (URISyntaxException e) {
throw new IllegalArgumentException(e);
}
};
public static final Map<String, HttpClientMetricNameStrategy> KNOWN_METRIC_NAME_STRATEGIES = new HashMap<>(3);
static {
KNOWN_METRIC_NAME_STRATEGIES.put("queryLessURLAndMethod", QUERYLESS_URL_AND_METHOD);
KNOWN_METRIC_NAME_STRATEGIES.put("hostAndMethod", HOST_AND_METHOD);
KNOWN_METRIC_NAME_STRATEGIES.put("methodOnly", METHOD_ONLY);
}
protected MetricRegistry metricsRegistry;
protected String scope;
protected HttpClientMetricNameStrategy nameStrategy;
public InstrumentedHttpRequestExecutor(int waitForContinue, HttpClientMetricNameStrategy nameStrategy) {
super(waitForContinue);
this.nameStrategy = nameStrategy;
}
public InstrumentedHttpRequestExecutor(HttpClientMetricNameStrategy nameStrategy) {
this.nameStrategy = nameStrategy;
}
private static String methodNameString(HttpRequest request) {
return request.getRequestLine().getMethod().toLowerCase(Locale.ROOT) + ".requests";
@ -50,7 +111,7 @@ public class InstrumentedHttpRequestExecutor extends HttpRequestExecutor impleme
@Override
public HttpResponse execute(HttpRequest request, HttpClientConnection conn, HttpContext context) throws IOException, HttpException {
Timer.Context timerContext = null;
if (metricsRegistry != null) {
if (metricsRegistry != null) {
timerContext = timer(request).time();
}
try {
@ -63,7 +124,7 @@ public class InstrumentedHttpRequestExecutor extends HttpRequestExecutor impleme
}
private Timer timer(HttpRequest request) {
return metricsRegistry.timer(getNameFor(request));
return metricsRegistry.timer(nameStrategy.getNameFor(scope, request));
}
@Override
@ -72,20 +133,4 @@ public class InstrumentedHttpRequestExecutor extends HttpRequestExecutor impleme
this.scope = scope;
}
private String getNameFor(HttpRequest request) {
try {
final RequestLine requestLine = request.getRequestLine();
String schemeHostPort = null;
if (request instanceof HttpRequestWrapper) {
HttpRequestWrapper wrapper = (HttpRequestWrapper) request;
if (wrapper.getTarget() != null) {
schemeHostPort = wrapper.getTarget().getSchemeName() + "://" + wrapper.getTarget().getHostName() + ":" + wrapper.getTarget().getPort();
}
}
final URIBuilder url = new URIBuilder(requestLine.getUri());
return SolrMetricManager.mkName((schemeHostPort != null ? schemeHostPort : "") + url.removeQuery().build().toString() + "." + methodNameString(request), scope);
} catch (URISyntaxException e) {
throw new IllegalArgumentException(e);
}
}
}

View File

@ -190,7 +190,8 @@ public class TestHarness extends BaseTestHarness {
UpdateShardHandlerConfig updateShardHandlerConfig
= new UpdateShardHandlerConfig(UpdateShardHandlerConfig.DEFAULT_MAXUPDATECONNECTIONS,
UpdateShardHandlerConfig.DEFAULT_MAXUPDATECONNECTIONSPERHOST,
30000, 30000);
30000, 30000,
UpdateShardHandlerConfig.DEFAULT_METRICNAMESTRATEGY);
// universal default metric reporter
Map<String,String> attributes = new HashMap<>();
attributes.put("name", "default");