catalogue
- Connection pool settings.
- Get the settings of connection timeout, establish connection timeout and maintain connection timeout.
- Long connection policy settings.
- Connection eviction policy settings.
- Setting of retry mechanism.
- Setting of personalization request parameters.
- Appendix.
order
HttpClient can be used to provide an efficient, up-to-date and feature rich client programming toolkit supporting HTTP protocol, and it supports the latest version and suggestions of HTTP protocol.
To send a request and receive a response using HttpClient:
- Create CloseableHttpClient object;
- Create a request method instance and specify the request URL. Example: if you want to send a Get request, create an HttpGet object; If you want to send a POST request, create an HttpPost object;
- If you need to send parameters, call setentity (httpentity) method to set parameters;
- Call setHeader(String name,String value) method of HttpGet/HttpPost object to set header information, or call setHeader(Header[] headers) to set a set of header parameters;
- Call execute (httpurirequest request request) of the CloseableHttpClient object to send a request. This method returns a CloseableHttpResponse;
- Call the getEntity() method of HttpResponse to get the HttpEntity object, which wraps the response content of the server. The program can obtain the response content of the server through the object; Call getAllHeaders(), getHeaders(String name) and other methods of CloseableHttpResponse to obtain the response header of the server;
- Release the connection. The connection must be released whether or not the execution method is successful
1. Introduce Maven dependency
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency>
1. HttpClient connection pool analysis
PoolingHttpClientConnectionManager is a connection pool of HttpClientConnection, which can provide concurrent request services for multiple threads. It mainly distributes connections and reclaims connections. For the same remote request, the idle long connection provided by the connection pool will be used preferentially.
Source location: org apache. http. impl. conn.PoolingHttpClientConnectionManager
Default construction method:
/** * @since 4.4 */ public PoolingHttpClientConnectionManager( final HttpClientConnectionOperator httpClientConnectionOperator, final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory, final long timeToLive, final TimeUnit timeUnit) { super(); this.configData = new ConfigData(); //The default configuration of connection pool is 2 for defaultMaxPerRoute and 20 for maxTotal this.pool = new CPool(new InternalConnectionFactory( this.configData, connFactory), 2, 20, timeToLive, timeUnit); //Officials recommend using this to check the availability of permanent links, rather than checking each request this.pool.setValidateAfterInactivity(2000); this.connectionOperator = Args.notNull(httpClientConnectionOperator, "HttpClientConnectionOperator"); this.isShutDown = new AtomicBoolean(false); }
- maxTotal: the maximum number of connections in the connection pool.
- Defaultmaxpreroute: the maximum number of connections per route (remote) request.
- setValidateAfterInactivity: check how long the connection is idle (in milliseconds).
Adjust connection pool parameters displayed:
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); // Increase max total connection to 200 cm.setMaxTotal(200); // Increase default max connection per route to 20 cm.setDefaultMaxPerRoute(20); // Increase max connections for localhost:80 to 50 HttpHost localhost = new HttpHost("locahost", 80); cm.setMaxPerRoute(new HttpRoute(localhost), 50); CloseableHttpClient httpClient = HttpClients.custom() .setConnectionManager(cm) .build();
Parameter settings, code writing and risks that must be known when using httpclient
1.1 difference between maxtotal and DefaultMaxPerRoute
Parameter configuration: MaxTotal=100, DefaultMaxPerRoute=5
Server side sleep for 2 seconds. This is a screenshot of the response information of the client.
Screenshot of concurrent response png
It can be seen that only five requests call the remote server concurrently, and after receiving the response. Five requests call the server again.
- MaxtTotal is the size of the whole pool;
- DefaultMaxPerRoute is a subdivision of MaxTotal according to the connected host; For example:
MaxtTotal=400 DefaultMaxPerRoute=200
And I only connect to http://sishuok.com When, the concurrency to this host is only 200 at most; Not 400;
And I'm connected to http://sishuok.com and http://qq.com When, the concurrency to each host is only 200 at most; That is, the total is 400 (but not more than 400); So the setting that works is DefaultMaxPerRoute.
2. SpringBoot integrates HttpClient
2.1 timeout setting
There are three timeout settings in httpClient: timeout for obtaining connection, timeout for establishing connection and timeout for reading data.
//Set up network Configurator @Bean public RequestConfig requestConfig(){ return RequestConfig.custom().setConnectionRequestTimeout(2000) //Timeout for getting connections from link pool .setConnectTimeout(2000) //Timeout time of connecting with the server, timeout time of creating socket connection .setSocketTimeout(2000) //Timeout of socket reading data and timeout of obtaining data from server .build(); }
1. Get available connection timeout ConnectionRequestTimeout from connection pool
Try to get a connection from the connection pool when you want to use a connection in HttpClient. If you haven't got an available connection after waiting for a certain time (for example, there are no idle connections in the connection pool), you will throw a get connection timeout exception.
org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool
The number of concurrent requested connections exceeds the DefaultMaxPerRoute setting. If there is still no available connection within the ConnectionRequestTimeout time, the above exception will be thrown. The way to solve the above exception is to appropriately increase the sizes of DefaultMaxPerRoute and MaxTotal.
2. Connection target timeout connectionTimeout
It refers to the connection timeout of connecting to the target url, that is, the maximum time from the customer service end sending the request to establishing a connection with the target url. If a connection has not been established within this time range, a connectionTimeOut exception is thrown.
For example, when testing, change the url to a nonexistent url:“ http://test.com ”, after the timeout of 3000ms, the system reports an exception: org.apache.commons.httpclient.ConnectTimeoutException:The host did not accept the connection within timeout of 3000 ms
3. Waiting for response timeout (data reading timeout) socketTimeout
After connecting to the previous url, obtain the return waiting time of the response, that is, the maximum time to wait for the response to be put back after establishing a connection with the target url. If no response is returned within the specified time, SocketTimeout will be thrown.
The connection url during the test is a url opened locally by me, http://localhost:8080/firstTest.htm?method=test , in my test url, when accessing this link, the thread sleep s for a period of time to simulate the response timeout returned.
2.2 KeepAliveStrategy strategy
Keep alive ——By using the keep alive mechanism, the number of tcp connection establishment can be reduced, which is also thought to reduce TIME_WAIT state connection to improve performance and throughput of HTTP server (fewer tcp connections mean fewer system kernel calls, socket accept() and close() calls). However, long-time tcp connection is easy to lead to invalid occupation of system resources. Improperly configured keep alive may cause greater losses than reusing the connection. Therefore, it is very important to set the keep alive timeout time correctly.
Keep alive: timeout=5, max=100.
It means: the expiration time is 5 seconds, max is up to 100 requests, and the connection is forcibly disconnected. That is, for each new request within the timeout time, max will automatically decrease by 1 until it is 0, and the connection will be forcibly disconnected.
It should be noted that the use of keep alive should be determined according to the business situation. If a few fixed clients visit the server for a long time and high frequency, it is very appropriate to enable keep client!
The default keepClient policy in HttpClient:
org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy
By default, the timeout parameter in keep alive in response is read. If it is not read, it is set to - 1, which represents infinity. However, there is a problem with this setting. Because the real HTTP server is configured to lose the connection after a specific inactivity cycle to save system resources, it often does not notify the client.
Default keep alive policy
@Contract(threading = ThreadingBehavior.IMMUTABLE) public class DefaultConnectionKeepAliveStrategy implements ConnectionKeepAliveStrategy { public static final DefaultConnectionKeepAliveStrategy INSTANCE = new DefaultConnectionKeepAliveStrategy(); @Override public long getKeepAliveDuration(final HttpResponse response, final HttpContext context) { Args.notNull(response, "HTTP response"); final HeaderElementIterator it = new BasicHeaderElementIterator( response.headerIterator(HTTP.CONN_KEEP_ALIVE)); while (it.hasNext()) { final HeaderElement he = it.nextElement(); final String param = he.getName(); final String value = he.getValue(); if (value != null && param.equalsIgnoreCase("timeout")) { try { return Long.parseLong(value) * 1000; } catch(final NumberFormatException ignore) { } } } return -1; } }
Solution: you can customize the keep alive policy. If you don't read it, set the save connection to 60s.
@Bean public HttpClientBuilder httpClientBuilder(PoolingHttpClientConnectionManager poolingHttpClientConnectionManager) { HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); //Set up connection pool httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager); //Set timeout httpClientBuilder.setDefaultRequestConfig(requestConfig()); //Define that the connection manager will be shared by multiple client instances. If the connection manager is shared, its life cycle should be managed by the caller. If the client is closed, it will not be closed. httpClientBuilder.setConnectionManagerShared(true); //Set KeepAlive ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy() { public long getKeepAliveDuration(HttpResponse response, HttpContext context) { // Honor 'keep-alive' header HeaderElementIterator it = new BasicHeaderElementIterator( response.headerIterator(HTTP.CONN_KEEP_ALIVE)); while (it.hasNext()) { HeaderElement he = it.nextElement(); String param = he.getName(); String value = he.getValue(); if (value != null && param.equalsIgnoreCase("timeout")) { try { return Long.parseLong(value) * 1000; } catch(NumberFormatException ignore) { } } } HttpHost target = (HttpHost) context.getAttribute( HttpClientContext.HTTP_TARGET_HOST); if ("www.naughty-server.com".equalsIgnoreCase(target.getHostName())) { // Keep alive for 5 seconds only return 5 * 1000; } else { // otherwise keep alive for 30 seconds return 30 * 1000; } } }; httpClientBuilder.setKeepAliveStrategy(myStrategy); return httpClientBuilder; }
2.3 connection eviction policy
When a connection is released to the connection pool, it can remain active without monitoring the status of the socket and any I/O events. If the connection is closed on the server side, the client connection cannot detect the changes in the connection state and close the socket on the local side to make an appropriate response.
HttpClient tries to solve this problem by testing whether the connection is valid, but it is closed on the server side, and the failed connection check is not 100% reliable. The only solution: create a monitoring thread to recycle connections that are considered expired due to prolonged inactivity.
public class IdleConnectionMonitorThread extends Thread { private final HttpClientConnectionManager connMgr; private volatile boolean shutdown; public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr) { super(); this.connMgr = connMgr; } @Override public void run() { try { while (!shutdown) { synchronized (this) { wait(5000); // Close expired connections connMgr.closeExpiredConnections(); // Optionally, close connections // that have been idle longer than 30 sec connMgr.closeIdleConnections(30, TimeUnit.SECONDS); } } } catch (InterruptedException ex) { // terminate } } public void shutdown() { shutdown = true; synchronized (this) { notifyAll(); } } }
The monitoring thread can periodically call the ClientConnectionManager#closeExpiredConnections() method to close all expired connections and withdraw the closed connections from the connection pool. It can also optionally call the ClientConnectionManager#closeIdleConnections() method to close all connections that have been idle for more than a given period of time. httpclient parameter configuration
@Bean public HttpClientBuilder httpClientBuilder(PoolingHttpClientConnectionManager poolingHttpClientConnectionManager) { HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); //Set connection pool httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager); //Set timeout httpClientBuilder.setDefaultRequestConfig(requestConfig()); //Define that the connection manager will be shared by multiple client instances. If the connection manager is shared, its life cycle should be managed by the caller. If the client is closed, it will not be closed. httpClientBuilder.setConnectionManagerShared(true); //Start the thread and empty the failed connection once in 5 seconds new IdleConnectionMonitorThread(poolingHttpClientConnectionManager).start(); return httpClientBuilder; }
2.4 retry mechanism of httpclient
This parameter is recommended to be closed if there are a large number of concurrent requests. If the number of items is less than, this is the default.
HttpClient uses connection pool PoolingHttpClientConnectionManager
Set retry policy: org apache. http. impl. client. DefaultHttpRequestRetryHandler
Source code of retry mechanism: org apache. http. impl. execchain. RetryExec#execute
The retttryhandler policy is not used by default, regardless of the setting of htttryhandler.
Construction method of default policy:
public DefaultHttpRequestRetryHandler(final int retryCount, final boolean requestSentRetryEnabled) { this(retryCount, requestSentRetryEnabled, Arrays.asList( InterruptedIOException.class, UnknownHostException.class, ConnectException.class, SSLException.class)); }
- retryCount: number of retries;
- requestSentRetryEnabled: if a request is retried successfully, whether it will be retried again;
- InterruptedIOException, UnknownHostException, ConnectException and sslexexception. In case of these 4 exceptions (and subclass exceptions), do not retry;
Verification method of default retry policy: org apache. http. impl. client. DefaultHttpRequestRetryHandler # retryRequest
@Override public boolean retryRequest( final IOException exception, final int executionCount, final HttpContext context) { Args.notNull(exception, "Exception parameter"); Args.notNull(context, "HTTP context"); if (executionCount > this.retryCount) { // Do not retry if over max retry count return false; } if (this.nonRetriableClasses.contains(exception.getClass())) { return false; } for (final Class<? extends IOException> rejectException : this.nonRetriableClasses) { if (rejectException.isInstance(exception)) { return false; } } final HttpClientContext clientContext = HttpClientContext.adapt(context); final HttpRequest request = clientContext.getRequest(); //If the same request has been terminated in the asynchronous task, it will not be retried if(requestIsAborted(request)){ return false; } //Determine whether the request is idempotent if (handleAsIdempotent(request)) { // Retry if the request is considered idempotent return true; } //If the request is not sent successfully, or if it is allowed to be sent successfully, it can be sent again if (!clientContext.isRequestSent() || this.requestSentRetryEnabled) { // Retry if the request has not been sent fully or // if it's OK to retry methods that have been sent return true; } // otherwise do not retry return false; }
About the default retry policy:
- If it is retried more than 3 times, it will not be retried;
- If the retry is a special exception and its subclasses, it will not be retried (see below);
- The same request is not requested if the asynchronous task is terminated;
- Idempotent methods can be retried, such as Get;
- If the request is not sent successfully, it can be retried;
How to judge whether the request is sent successfully?
Source code: org apache. http. protocol. Httpcorecontext # isrequestsent according to http request_ Send parameter to judge whether the transmission is successful.
The underlying communication of RetryExec uses MainClientExec, and the underlying communication of MainClientExec calls httprequesteexecutor doSendRequest().
So http request_ The send parameter is set through httprequesteexecutor Set by dosendrequest() method.
Exception not retried
Research on HttpClient retry strategy
- InterruptedIOException, thread interrupt exception
- UnknownHostException, corresponding host not found
- ConnectException, found host, but failed to establish connection.
- SSLException, https authentication exception
In addition, we often mention two types of timeout, connection timeout and read timeout:
- java.net.SocketTimeoutException: Read timed out
- java.net.SocketTimeoutException: connect timed out
Both timeouts are sockettimeoutexceptions, which inherit from InterruptedIOException. They belong to the first thread interrupt exception above and will not be retried.
Idempotent request not retried
In the default retry class: handleAsIdempotent(request) will check whether the request is idempotent. Default implementation:
public class DefaultHttpRequestRetryHandler implements HttpRequestRetryHandler { protected boolean handleAsIdempotent(final HttpRequest request) { return !(request instanceof HttpEntityEnclosingRequest); } }
Judge whether the request belongs to HttpEntityEnclosingRequest class.
Subclass png
This will cause the handleAsIdempotent method to return false if it is a post request, that is, it will not retry.
How to prohibit retry
In HttpClinetBuilder, when RetryExec executor is selected in its Build() method, the retry policy is enabled by default.
Therefore, we can manually disable it when building an httpClient instance.
httpClientBuilder.disableAutomaticRetries();
How to customize retry policy
Just need to implement org apache. http. client. Httprequestretryhandler interface, just re the methods inside.
The source code of the retry strategy is at org apache. http. impl. execchain. Retryexec#execute implementation.
httpClientBuilder.setRetryHandler(new MyHttpRequestRetryHandler());
2.5 setting personalized request parameters
Because in the configuration file, we have configured the default socket timeout (the maximum time to establish a connection, that is, the response timeout), but in the actual business, different requests have different response timeout. How to set different timeout times for different businesses?
We know that in fact, the CloseableHttpClient we injected is an abstract class. In fact, it will be org apache. http. impl. client. Internalhttpclient type is injected in, so we use org apache. http. client. methods. When httprequestbase (Note: the common parent of httpPost/httpGet) sends a request, the RequestConfig parameter can be set separately.
RequestConfig.Builder custom = RequestConfig.copy(configClient.getConfig()); Get requestconfig Builder object to set personalization parameters.
private static String doHttp(HttpRequestBase request, int socketTimeout) throws IOException { //Set timeout if (socketTimeout > 0) { //Get the original configuration //Actual injection type org apache. http. impl. client. InternalHttpClient Configurable configClient = (Configurable) httpClient; RequestConfig.Builder custom = RequestConfig.copy(configClient.getConfig()); //Set personalized configuration RequestConfig config = custom.setSocketTimeout(socketTimeout).build(); request.setConfig(config); } ResponseHandler<String> handler = new BasicResponseHandler(); String response = httpClient.execute(request, handler); return response; } }
2.6 HttpClient response data processing
EntityUtils.consume will release all the resources held by httpEntity, which actually means releasing any underlying flow and putting the connection object back into the pool (in the case of multithreading when connecting to the pool), or releasing the connection manager to process the next request.
Source code: org apache. http. impl. client. CloseableHttpClient # execute
If you get a custom response entity, you can implement org apache. http. client. Responsehandler interface.
How to process the response:
@Test public void test1() throws IOException, InterruptedException { HttpPost httpPost = new HttpPost("http://www.baidu.com"); httpPost.setConfig(requestConfig); Map<String, String> innerReq = new HashMap<>(); innerReq.put("XX", "data1"); innerReq.put("YY", "data2"); String innerReqJson = JSONObject.toJSONString(innerReq); StringEntity entity = new StringEntity(innerReqJson, "UTF-8"); httpPost.addHeader("content-type", "application/json;charset=UTF-8"); httpPost.setEntity(entity); //Request execution CloseableHttpResponse execute = closeableHttpClient.execute(httpPost); //Set return data String res = EntityUtils.toString(execute.getEntity(), "UTF-8"); //close resource EntityUtils.consume(execute.getEntity()); log.info(res); }
close resource
EntityUtils.consume(execute.getEntity());
(New) use ResponseHandler to process response data
Whether the request is executed successfully or causes an exception, HttpClient will automatically ensure that the connection is released back to the connection manager.
@Test public void test() throws IOException, InterruptedException { HttpPost httpPost = new HttpPost("http://www.baidu.com"); httpPost.setConfig(requestConfig); Map<String, String> innerReq = new HashMap<>(); innerReq.put("XX", "data1"); innerReq.put("YY", "data2"); String innerReqJson = JSONObject.toJSONString(innerReq); StringEntity entity = new StringEntity(innerReqJson, "UTF-8"); httpPost.addHeader("content-type", "application/json;charset=UTF-8"); httpPost.setEntity(entity); //Custom ResponseHandler ResponseHandler<ResponseVo> handler = new ResponseHandler<ResponseVo>() { @Override public ResponseVo handleResponse(HttpResponse response) throws ClientProtocolException, IOException { final StatusLine statusLine = response.getStatusLine(); final HttpEntity entity = response.getEntity(); if (statusLine.getStatusCode() >= 300) { EntityUtils.consume(entity); throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); } if (entity == null) { throw new ClientProtocolException("Abnormal!"); } String res = EntityUtils.toString(entity); ResponseVo responseVo = JSON.parseObject(res, ResponseVo.class); return responseVo; } }; //Whether the request is executed successfully or causes an exception, HttpClient will automatically ensure that the connection is released back to the connection manager. ResponseHandler<String> responseHandler = new BasicResponseHandler(); // String execute1 = closeableHttpClient.execute(httpPost, responseHandler); ResponseVo execute = closeableHttpClient.execute(httpPost, handler); log.info(JSON.toJSONString(execute)); }
2.7 request tools
Receive POST request:
public static String doPost(String url, Object paramsObj, int socketTimeout) throws IOException { HttpPost post = new HttpPost(url); StringEntity entity = new StringEntity(JSONObject.toJSONString(paramsObj), "UTF-8"); post.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE); post.setEntity(entity); return doHttp(post, socketTimeout); }
Receive GET request:
public static String doGet(String url, Map<String, String> params, int socketTimeout) throws IOException, URISyntaxException { URIBuilder uriBuilder = new URIBuilder(url); uriBuilder.setCharset(Consts.UTF_8).build(); if (params != null) { params.forEach(uriBuilder::addParameter); } HttpGet httpGet = new HttpGet(uriBuilder.build()); //Set request header httpGet.addHeader(HttpHeaders.CONTENT_TYPE, "text/html;charset=UTF-8"); return doHttp(httpGet, socketTimeout); }
Public processing class:
private static String doHttp(HttpRequestBase request, int socketTimeout) throws IOException { //Set timeout if (socketTimeout > 0) { //Get the original configuration //Actual injection type org apache. http. impl. client. InternalHttpClient Configurable configClient = (Configurable) httpClient; RequestConfig.Builder custom = RequestConfig.copy(configClient.getConfig()); //Set personalized configuration RequestConfig config = custom.setSocketTimeout(socketTimeout).build(); request.setConfig(config); } ResponseHandler<String> handler = new BasicResponseHandler(); long startPoint = System.currentTimeMillis(); String response = httpClient.execute(request, handler); log.info("Request time[{}], Interface return information[{}]", System.currentTimeMillis() - startPoint, response); return response; }
Two ways to pass parameters in http post method
Appendix:
Appendix code reference, spring boot integration HttpClient
httpClient configuration:
@Configuration public class HttpClientConfig { @Autowired private HttpClientProperties httpClientProperties; /** * Display and modify the httpClient connection pool parameters. Note: if the settings are not displayed, there should be a default configuration! * * @return */ @Bean public PoolingHttpClientConnectionManager poolingHttpClientConnectionManager() { //The created object has been set: the processing Socket link factory object corresponding to Protocol Http and Https. PoolingHttpClientConnectionManager httpClientConnectionManager = new PoolingHttpClientConnectionManager(); httpClientConnectionManager.setDefaultMaxPerRoute(httpClientProperties.getDefaultMaxPerRoute()); httpClientConnectionManager.setMaxTotal(httpClientProperties.getMaxTotal()); httpClientConnectionManager.setValidateAfterInactivity(httpClientProperties.getValidateAfterInactivity()); return httpClientConnectionManager; } //Set up network Configurator @Bean public RequestConfig requestConfig(){ return RequestConfig.custom().setConnectionRequestTimeout(httpClientProperties.getConnectionRequestTimeout()) //Timeout for getting connections from link pool .setConnectTimeout(httpClientProperties.getConnectTimeout()) //Timeout time of connecting with the server, timeout time of creating socket connection .setSocketTimeout(httpClientProperties.getSocketTimeout()) //Timeout of socket reading data and timeout of obtaining data from server // . setsockettimeout (1) / / timeout of socket reading data and timeout of obtaining data from the server // . setExpectContinueEnabled(true) / / set whether to enable the client. Before sending the Request Message, judge whether the server is willing to accept the message body sent by the client .build(); } /** * Instantiate the connection pool and set the connection pool manager * * @param poolingHttpClientConnectionManager * @return */ @Bean public HttpClientBuilder httpClientBuilder(PoolingHttpClientConnectionManager poolingHttpClientConnectionManager) { HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); //Set up connection pool httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager); //Set timeout httpClientBuilder.setDefaultRequestConfig(requestConfig()); //Define that the connection manager will be shared by multiple client instances. If the connection manager is shared, its life cycle should be managed by the caller. If the client is closed, it will not be closed. httpClientBuilder.setConnectionManagerShared(true); //Set keep alive ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy() { public long getKeepAliveDuration(HttpResponse response, HttpContext context) { // Honor 'keep-alive' header HeaderElementIterator it = new BasicHeaderElementIterator( response.headerIterator(HTTP.CONN_KEEP_ALIVE)); while (it.hasNext()) { HeaderElement he = it.nextElement(); String param = he.getName(); String value = he.getValue(); if (value != null && param.equalsIgnoreCase("timeout")) { try { return Long.parseLong(value) * 1000; } catch(NumberFormatException ignore) { } } } HttpHost target = (HttpHost) context.getAttribute( HttpClientContext.HTTP_TARGET_HOST); if ("www.naughty-server.com".equalsIgnoreCase(target.getHostName())) { // Keep alive for 5 seconds only return 5 * 1000; } else { // otherwise keep alive for 30 seconds return 30 * 1000; } } }; httpClientBuilder.setKeepAliveStrategy(myStrategy); // httpClientBuilder.setRetryHandler(new MyHttpRequestRetryHandler()); // httpClientBuilder.disableAutomaticRetries(); new IdleConnectionMonitorThread(poolingHttpClientConnectionManager).start();//Start the thread and empty the failed connection once in 5 seconds return httpClientBuilder; } @Bean public CloseableHttpClient getCloseableHttpClient(HttpClientBuilder httpClientBuilder) { return httpClientBuilder.build(); } }
Scheduled cleanup thread
@Slf4j public class IdleConnectionMonitorThread extends Thread { private final HttpClientConnectionManager connMgr; private volatile boolean shutdown; public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr) { super(); this.connMgr = connMgr; } @Override public void run() { try { while (!shutdown) { synchronized (this) { wait(5000); log.info("[Scheduled clearing of expired connections starts...]"); // Close timed out connections connMgr.closeExpiredConnections(); // Close connections with idle time greater than 30s connMgr.closeIdleConnections(30, TimeUnit.SECONDS); } } } catch (InterruptedException ex) { // terminate } } public void shutdown() { shutdown = true; synchronized (this) { notifyAll(); } } }
spring: http-pool: # Maximum connections in connection pool max-total: 3000 # Maximum number of connections per round request default-max-per-route: 20 # How long (in milliseconds) is idle to verify the validity of the connection validate-after-inactivity: 2000 # Maximum timeout for establishing a connection (MS) connect-timeout: 20000 # Gets the maximum timeout (in milliseconds) for a connection connection-request-timeout: 20000 # The maximum time (in milliseconds) to remain connected to the server socket-timeout: 20000
@ConfigurationProperties(prefix = "spring.http-pool") public class HttpClientProperties { //Default configuration private int defaultMaxPerRoute = 2; private int maxTotal = 20; private int validateAfterInactivity = 2000; private int connectTimeout = 2000; private int connectionRequestTimeout = 20000; private int socketTimeout = 20000; }
Tools:
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.TypeReference; import lombok.extern.slf4j.Slf4j; import org.apache.http.Consts; import org.apache.http.Header; import org.apache.http.HttpHeaders; import org.apache.http.client.ResponseHandler; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.*; import org.apache.http.client.utils.URIBuilder; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.BasicResponseHandler; import org.apache.http.impl.client.CloseableHttpClient; import org.springframework.beans.BeanUtils; import org.springframework.http.MediaType; import org.springframework.web.client.RestTemplate; import java.io.IOException; import java.net.URISyntaxException; import java.util.List; import java.util.Map; import java.util.Set; /** * @program: springboot * @description: httpClient Communication tools * @author: xueruiye * @create: 2019-08-13 17:18 * <p> * Note: set the tool class of httpClient. Provides static methods for get and post access. * get Request content type = = text / HTML; charset=UTF-8 * post Request content type = Application / JSON; charset=UTF-8 * You can flexibly set socket timeout (socket connection time, i.e. timeout, unit: Ms!) */ @Slf4j public class HttpClientUtils { private static CloseableHttpClient httpClient = SpringContextUtil.getBean("customCloseableHttpClient", CloseableHttpClient.class); /** * get Request content type = = text / HTML; charset=UTF-8 * * @param url url address * @param paramsObj params Object object composed of parameters * @return * @throws IOException * @throws URISyntaxException */ public static <T> String doGet(String url, Object paramsObj) throws IOException, URISyntaxException { Map<String, String> params = JSON.parseObject(JSON.toJSONString(paramsObj), Map.class); return doGet(url, params, -1); } public static <T> String doGet(String url, Object paramsObj, int socketTimeout) throws IOException, URISyntaxException { Map<String, String> params = JSON.parseObject(JSON.toJSONString(paramsObj), Map.class); return doGet(url, params, socketTimeout); } /** * post The call uses the timeout configured in the configuration file * * @param url Request address * @param paramsObj Request entity * @param responseType Request content example: new typereference < list < account > > () {} * @param <T> * @return * @throws IOException */ public static <T> T doPost(String url, Object paramsObj, TypeReference<T> responseType) throws IOException { return doPost(url, paramsObj, responseType, -1); } public static String doPost(String url, Object paramsObj) throws IOException { return doPost(url, paramsObj, -1); } /** * post Request content type = Application / JSON; charset=UTF-8 * * @param url url address * @param paramsObj Request parameter field * @param responseType Response object type * @param socketTimeout Timeout * @param <T> * @return Content corresponding to response entity * @throws IOException */ public static <T> T doPost(String url, Object paramsObj, TypeReference<T> responseType, int socketTimeout) throws IOException { String responseContent = doPost(url, paramsObj, socketTimeout); if (StringUtils.isBlank(responseContent)) { return null; } T response = JSONObject.parseObject(responseContent, responseType); return response; } /** * @param url * @param paramsObj * @param socketTimeout * @return * @throws IOException */ public static String doPost(String url, Object paramsObj, int socketTimeout) throws IOException { HttpPost post = new HttpPost(url); //If a String type object is submitted, no String type conversion is required String paramsStr = paramsObj instanceof String ? (String) paramsObj : JSONObject.toJSONString(paramsObj); StringEntity entity = new StringEntity(paramsStr, "UTF-8"); post.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE); post.setEntity(entity); return doHttp(post, socketTimeout); } /** * get Request content type = = text / HTML; charset=UTF-8 * * @param url url address * @param params params Map object composed of parameters * @return * @throws IOException * @throws URISyntaxException */ public static String doGet(String url, Map<String, String> params) throws IOException, URISyntaxException { return doGet(url, params, -1); } public static String doGet(String url, Map<String, String> params, int socketTimeout) throws IOException, URISyntaxException { URIBuilder uriBuilder = new URIBuilder(url); uriBuilder.setCharset(Consts.UTF_8).build(); if (params != null) { // Set<String> keys = params.keySet(); // for (String key : keys) { // uriBuilder.addParameter(key, params.get(key)); // } params.forEach(uriBuilder::addParameter); } HttpGet httpGet = new HttpGet(uriBuilder.build()); //Set request header httpGet.addHeader(HttpHeaders.CONTENT_TYPE, "text/html;charset=UTF-8"); return doHttp(httpGet, socketTimeout); } /** * Actually call remote methods * * @param request httpGet/httpPost Common parent of * @param socketTimeout Timeout * @return * @throws IOException */ private static String doHttp(HttpRequestBase request, int socketTimeout) throws IOException { //Set timeout if (socketTimeout > 0) { //Get the original configuration //Actual injection type org apache. http. impl. client. InternalHttpClient Configurable configClient = (Configurable) httpClient; RequestConfig.Builder custom = RequestConfig.copy(configClient.getConfig()); //Set personalized configuration RequestConfig config = custom.setSocketTimeout(socketTimeout).build(); request.setConfig(config); } ResponseHandler<String> handler = new BasicResponseHandler(); long startPoint = System.currentTimeMillis(); String response = httpClient.execute(request, handler); log.info("Request time[{}], Interface return information[{}]", System.currentTimeMillis() - startPoint, response); return response; } }
Article reference
1. Official documents
Class PoolingHttpClientConnectionManager official website API document
Class RequestConfig official website API document
Class HttpClientBuilder official API document
Official API document of apache connection pool
PoolingHttpClientConnectionManager for httpclient source code analysis
2. Related blogs
Using PoolingHttpClientConnectionManager to solve friend
http and https examples of post requests in HttpClient
Http request connection pool - HttpClient connection pool
Detailed explanation of three timeouts in HttpClient
Understand expect: 100 continue in HTTP protocol
java. Lang. IllegalStateException: solution to connection pool shutdown
httpclient parameter configuration
Optimized use of httpClient in high concurrency scenarios
Author: xiaopang learns programming
Link: https://www.jianshu.com/p/e77e9e126f89
Source: Jianshu
The copyright belongs to the author. For commercial reprint, please contact the author for authorization. For non-commercial reprint, please indicate the source.