httpClient連接池創(chuàng)建邏輯:
- org.springframework.boot.web.client.ClientHttpRequestFactorySupplier
我們知道restTemplate只是一個(gè)模板類叫潦,具體的httpClent還是需要各種jar包類提供箱蟆,下面的類就是一個(gè)選擇httpClient的具體實(shí)現(xiàn)過(guò)程斑粱,以及創(chuàng)建對(duì)應(yīng)的工廠類:
// ClientHttpRequestFactory的供應(yīng)商欧啤,根據(jù)類路徑上的可用實(shí)現(xiàn)檢測(cè)首選候選
public class ClientHttpRequestFactorySupplier implements Supplier<ClientHttpRequestFactory> {
private static final Map<String, String> REQUEST_FACTORY_CANDIDATES;
static {
Map<String, String> candidates = new LinkedHashMap<>();
candidates.put("org.apache.http.client.HttpClient","org.springframework.http.client.HttpComponentsClientHttpRequestFactory");
candidates.put("okhttp3.OkHttpClient","org.springframework.http.client.OkHttp3ClientHttpRequestFactory");
REQUEST_FACTORY_CANDIDATES = Collections.unmodifiableMap(candidates);
}
@Override
public ClientHttpRequestFactory get() {
for (Map.Entry<String, String> candidate : REQUEST_FACTORY_CANDIDATES.entrySet()) {
ClassLoader classLoader = getClass().getClassLoader();
if (ClassUtils.isPresent(candidate.getKey(), classLoader)) {
Class<?> factoryClass = ClassUtils.resolveClassName(candidate.getValue(), classLoader);
return (ClientHttpRequestFactory) BeanUtils.instantiateClass(factoryClass);
}
}
return new SimpleClientHttpRequestFactory();
}
}
此類通過(guò)定義好的類路徑去尋找最優(yōu)的httpClient提供商,分別按順序通過(guò)反射去篩選是否能找到apache.httpClient和okhttp的類兑宇,如果能找到apache.httpClient則去實(shí)例化對(duì)應(yīng)的ClientHttpRequestFactory類碍侦,如果都找不到則創(chuàng)建默認(rèn)的SimpleClientHttpRequestFactory類,此工廠中使用了默認(rèn)的jdk.net包。對(duì)應(yīng)的okhttp也有自己的工廠類OkHttp3ClientHttpRequestFactory瓷产。
- org.springframework.http.client.HttpComponentsClientHttpRequestFactory:74
/**
* 根據(jù)系統(tǒng)屬性比规,使用默認(rèn)的HttpClient創(chuàng)建HttpComponentsClientHttpRequestFactory的新實(shí)例
* Create a new instance of the {@code HttpComponentsClientHttpRequestFactory}
* with a default {@link HttpClient} based on system properties.
*/
public HttpComponentsClientHttpRequestFactory() {
this.httpClient = HttpClients.createSystem();
}
此類是spring為restTemplate提供的類,滿足了restTemplate使用HttpClient的需求
構(gòu)造方法中調(diào)用了HttpClient的createSystem方法創(chuàng)建一個(gè)HttpClent實(shí)體拦英,此構(gòu)造方法在spring實(shí)例化restTemplate時(shí)會(huì)被調(diào)用蜒什。
- org.apache.http.impl.client.HttpClients:63
/**
* 使用基于系統(tǒng)屬性的默認(rèn)配置創(chuàng)建CloseableHttpClient實(shí)例
* Creates {@link CloseableHttpClient} instance with default
* configuration based on system properties.
*/
public static CloseableHttpClient createSystem() {
return HttpClientBuilder.create().useSystemProperties().build();
}
此類是httpClient提供的工廠方法類,提供了多種創(chuàng)建httpClient實(shí)例的工廠方法
調(diào)用了HttpClientBuilder的create()方法疤估,默認(rèn)使用系統(tǒng)屬性灾常,調(diào)用了useSystemProperties()方法
- org.apache.http.impl.client.HttpClientBuilder
public static HttpClientBuilder create() {
return new HttpClientBuilder();
}
// 在創(chuàng)建和配置默認(rèn)實(shí)現(xiàn)時(shí)使用系統(tǒng)屬性
public final HttpClientBuilder useSystemProperties() {
this.systemProperties = true;
return this;
}
此類是CloseableHttpClient實(shí)例的構(gòu)建器
其中最主的是build()方法,對(duì)各種參數(shù)進(jìn)行了設(shè)置铃拇,其中一些重要部分的設(shè)置代碼如下所示:
:
// 連接池初始化 調(diào)用PoolingHttpClientConnectionManager構(gòu)造方法創(chuàng)建連接池
final PoolingHttpClientConnectionManager poolingmgr = new PoolingHttpClientConnectionManager(
RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", sslSocketFactoryCopy)
.build(),
null,
null,
dnsResolver,
connTimeToLive,
connTimeToLiveTimeUnit != null ? connTimeToLiveTimeUnit : TimeUnit.MILLISECONDS);
if (defaultSocketConfig != null) {
poolingmgr.setDefaultSocketConfig(defaultSocketConfig);
}
if (defaultConnectionConfig != null) {
poolingmgr.setDefaultConnectionConfig(defaultConnectionConfig);
}
// httpClient默認(rèn)連接數(shù)量的設(shè)置
// 其中systemProperties參數(shù)代表創(chuàng)建的httpClient是否使用系統(tǒng)屬性钞瀑,在上面的useSystemProperties()方法中設(shè)置為true
// 如果 http.keepAlive(默認(rèn)值為true) 為true,則會(huì)將 http.maxConnections(默認(rèn)值為5) 屬性的值設(shè)置為同一個(gè)路由最多可以使用的連接數(shù)慷荔,而連接池的最大連接數(shù)量為同一個(gè)路由最多使用連接數(shù)量的兩倍
if (systemProperties) {
String s = System.getProperty("http.keepAlive", "true");
if ("true".equalsIgnoreCase(s)) {
s = System.getProperty("http.maxConnections", "5");
final int max = Integer.parseInt(s);
poolingmgr.setDefaultMaxPerRoute(max);
poolingmgr.setMaxTotal(2 * max);
}
}
if (maxConnTotal > 0) {
poolingmgr.setMaxTotal(maxConnTotal);
}
if (maxConnPerRoute > 0) {
poolingmgr.setDefaultMaxPerRoute(maxConnPerRoute);
}
connManagerCopy = poolingmgr;
org.apache.http.impl.conn.PoolingHttpClientConnectionManager
public PoolingHttpClientConnectionManager(
final Registry<ConnectionSocketFactory> socketFactoryRegistry,
final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory,
final SchemePortResolver schemePortResolver,
final DnsResolver dnsResolver,
final long timeToLive, final TimeUnit timeUnit) {
this(
new DefaultHttpClientConnectionOperator(socketFactoryRegistry,
schemePortResolver, dnsResolver),
connFactory,
timeToLive, timeUnit
);
}
// 真正創(chuàng)建創(chuàng)建連接池的構(gòu)造方法
public PoolingHttpClientConnectionManager(
final HttpClientConnectionOperator httpClientConnectionOperator,
final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory,
final long timeToLive, final TimeUnit timeUnit) {
super();
this.configData = new ConfigData();
// 設(shè)置默認(rèn)每個(gè)路由最大使用兩個(gè)連接雕什,連接池總共有20個(gè)連接
this.pool = new CPool(new InternalConnectionFactory(this.configData, connFactory), 2, 20, timeToLive, timeUnit);
this.pool.setValidateAfterInactivity(2000);
this.connectionOperator = Args.notNull(httpClientConnectionOperator, "HttpClientConnectionOperator");
this.isShutDown = new AtomicBoolean(false);
}
org.apache.http.impl.conn.DefaultHttpClientConnectionOperator:
public DefaultHttpClientConnectionOperator(
final Lookup<ConnectionSocketFactory> socketFactoryRegistry,
final SchemePortResolver schemePortResolver,
final DnsResolver dnsResolver) {
super();
Args.notNull(socketFactoryRegistry, "Socket factory registry");
this.socketFactoryRegistry = socketFactoryRegistry;
this.schemePortResolver = schemePortResolver != null ? schemePortResolver :
DefaultSchemePortResolver.INSTANCE;
this.dnsResolver = dnsResolver != null ? dnsResolver : SystemDefaultDnsResolver.INSTANCE;
}