异常问题:
- 原因:httpclient 之前与服务端建立的链接已经失效(例如:tomcat 默认的keep-alive timeout :20s),再次从连接池拿该失效链接进行请求时,就会保存。
- 解决方法:官方链接:http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d5e659
- 上面官方链接的2.6 解决方法的代码如果报错,可能是自己的httpclient版本 不适用。自己用的是httpclient 4.0.1,使用以下代码绿色代码:
-
import com.google.api.client.http.ByteArrayContent; import com.google.api.client.http.GenericUrl; import com.google.api.client.http.HttpBackOffUnsuccessfulResponseHandler; import com.google.api.client.http.HttpContent; import com.google.api.client.http.HttpHeaders; import com.google.api.client.http.HttpRequest; import com.google.api.client.http.HttpRequestFactory; import com.google.api.client.http.HttpResponse; import com.google.api.client.http.HttpStatusCodes; import com.google.api.client.http.HttpTransport; import com.google.api.client.http.apache.ApacheHttpTransport; import com.google.api.client.util.BackOff; import java.io.IOException; import java.io.InputStream; import java.net.ProxySelector; import java.util.Map; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.TimeUnit; import javax.annotation.PreDestroy; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.apache.http.HeaderElement; import org.apache.http.HeaderElementIterator; import org.apache.http.HttpHost; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.conn.ConnectionKeepAliveStrategy; import org.apache.http.conn.params.ConnManagerParams; import org.apache.http.conn.params.ConnPerRouteBean; import org.apache.http.conn.scheme.PlainSocketFactory; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.client.DefaultHttpRequestRetryHandler; import org.apache.http.impl.conn.ProxySelectorRoutePlanner; import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; import org.apache.http.message.BasicHeaderElementIterator; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; import org.apache.http.protocol.HTTP; import org.apache.http.protocol.HttpContext; /** * @author Li Sheng */ @Slf4j public class HttpClientUtils { private static HttpRequestFactory requestFactory; private static HttpTransport httpTransport; private static final String CONTENT_TYPE_JSON = "application/json"; private static final int CACHE_SIZE = 4096; static { HttpParams params = new BasicHttpParams(); HttpConnectionParams.setStaleCheckingEnabled(params, false); HttpConnectionParams.setSocketBufferSize(params, 245760); // 8k(8192) * 30 ConnManagerParams.setMaxTotalConnections(params, 400); ConnManagerParams.setMaxConnectionsPerRoute(params, new ConnPerRouteBean(200)); SchemeRegistry registry = new SchemeRegistry(); registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); registry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443)); ClientConnectionManager connectionManager = new ThreadSafeClientConnManager(params, registry); DefaultHttpClient defaultHttpClient = new DefaultHttpClient(connectionManager, params); defaultHttpClient.setHttpRequestRetryHandler(new DefaultHttpRequestRetryHandler(0, false)); defaultHttpClient .setRoutePlanner(new ProxySelectorRoutePlanner(registry, ProxySelector.getDefault())); ConnectionKeepAliveStrategy connectionKeepAliveStrategy = new ConnectionKeepAliveStrategy() { @Override public long getKeepAliveDuration(org.apache.http.HttpResponse httpResponse, HttpContext httpContext) { return 20 * 1000; // 20 seconds,because tomcat default keep-alive timeout is 20s } }; defaultHttpClient.setKeepAliveStrategy(connectionKeepAliveStrategy); httpTransport = new ApacheHttpTransport(defaultHttpClient); requestFactory = httpTransport.createRequestFactory(); } @Data public static class PostParam { private Integer connectTimeoutMills; // 可选,默认 20s private Integer readTimeoutMills; // 可选,默认 20s private Map<String, String> headers; // 可选 private String url; //必填 private String postJson; //必填 private Boolean readResponseData; //必填:是否需要读取数据。如果不需要返回结果,设置 false private BackOff backOff; //可选,重试机制策略 private String authorization; //可选 public PostParam(String url, String postJson, boolean readResponseData) { this.url = url; this.postJson = postJson; this.readResponseData = readResponseData; } } public static String postWithJson(PostParam postParam) { GenericUrl genericUrl = new GenericUrl(postParam.getUrl()); HttpContent httpContent = ByteArrayContent.fromString(null, postParam.getPostJson()); HttpResponse httpResponse = null; try { HttpRequest httpRequest = requestFactory.buildPostRequest(genericUrl, httpContent); if (postParam.getConnectTimeoutMills() != null) { httpRequest.setConnectTimeout(postParam.getConnectTimeoutMills()); } if (postParam.getReadTimeoutMills() != null) { httpRequest.setReadTimeout(postParam.getReadTimeoutMills()); } if (postParam.getBackOff() != null) { httpRequest.setUnsuccessfulResponseHandler( new HttpBackOffUnsuccessfulResponseHandler(postParam.getBackOff())); } HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(CONTENT_TYPE_JSON); Map<String, String> headers = postParam.getHeaders(); if (headers != null && headers.size() > 0) { headers.forEach(httpHeaders::set); } if (postParam.getAuthorization() != null && !postParam.equals("")) { httpHeaders.setAuthorization(postParam.getAuthorization()); } httpRequest.setHeaders(httpHeaders); httpResponse = httpRequest.execute(); if (httpResponse.getStatusCode() != HttpStatusCodes.STATUS_CODE_OK) { log.error("http status not 200. param:{},status:{},msg:{}", postParam, httpResponse.getStatusCode(), httpResponse.getStatusMessage()); return null; } Boolean readResponseData = postParam.getReadResponseData(); if (readResponseData != null && readResponseData) { InputStream inputStream = httpResponse.getContent(); if (inputStream != null) { StringBuffer out = new StringBuffer(); byte[] b = new byte[CACHE_SIZE]; for (int n; (n = inputStream.read(b)) != -1; ) { out.append(new String(b, 0, n)); } return out.toString(); } } } catch (Exception e) { log.error("post exception,param:{}", postParam, e); } finally { try { if (httpResponse != null) { httpResponse.disconnect(); } } catch (Exception e) { log.error("httpResponse disconnect exception", e); } } return null; } @PreDestroy public static void destory() { try { log.info("httpTransport shutdown now...."); httpTransport.shutdown(); } catch (IOException e) { log.error("shut down httpTransport exception", e); } } }
如果想使用上面的 HttpClientUtils,必须引入 google-httpclient:
-
<dependency> <groupId>com.google.http-client</groupId> <artifactId>google-http-client</artifactId> <version>1.22.0</version> </dependency>