Return-Path: X-Original-To: apmail-hc-httpclient-users-archive@www.apache.org Delivered-To: apmail-hc-httpclient-users-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 4616E18779 for ; Thu, 25 Feb 2016 11:25:26 +0000 (UTC) Received: (qmail 92071 invoked by uid 500); 25 Feb 2016 11:25:10 -0000 Delivered-To: apmail-hc-httpclient-users-archive@hc.apache.org Received: (qmail 92028 invoked by uid 500); 25 Feb 2016 11:25:10 -0000 Mailing-List: contact httpclient-users-help@hc.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: "HttpClient User Discussion" Delivered-To: mailing list httpclient-users@hc.apache.org Received: (qmail 92017 invoked by uid 99); 25 Feb 2016 11:25:10 -0000 Received: from mail-relay.apache.org (HELO mail-relay.apache.org) (140.211.11.15) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 25 Feb 2016 11:25:10 +0000 Received: from ok2c (84.76.106.92.dynamic.wline.res.cust.swisscom.ch [92.106.76.84]) by mail-relay.apache.org (ASF Mail Server at mail-relay.apache.org) with ESMTPSA id 3869F1A0279 for ; Thu, 25 Feb 2016 11:25:08 +0000 (UTC) Message-ID: <1456399503.14326.5.camel@apache.org> Subject: Re: pooling connection manager: changing max per route From: Oleg Kalnichevski To: HttpClient User Discussion Date: Thu, 25 Feb 2016 12:25:03 +0100 In-Reply-To: References: <1456254467.29472.2.camel@apache.org> Content-Type: text/plain; charset="UTF-8" X-Mailer: Evolution 3.12.9-1+b1 Mime-Version: 1.0 Content-Transfer-Encoding: 8bit On Wed, 2016-02-24 at 01:23 +0000, Pete Keyes wrote: > I think this is a valid example. Code below produces the following I/O on stdout. Max routes for the specific Route is set to 200. The default for the connection pool manager is 50. > > [Tue Feb 23 17:05:11 PST 2016]static http-deliver init starting... > [Tue Feb 23 17:05:11 PST 2016]init hc ssl context... > [Tue Feb 23 17:05:12 PST 2016]init hc 'allow-all' ssl socket factory... > [Tue Feb 23 17:05:12 PST 2016]init hc 'allow-all' ssl registry... > [Tue Feb 23 17:05:12 PST 2016]init hc 'allow-all' connection manager... > [Tue Feb 23 17:05:12 PST 2016]creating common http-client:; conn-timeout=120000ms; conns-max=100000; so-timeout=120000ms > [Tue Feb 23 17:05:12 PST 2016]starting http-client connection monitor thread... > [Tue Feb 23 17:05:12 PST 2016]http-client connection pool monitor started. > > logs specific route max > [Tue Feb 23 17:05:12 PST 2016]set route max-conns:; max-allowed=200; protocol-host-port=https://lx01796:2443; httpRoute={}->https://lx01796:2443 > > pool monitor logs stats from pool manager. value is 50 rather than 200 > [Tue Feb 23 17:05:13 PST 2016]emit telemetry message: type=hc-conn-pool; route=lx01796; available=0; active=1; blocking=0; max-allowed=50; server-nm=localhost:0000 > > [Tue Feb 23 17:05:13 PST 2016]emit telemetry message: type=hc-conn-pool; route=all-routes; available=0; active=1; blocking=0; max-allowed=100000; server-nm=localhost:0000 > [Tue Feb 23 17:05:14 PST 2016]emit telemetry message: type=hc-conn-pool; route=lx01796; available=0; active=1; blocking=0; max-allowed=50; server-nm=localhost:0000 > [Tue Feb 23 17:05:14 PST 2016]emit telemetry message: type=hc-conn-pool; route=all-routes; available=0; active=1; blocking=0; max-allowed=100000; server-nm=localhost:0000 > [Tue Feb 23 17:05:14 PST 2016]response code: 500 > [Tue Feb 23 17:05:15 PST 2016]emit telemetry message: type=hc-conn-pool; route=lx01796; available=0; active=0; blocking=0; max-allowed=50; server-nm=localhost:0000 > [Tue Feb 23 17:05:15 PST 2016]emit telemetry message: type=hc-conn-pool; route=all-routes; available=0; active=0; blocking=0; max-allowed=100000; server-nm=localhost:0000 > [Tue Feb 23 17:05:16 PST 2016]emit telemetry message: type=hc-conn-pool; route=all-routes; available=0; active=0; blocking=0; max-allowed=100000; server-nm=localhost:0000 > I ran your code and did not see a single log entry with 'max-allowed=50'. Oleg > > > Test Case Code: > package com.sbux.junk; > > import java.net.URL; > import java.nio.charset.Charset; > import java.util.Date; > import java.util.Set; > import java.util.concurrent.TimeUnit; > import java.util.concurrent.atomic.AtomicBoolean; > import javax.net.ssl.HostnameVerifier; > import javax.net.ssl.SSLContext; > import org.apache.http.HttpHost; > import org.apache.http.HttpResponse; > import org.apache.http.auth.AuthScope; > import org.apache.http.auth.UsernamePasswordCredentials; > import org.apache.http.client.AuthCache; > import org.apache.http.client.CredentialsProvider; > import org.apache.http.client.config.CookieSpecs; > import org.apache.http.client.config.RequestConfig; > import org.apache.http.client.methods.HttpGet; > import org.apache.http.client.methods.HttpRequestBase; > import org.apache.http.client.protocol.HttpClientContext; > import org.apache.http.config.ConnectionConfig; > import org.apache.http.config.Registry; > import org.apache.http.config.RegistryBuilder; > import org.apache.http.config.SocketConfig; > import org.apache.http.conn.routing.HttpRoute; > import org.apache.http.conn.socket.ConnectionSocketFactory; > import org.apache.http.conn.socket.PlainConnectionSocketFactory; > import org.apache.http.conn.ssl.NoopHostnameVerifier; > import org.apache.http.conn.ssl.SSLConnectionSocketFactory; > import org.apache.http.conn.ssl.TrustSelfSignedStrategy; > import org.apache.http.impl.auth.BasicScheme; > import org.apache.http.impl.client.BasicAuthCache; > import org.apache.http.impl.client.BasicCredentialsProvider; > import org.apache.http.impl.client.CloseableHttpClient; > import org.apache.http.impl.client.HttpClientBuilder; > import org.apache.http.impl.client.HttpClients; > import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; > import org.apache.http.pool.PoolStats; > import org.apache.http.ssl.SSLContexts; > import org.apache.logging.log4j.Marker; > import org.apache.logging.log4j.MarkerManager; > > > > public class HttpClientConnLimitTest { > private static PoolingHttpClientConnectionManager globalConnManagerPool; > private static Thread globalConnManagerThread; > private static CloseableHttpClient globalHttpClient; > public static final HostnameVerifier ALLOW_ALL_VERIFIER = NoopHostnameVerifier.INSTANCE; > public static final int DEFAULT_MAX_CONNECTIONS = 10000; > public static final int DEFAULT_MAX_CONNECTIONS_PER_ROUTE = 50; > public static final int DEFAULT_MAX_INACTIVITY_MS = 1000*60; > public static final long DEFAULT_MAX_CONN_IDLE_MINUTES = 5; > public static final int DEFAULT_DEFAULT_CONNECT_TIMEOUT_MS = 120000; > public static final int DEFAULT_DEFAULT_SO_TIMEOUT_MS = 120000; > public static final int DEFAULT_DEFAULT_RCV_BUFFER_BYTES = 2048; > public static final int DEFAULT_DEFAULT_SND_BUFFER_BYTES = 2048; > public static final String DEFAULT_DEFAULT_SOCKET_SO_KEEPALIVE_FLAG = "yes"; > public static final String DEFAULT_HAWS_HTTP_CLIENT_CONNECTION_MONITOR = "yes"; > > > private static final AtomicBoolean singletonsInitialized = new AtomicBoolean(false); > > private static void log(String s) { > System.out.println("[" + new Date() + "]" + s); > } > private static void pause(long l) { > try { Thread.sleep(l); } catch(Throwable t) {} > } > > private static void initSingletons() throws Exception { > final boolean done = singletonsInitialized.getAndSet(true); > if(done) { > return; > } > > log("static http-deliver init starting..."); > log("init hc ssl context..."); > SSLContext sslContext; > sslContext = SSLContexts.custom().useProtocol("TLS") > .loadTrustMaterial(null, new TrustSelfSignedStrategy()) > .build() > ; > log("init hc 'allow-all' ssl socket factory..."); > final SSLConnectionSocketFactory sslConnSocketFactory = new SSLConnectionSocketFactory( > sslContext > , ALLOW_ALL_VERIFIER > ); > log("init hc 'allow-all' ssl registry..."); > final Registry registry = RegistryBuilder.create() > .register("http", PlainConnectionSocketFactory.getSocketFactory()) > .register("https", sslConnSocketFactory) > .build() > ; > > log("init hc 'allow-all' connection manager..."); > globalConnManagerPool = new PoolingHttpClientConnectionManager( > registry > ); > > /* > * setup the connection pool manager defaults > */ > globalConnManagerPool.setDefaultMaxPerRoute(DEFAULT_MAX_CONNECTIONS_PER_ROUTE); > globalConnManagerPool.setMaxTotal(100000); > globalConnManagerPool.setValidateAfterInactivity(30000); > > log("creating common http-client:" > + "; conn-timeout=" + 120000 + "ms" > + "; conns-max=" + 100000 > + "; so-timeout=" + 120000 + "ms" > ); > final RequestConfig requestConfig = RequestConfig.copy(RequestConfig.DEFAULT) > .setConnectTimeout(120000) > .setConnectionRequestTimeout(120000) > .setCookieSpec(CookieSpecs.DEFAULT) > .setSocketTimeout(120000) > .build() > ; > > /* > * create http client for this bean - same for all env's > * > * each env specific execution will have private http client context > */ > final Charset charset = Charset.forName("utf-8"); > final ConnectionConfig connConfig = ConnectionConfig.copy(ConnectionConfig.DEFAULT) > .setCharset(charset) > .build() > ; > > // build custom socket config > final SocketConfig socketConfig = SocketConfig.copy(SocketConfig.DEFAULT) > .setRcvBufSize(2048) > .setSndBufSize(2048) > .setSoKeepAlive(true) > .setSoTimeout(120000) > .build() > ; > final HttpClientBuilder builder = HttpClients.custom() > .setConnectionManager(globalConnManagerPool) > .setConnectionManagerShared(true) > .setConnectionTimeToLive(DEFAULT_MAX_CONN_IDLE_MINUTES, TimeUnit.MINUTES) > .setDefaultConnectionConfig(connConfig) > .setDefaultRequestConfig(requestConfig) > .setMaxConnPerRoute(DEFAULT_MAX_CONNECTIONS_PER_ROUTE) > .setDefaultSocketConfig(socketConfig) > .setMaxConnTotal(100000) > .useSystemProperties() > ; > > /* > * disable hostname verification ... if needed > */ > builder.setSSLHostnameVerifier(ALLOW_ALL_VERIFIER); > > // add CloseableHttpClient to the map > globalHttpClient = builder.build(); > > globalConnManagerThread = new Thread() { > final Marker marker = MarkerManager.getMarker("http-pool"); > private void cleanupPools() throws Exception { > globalConnManagerPool.closeExpiredConnections(); > globalConnManagerPool.closeIdleConnections( > 5, TimeUnit.MINUTES > ); > } > > private void getPoolStats( > final PoolingHttpClientConnectionManager cm > ) > { > try { > /* > * inspect pool by route > */ > Set routes = cm.getRoutes(); > if(routes != null && !routes.isEmpty()) { > for(final HttpRoute r : routes) { > final String hostname = r.getTargetHost().getHostName(); > > // get pool stats for route > final PoolStats ps = cm.getStats(r); > final int available = ps.getAvailable(); > final int active = ps.getLeased(); > final int limit = ps.getMax(); > final int blocking = ps.getPending(); > > String s = "emit telemetry message:" > + " type=hc-conn-pool" > + "; route=" + hostname > + "; available=" + available > + "; active=" + active > + "; blocking=" + blocking > + "; max-allowed=" + limit > + "; server-nm=" + "localhost:0000" > ; > log(s); > } > } > > /* > * inspect pool globally across all routes > */ > PoolStats tps = cm.getTotalStats(); > int available = tps.getAvailable(); > int active = tps.getLeased(); > int limit = tps.getMax(); > int blocking = tps.getPending(); > String s = "emit telemetry message:" > + " type=hc-conn-pool" > + "; route=all-routes" > + "; available=" + available > + "; active=" + active > + "; blocking=" + blocking > + "; max-allowed=" + limit > + "; server-nm=" + "localhost:0000" > ; > log(s); > } > catch(Throwable t) { > log("failed to log http-client connection pool stats: " + t); > t.printStackTrace(System.out); > } > } > > private void logPoolStats() throws Exception { > getPoolStats(globalConnManagerPool); > } > > @Override public void run() { > log("http-client connection pool monitor started."); > while(true) { > pause(1000); > try { > logPoolStats(); > cleanupPools(); > } > catch(Throwable t) { > log("http connection monitor error: " + t); > t.printStackTrace(System.out); > } > } > } > }; > > log("starting http-client connection monitor thread..."); > globalConnManagerThread.setDaemon(true); > globalConnManagerThread.start(); > } > > private static void startRequestor() throws Exception { > Thread t = new Thread() { > private RequestConfig requestConfig; > > @Override public void run() { > final RequestConfig requestConfig = RequestConfig.copy(RequestConfig.DEFAULT) > .setAuthenticationEnabled(true) > .setConnectTimeout(120000) > .setConnectionRequestTimeout(120000) > .setExpectContinueEnabled(true) > .setRedirectsEnabled(false) > .setSocketTimeout(120000) > .build() > ; > > URL url=null; > try { > url = new URL("https://HOSTNAME:PORTNO/GET/SERVICE/URI"); > } > catch(Throwable t) { > log("can't parse url"); > t.printStackTrace(System.out); > } > > final String urlText = url.toString(); > final String host = url.getHost(); > final int portNo = url.getPort(); // == -1 ? url.getDefaultPort() : url.getPort(); > final String protocol = url.getProtocol(); > final HttpHost httpHost = new HttpHost(host, portNo, protocol); > final HttpRoute httpRoute = new HttpRoute(httpHost); > globalConnManagerPool.setMaxPerRoute(httpRoute, 200); > log("set route max-conns:" > + "; max-allowed=" + 200 > + "; protocol-host-port=" + protocol + "://" + host + ":" + portNo > + "; httpRoute=" + String.valueOf(httpRoute) > ); > > final String username = "USERNAME HERE"; > final String password = "PASSWORD HERE"; > final AuthCache authCache; > final CredentialsProvider credsProvider; > final AuthScope authScope = new AuthScope(url.getHost(), portNo); > final UsernamePasswordCredentials basic = new UsernamePasswordCredentials(username, password); > credsProvider = new BasicCredentialsProvider(); > credsProvider.setCredentials(authScope, basic); > > // create authentication cache for preemptive authorization > authCache = new BasicAuthCache(); > final BasicScheme basicScheme = new BasicScheme(); > authCache.put(httpHost, basicScheme); > HttpClientContext httpContext = HttpClientContext.create(); > httpContext.setAuthCache(authCache); > httpContext.setCredentialsProvider(credsProvider); > httpContext.setRequestConfig(requestConfig); > > for(int i=0; i < 100; i++) { > pause(250); > try { > HttpRequestBase method = new HttpGet(urlText); > method.setHeader("gws-requestId", "pkut-"+System.currentTimeMillis()); > method.setHeader("gws-audit", "pk"); > method.setHeader("gws-environment", "ps"); > method.setHeader("gws-version", "1"); > > HttpResponse response = globalHttpClient.execute(method, httpContext); > log("response code: " + response.getStatusLine().getStatusCode()); > response.getEntity().getContent().close(); > } > catch(Throwable t) { > log("error invoking service: " + t); > t.printStackTrace(System.out); > } > } > } > }; > t.start(); > } > > public static void main(String args[]) throws Exception { > initSingletons(); > for(int i=0; i < 10; i++) { > startRequestor(); > } > pause(1000*5); > } > } > > — > Pete > > > From: "olegk@apache.org" > > Reply-To: HttpClient Discussion > > Date: Tuesday, February 23, 2016 at 11:07 AM > To: HttpClient Discussion > > Subject: Re: pooling connection manager: changing max per route > > On Tue, 2016-02-23 at 01:11 +0000, Pete Keyes wrote: > version: 4.4.1 > App Server: TomEE7 > We use PoolingHttpClientConnectionManager with the following defaults: > * max total connections: 100,000 > * default max per route: 50 > we create the HttpClient and PoolingHttpClientConnectionManager as static singletons at container startup. > private static Thread globalConnManagerThread; > private static CloseableHttpClient globalHttpClient; > static { > // create global pooling connection manager > globalConnManagerPool = new PoolingHttpClientConnectionManager( > registry > ); > globalConnManagerPool.setDefaultMaxPerRoute(50); > globalConnManagerPool.setMaxTotal(100000); > // create global HttpClient instance > final HttpClientBuilder builder = HttpClients.custom() > .setConnectionManager(globalConnManagerPool) > .setConnectionManagerShared(true) > .setConnectionTimeToLive(5, TimeUnit.MINUTES) > .setDefaultConnectionConfig(connConfig) > .setDefaultRequestConfig(requestConfig) > .setMaxConnPerRoute(50) // <<== default per route is 50 > .setDefaultSocketConfig(socketConfig) > .setMaxConnTotal(100000) > .setRetryHandler(httpRetryHandler) > .useSystemProperties() > ; > > Please note that connection level parameters will have no effect when > the connection manager is explicitly set. Please have a look at > Javadocs. > > > CloseableHttpClient globalHttpClient = builder.build(); > } > We set the max per specific route with the following code snippet: > @PostConstruct public void init() { > List allUrls = getAllUrls(); > for(final URL url : allUrls) { > HttpServiceConfig hsc = getHttpServiceConfig(url); > final String host = url.getHost(); > final int portNo = url.getPort() == -1 ? url.getDefaultPort() : url.getPort(); > final String protocol = url.getProtocol(); > final HttpHost httpHost = new HttpHost(host, portNo, protocol); > final HttpRoute httpRoute = new HttpRoute(httpHost); > globalConnManagerPool.setMaxPerRoute(httpRoute, 200); // <<== max set to 200 for route > log.info(gMarker, "set route max-conns:" > + "; max-allowed=" + hsc.getMaxConns() > + "; protocol-host-port=" + protocol + "://" + host + ":" + portNo > + "; httpRoute=" + String.valueOf(httpRoute) > ); > } > } > we see each route specific max connection log message at startup. for instance: > set route max-conns: conns-max=200; protocol-host-port=https://some-host:443; httpRoute={}->https://some-host:443 > we have a background thread that monitors the apache-hc connection pool statistics and emits log messages. we see the following apache-hc connection manager pool “route specific” statistics logged: > private void logPoolStats() { > Set routes = globalConnManagerPool.getRoutes(); > for(final HttpRoute r : routes) { > final PoolStats ps = cm.getStats(r); > final int available = ps.getAvailable(); > final int active = ps.getLeased(); > final int limit = ps.getMax(); > final int blocking = ps.getPending(); > String s = "emit statistic:" > + " type=hc-conn-pool" > + "; route=" + hostname > + "; available=" + available > + "; active=" + active > + "; blocking=" + blocking > + "; max-allowed=" + limit > + "; server-nm=" + Helpers.getLocalHost() + ":" + Helpers.getServerHttpsPort() > ; > log.info(marker, s); > } > } > we see log messages like the following from the apache-hc connection pool monitor logging: > emit statistic: type=hc-conn-pool; route=some-host; available=1; active=0; blocking=0; max-allowed=50 > The connection manager isn’t respecting the per-route connection settings. Above we see that the “max-allowed” per route reported by the pool-manager is 50 for a route that specifically set the max-allowed to 200. > Any hints about what we are doing wrong to override the limit on a per-route basis? > > Could you please try to reproduce the issue with a test case? > > Oleg > > > --------------------------------------------------------------------- > To unsubscribe, e-mail: httpclient-users-unsubscribe@hc.apache.org > For additional commands, e-mail: httpclient-users-help@hc.apache.org > > --------------------------------------------------------------------- To unsubscribe, e-mail: httpclient-users-unsubscribe@hc.apache.org For additional commands, e-mail: httpclient-users-help@hc.apache.org