官網地址 http://hc.apache.org/
官方PDF地址: http://hc.apache.org/httpcomponents-core-ga/tutorial/pdf/httpcore-tutorial.pdf
HttpClient是基於HttpCore的HTTP / 1.1兼容HTTP代理實現。 它還為客戶端身份驗證,HTTP狀態管理和HTTP連接管理提供可重用的組件。 HttpComponents Client是Commons HttpClient 3.x的繼承者和替代者。 強烈建議Commons HttpClient用戶進行升級。
HttpClient 特性
- 基於標准的純Java,HTTP版本1.0和1.1的實現
- 在可擴展的OO框架中完全實現所有HTTP方法(GET,POST,PUT,DELETE,HEAD,OPTIONS和TRACE)。
- 支持使用HTTPS(基於SSL的HTTP)協議進行加密。
- 通過HTTP代理的透明連接。
- 通過CONNECT方法通過HTTP代理建立的HTTPS連接隧道。
- 基本,摘要,NTLMv1,NTLMv2,NTLM2會話,SNPNEGO,Kerberos身份驗證方案。
- 用於自定義身份驗證方案的插件機制。
- 可插拔的安全套接字工廠,使使用第三方解決方案更加容易
- 在多線程應用程序中使用的連接管理支持。支持設置
- 最大總連接數以及每個主機的最大連接數。檢測並關閉陳舊的連接。
- 自動Cookie處理,用於從服務器讀取Set-Cookie:標頭,並在適當時在Cookie:標頭中發回。
- 自定義Cookie策略的插件機制。
- 請求輸出流,以避免通過直接流到服務器的套接字來緩沖任何內容主體。
- 響應輸入流,通過直接從套接字流傳輸到服務器來有效讀取響應主體。
- 在HTTP / 1.0中使用KeepAlive的持久連接和在HTTP / 1.1中的持久性
- 直接訪問服務器發送的響應代碼和標頭。
- 設置連接超時的能力。
- 支持HTTP / 1.1響應緩存。
Asynch HttpClient是基於HttpCore NIO和HttpClient組件的HTTP / 1.1兼容HTTP代理實現。 它是Apache HttpClient的補充模塊,適用於特殊情況,在特殊情況下,就原始數據吞吐量而言,處理大量並發連接的能力比性能更為重要。
HttpAsyncClient特性
- 基於標准的純Java,HTTP版本1.0和1.1的實現
- 在可擴展的OO框架中完全實現所有HTTP方法(GET,POST,PUT,DELETE,HEAD,OPTIONS和TRACE)。
- 支持使用HTTPS(基於SSL的HTTP)協議進行加密。
- 通過HTTP代理的透明連接。\
- 通過CONNECT方法通過HTTP代理建立的HTTPS連接隧道。
- 連接管理支持並發請求執行。支持設置最大總連接數以及每個主機的最大連接數。檢測並關閉過期的連接。
- 在HTTP / 1.0中使用KeepAlive的持久連接和在HTTP / 1.1中的持久性
- 設置連接超時的能力。
- 源代碼可根據Apache許可免費獲得。
- 基本,摘要,NTLMv1,NTLMv2,NTLM2會話,SNPNEGO和Kerberos身份驗證方案。
- 用於自定義身份驗證方案的插件機制。
- 自動Cookie處理,用於從服務器讀取Set-Cookie:標頭,並在適當時在Cookie:標頭中發回。
- 自定義Cookie策略的插件機制。
- 支持HTTP / 1.1響應緩存。
- 支持流水線的請求執行和處理。
前言
超文本傳輸協議(HTTP)可能是當今Internet上使用的最重要的協議。 Web服務,支持網絡的設備和網絡計算的增長繼續將HTTP協議的作用擴展到用戶驅動的Web瀏覽器之外,同時增加了需要HTTP支持的應用程序的數量。
盡管java.net軟件包提供了用於通過HTTP訪問資源的基本功能,但它並未提供許多應用程序所需的全部靈活性或功能。 HttpClient試圖填補這一點通過提供高效,最新且功能豐富的軟件包來實現最新的HTTP標准和建議的客戶端,從而使此方法無效
HttpClient是為擴展而設計的,同時提供對基本HTTP協議的強大支持,對於構建HTTP感知的客戶端應用程序(例如Web瀏覽器,Web服務)的任何人來說,HttpClient可能都會感興趣客戶端或利用或擴展HTTP協議進行分布式通信的系統。
-
HttpClient作用域
-
基於HttpCore的客戶端HTTP傳輸庫
-
基於經典(阻塞)I / O
-
內容不可知
-
HttpClient不是什么
HttpClient不是瀏覽器。 它是客戶端HTTP傳輸庫。 HttpClient的目的是傳輸和接收HTTP消息,HttpClient將不會嘗試處理內容,執行HTML頁面中嵌入的javascript,嘗試猜測內容類型(如果未顯式設置),重新格式化請求/重寫位置URI或其他與HTTP傳輸無關的功能。
最新Pom引入
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpasyncclient</artifactId>
<version>4.1.4</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.10</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.10</version>
</dependency>
HttpClient連接池簡單封裝
支持https 無須認證請求
import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.DnsResolver;
import org.apache.http.conn.HttpConnectionFactory;
import org.apache.http.conn.ManagedHttpClientConnection;
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.TrustStrategy;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.DefaultHttpResponseParserFactory;
import org.apache.http.impl.conn.ManagedHttpClientConnectionFactory;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.conn.SystemDefaultDnsResolver;
import org.apache.http.impl.io.DefaultHttpRequestWriterFactory;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.Serializable;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;
/**
* @author Created by niugang on 2020/3/25/15:13
* HttpClient實現應該是線程安全的。 建議將此類的相同實例重用於多個請求執行。
*/
public class HttpClientV2Utils {
private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientV2Utils.class);
public static CloseableHttpClient httpClient = null;
/**
* 獲取HttpClient工具類
*
* @return CloseableHttpClient
* @throws Exception Exception
*/
public static synchronized CloseableHttpClient getHttpClient() throws Exception {
if (httpClient == null) {
//使用 loadTrustMaterial() 方法實現一個信任策略,信任所有證書
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
// 信任所有
@Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
}).build();
// 該主機名驗證程序實際上關閉了主機名驗證。 它接受任何有效的SSL會話並匹配目標主機
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
//為支持的協議方案創建自定義連接套接字工廠的注冊表。
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.INSTANCE)
.register("https", sslsf)
.build();
//HTTPConnection工廠 :配置請求/解析響應
HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory = new ManagedHttpClientConnectionFactory(
DefaultHttpRequestWriterFactory.INSTANCE, DefaultHttpResponseParserFactory.INSTANCE);
//DNS解析器
DnsResolver dnsResolver = SystemDefaultDnsResolver.INSTANCE;
//創建池化管理器
PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager(
socketFactoryRegistry, connFactory, dnsResolver);
// 默認為Socket配置
SocketConfig socketConfig = SocketConfig.custom()
.setTcpNoDelay(true)
.build();
manager.setDefaultSocketConfig(socketConfig);
// 配置永久連接的最大總數或每個路由限制
// 可以保留在池中或由連接管理器租用。
//每個路由的默認最大連接,每個路由實際最大連接為默認為DefaultMaxPreRoute控制,而MaxTotal是控制整個池子最大數
manager.setMaxTotal(100);
manager.setDefaultMaxPerRoute(10);
// 在從連接池獲取連接時,連接不活躍多長時間后需要進行一次驗證,默認為2s
manager.setValidateAfterInactivity(5 * 1000);
//默認請求配置
//設置連接超時時間 4s
//設置等待數據超時時間,5s
//設置從連接池獲取連接的等待超時時間
RequestConfig defaultRequestConfig = RequestConfig.custom()
.setConnectTimeout(4 * 1000)
.setSocketTimeout(5 * 1000)
.setConnectionRequestTimeout(2000)
.build();
httpClient = HttpClients.custom()
.setConnectionManager(manager)
.setDefaultRequestConfig(defaultRequestConfig)
//連接池不是共享模式
.setConnectionManagerShared(false)
.evictIdleConnections(60, TimeUnit.SECONDS)
//定期回收空閑連接
.evictExpiredConnections()
.setConnectionTimeToLive(60, TimeUnit.SECONDS)
//連接重用策略,即是否能keeplive
.setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE)
//長連接配置,即獲取長連接生產多長時間
.setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE)
//設置重試次數,默認為3次;當前禁用掉(根據需要重啟)
.setRetryHandler(new DefaultHttpRequestRetryHandler(0, false))
.build();
// jvm 停止或重啟時,關閉連接池釋放連接資源(跟數據庫連接池類似)
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
try {
httpClient.close();
} catch (IOException e) {
LOGGER.error("HttpClient close exception", e);
}
}
});
}
return httpClient;
}
/**
* get方法封裝
* <p>
* 要使用 EntityUtils.consume(response.getEntity());或者EntityUtils.toString(response.getEntity());
* 消費響應,不推薦HttpEntity.getContent.close方法來釋放連接,處理不好異常將導致連接不釋放,也不推薦CloseableHttpClient#close關閉連接,它將直接關閉
* socket,導致長連接不能復用
*
* @param httpGet httpGet
* @return String
*/
private static String doGet(HttpGet httpGet) {
HttpResponse response = null;
try {
response = getHttpClient().execute(httpGet);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
if (response.getEntity() != null) {
EntityUtils.consume(response.getEntity());
}
LOGGER.info("GET execute exception [{}]->[{}]", response.getStatusLine(), response.getStatusLine().getReasonPhrase());
return "error";
} else {
return EntityUtils.toString(response.getEntity());
}
} catch (Exception e) {
if (response != null) {
try {
EntityUtils.consume(response.getEntity());
} catch (IOException e1) {
LOGGER.info("GET consume response entity exception", e);
}
}
}
return "error";
}
public static void main(String[] args) throws Exception {
HttpGet httpsget = new HttpGet("https://11.12.115.104:8443/bs/gateway/get.do");
httpsget.setHeader("token", "c50e18bc01a62d395a9755b523bc2e50");
String s = doGet(httpsget);
LOGGER.info("doGet :{}", s);
//####1.HTTPS get請求驗證 方式二
URI uri = new URIBuilder()
.setScheme("https")
.setHost("11.12.115.104")
.setPort(8443)
.setPath("/bs/gateway/get.do")
.build();
HttpGet httpsget1 = new HttpGet(uri);
httpsget1.setHeader("token", "12fd6749989c7aa60ef34c8d53c0cf71");
// 自定義響應處理器
ResponseHandler<String> responseHandler = new ResponseHandler<String>() {
@Override
public String handleResponse(
final HttpResponse response) throws IOException {
int status = response.getStatusLine().getStatusCode();
if (status >= 200 && status < 300) {
HttpEntity entity = response.getEntity();
return entity != null ? EntityUtils.toString(entity, StandardCharsets.UTF_8) : null;
} else {
throw new ClientProtocolException("Unexpected response status: " + status + "->" + response.getStatusLine().getReasonPhrase());
}
}
};
String execute = getHttpClient().execute(httpsget1, responseHandler);
LOGGER.info("https get excute result {}", execute);
//####2.Https Post請求驗證 請求體為json數據
JSONObject jsonObject = new JSONObject();
jsonObject.put("user", "44444");
jsonObject.put("ip", "192.168.1.3");
jsonObject.put("status", "0");
ByteArrayEntity byteArrayEntity = new ByteArrayEntity(jsonObject.toJSONString().getBytes(), ContentType.create("application/json", "UTF-8"));
HttpPost httpspost = new HttpPost("https://11.12.115.104:8443/accessControl/saveDevice.do");
httpspost.setHeader("token", "12fd6749989c7aa60ef34c8d53c0cf71");
httpspost.setEntity(byteArrayEntity);
String httpsGostExecute = getHttpClient().execute(httpspost, responseHandler);
LOGGER.info("https post excute result {}", httpsGostExecute);
//####3.http get
HttpGet httpGet = new HttpGet("http://www.baidu.com");
String httpGetExecute = getHttpClient().execute(httpGet, responseHandler);
LOGGER.info("http get excute result {}", httpGetExecute);
}
}
HttpAsyncClient連接池簡單封裝
package com.example.demo.utils;
import lombok.SneakyThrows;
import org.apache.http.*;
import org.apache.http.client.ClientProtocolException;
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.concurrent.FutureCallback;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.DnsResolver;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.conn.SystemDefaultDnsResolver;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.impl.nio.codecs.DefaultHttpRequestWriterFactory;
import org.apache.http.impl.nio.codecs.DefaultHttpResponseParserFactory;
import org.apache.http.impl.nio.conn.ManagedNHttpClientConnectionFactory;
import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager;
import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
import org.apache.http.impl.nio.reactor.IOReactorConfig;
import org.apache.http.nio.conn.ManagedNHttpClientConnection;
import org.apache.http.nio.conn.NHttpConnectionFactory;
import org.apache.http.nio.conn.NoopIOSessionStrategy;
import org.apache.http.nio.conn.SchemeIOSessionStrategy;
import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
import org.apache.http.nio.reactor.ConnectingIOReactor;
import org.apache.http.nio.reactor.IOReactorException;
import org.apache.http.nio.util.HeapByteBufferAllocator;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.Future;
/**
* 此示例演示如何自定義和配置HTTP請求執行和連接管理的最常見方面。
*
* @author Created by niugang on 2020/3/24/11:25
*/
public class HttpAsyncClientUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(HttpAsyncClientUtils.class);
public static CloseableHttpAsyncClient httpclient = null;
/**
* 獲取 ttpAsyncClient
*
* @return CloseableHttpAsyncClient
* @throws KeyStoreException KeyStoreException
* @throws NoSuchAlgorithmException NoSuchAlgorithmException
* @throws IOReactorException IOReactorException
*/
public static synchronized CloseableHttpAsyncClient getHttpAsyncClient() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException, IOReactorException {
if (httpclient == null) {
// HTTPConnection工廠 :配置請求/解析響應
NHttpConnectionFactory<ManagedNHttpClientConnection> connFactory = new ManagedNHttpClientConnectionFactory(
DefaultHttpRequestWriterFactory.INSTANCE, DefaultHttpResponseParserFactory.INSTANCE, HeapByteBufferAllocator.INSTANCE);
//ssl 連接設置 無須證書也能訪問 https
//使用 loadTrustMaterial() 方法實現一個信任策略,信任所有證書
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
// 信任所有
@Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
}).build();
// 為支持的協議方案創建自定義連接套接字工廠的注冊表。
Registry<SchemeIOSessionStrategy> sessionStrategyRegistry = RegistryBuilder.<SchemeIOSessionStrategy>create()
.register("http", NoopIOSessionStrategy.INSTANCE)
.register("https", new SSLIOSessionStrategy(sslContext, NoopHostnameVerifier.INSTANCE))
.build();
//DNS解析器
DnsResolver dnsResolver = SystemDefaultDnsResolver.INSTANCE;
// Create I/O reactor configuration
IOReactorConfig ioReactorConfig = IOReactorConfig.custom()
.setIoThreadCount(Runtime.getRuntime().availableProcessors())
.setConnectTimeout(30000)
.setSoTimeout(30000)
.build();
// 創建一個定制的I/O reactort
ConnectingIOReactor ioReactor = new DefaultConnectingIOReactor(ioReactorConfig);
// 使用自定義配置創建連接管理器。
PoolingNHttpClientConnectionManager connManager = new PoolingNHttpClientConnectionManager(
ioReactor, connFactory, sessionStrategyRegistry, dnsResolver);
//創建連接配置
ConnectionConfig connectionConfig = ConnectionConfig.custom()
.setMalformedInputAction(CodingErrorAction.IGNORE)
.setUnmappableInputAction(CodingErrorAction.IGNORE)
.setCharset(Consts.UTF_8)
.build();
// 將連接管理器配置為默認使用或針對特定主機使用連接配置。
connManager.setDefaultConnectionConfig(connectionConfig);
// 配置永久連接的最大總數或每個路由限制
// 可以保留在池中或由連接管理器租用。
//每個路由的默認最大連接,每個路由實際最大連接為默認為DefaultMaxPreRoute控制,而MaxTotal是控制整個池子最大數
connManager.setMaxTotal(100);
connManager.setDefaultMaxPerRoute(10);
// 創建全局請求配置
RequestConfig defaultRequestConfig = RequestConfig.custom()
.setCookieSpec(CookieSpecs.DEFAULT)
.setSocketTimeout(5 * 1000)
.setConnectTimeout(5 * 1000)
.setExpectContinueEnabled(true)
.build();
// Create an HttpClientUtils with the given custom dependencies and configuration.
httpclient = HttpAsyncClients.custom()
.setConnectionManager(connManager)
.setDefaultRequestConfig(defaultRequestConfig)
.build();
//jvm 停止或重啟時,關閉連接池釋放連接資源(跟數據庫連接池類似)
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
try {
httpclient.close();
} catch (IOException e) {
LOGGER.error("HttpClient close exception", e);
}
}
});
}
return httpclient;
}
public static HttpResponse doGet(String url) {
CloseableHttpAsyncClient httpAsyncClient = null;
try {
final HttpGet httpget = new HttpGet(url);
LOGGER.info("Executing request {}", httpget.getRequestLine());
httpAsyncClient = getHttpAsyncClient();
httpAsyncClient.start();
// Pass local context as a parameter
Future<HttpResponse> future = httpAsyncClient.execute(httpget, new FutureCallback<HttpResponse>() {
@SneakyThrows
@Override
public void completed(final HttpResponse response2) {
LOGGER.info("{} -> {} completed", httpget.getRequestLine(), response2.getStatusLine());
}
@Override
public void failed(final Exception ex) {
LOGGER.error("{}->", httpget.getRequestLine(), ex);
}
@Override
public void cancelled() {
LOGGER.error("{} cancelled", httpget.getRequestLine());
}
});
//等待響應結果
return future.get();
} catch (Exception e) {
LOGGER.error("GET execute exception ", e);
} finally {
if (httpAsyncClient != null) {
//直接關閉socket,會導致長連接不能復用,所以返回Future,調用放調用get方法,將一直處於阻塞狀態
try {
httpAsyncClient.close();
} catch (IOException e) {
LOGGER.error("GET execute httpAsyncClient close exception ", e);
}
}
}
return null;
}
public static void main(String[] args) throws Exception {
HttpResponse response = doGet("https://11.12.115.104:8443/serverStatus/serverInfo");
if (response == null) {
return;
}
LOGGER.info("Response: {}", response.getStatusLine());
int status = response.getStatusLine().getStatusCode();
if (status >= 200 && status < 300) {
HttpEntity entity = response.getEntity();
String res = entity != null ? EntityUtils.toString(entity, StandardCharsets.UTF_8) : null;
LOGGER.info("result->{}", res);
} else {
throw new ClientProtocolException("Unexpected response status: " + status + "->" + response.getStatusLine().getReasonPhrase());
}
}
}
微信公眾號