java - 拥有signle实例Httpclient apache
问题描述
我想要一个初始化和处理 httpclient 调用(https)的类。我已经编写了代码,但我不知道它是否正确和最优。有没有更好的办法?我想缓存 ssl 调用一段时间以避免耗时的请求。有时它会返回 javax.net.ssl.SSLException: SSL peer closed wrongly 我想知道这是否与实现有关。这是代码:
import org.apache.http.HeaderElement;
import org.apache.http.HeaderElementIterator;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
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.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.protocol.HTTP;
import org.apache.http.ssl.SSLContextBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import javax.net.ssl.*;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Configuration
@EnableScheduling
public class HttpClientConfig {
private static final Logger logger = LoggerFactory.getLogger(HttpClientConfig.class);
// Determines the timeout in milliseconds until a connection is established.
private static final int CONNECT_TIMEOUT = 30000;
// The timeout when requesting a connection from the connection manager.
private static final int REQUEST_TIMEOUT = 30000;
// The timeout for waiting for data
private static final int SOCKET_TIMEOUT = 60000;
private static final int MAX_TOTAL_CONNECTIONS = 50;
private static final int DEFAULT_KEEP_ALIVE_TIME_MILLIS = 30 * 1000;
private static final int CLOSE_IDLE_CONNECTION_WAIT_TIME_SECS = 60;
private CloseableHttpClient client;
@Bean
public PoolingHttpClientConnectionManager poolingConnectionManager() {
SSLContextBuilder builder = new SSLContextBuilder();
try {
builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
} catch (NoSuchAlgorithmException | KeyStoreException e) {
logger.error("Pooling Connection Manager Initialization failure because of " + e.getMessage(), e);
}
SSLContext sslContext = null;
try {
TrustManager[] tm = new TrustManager[]{
new X509TrustManager() {
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
@Override
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
}
};
sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(getKeyManager(), tm, null);
} catch (Exception e) {
logger.error("Pooling Connection Manager Initialization failure because of " + e.getMessage(), e);
}
LayeredConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder
.<ConnectionSocketFactory>create().register("https", sslsf)
.register("http", new PlainConnectionSocketFactory())
.build();
PoolingHttpClientConnectionManager poolingConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
poolingConnectionManager.setMaxTotal(MAX_TOTAL_CONNECTIONS);
poolingConnectionManager.setDefaultMaxPerRoute(10);
return poolingConnectionManager;
}
@Bean
public ConnectionKeepAliveStrategy connectionKeepAliveStrategy() {
return (response, context) -> {
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")) {
return Long.parseLong(value) * 1000;
}
}
return DEFAULT_KEEP_ALIVE_TIME_MILLIS;
};
}
public KeyManager[] getKeyManager() {
KeyManager[] km = null;
try {
String pwd = "lala";
KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(
KeyManagerFactory.getDefaultAlgorithm());
kmfactory.init(getKeyStore(), pwd.toCharArray());
km = kmfactory.getKeyManagers();
} catch (Exception e) {
}
return km;
}
public KeyStore getKeyStore() {
.......
return ks;
}
@Bean
public CloseableHttpClient httpClient() throws KeyManagementException, NoSuchAlgorithmException {
RequestConfig requestConfig = RequestConfig.custom()
.setConnectionRequestTimeout(REQUEST_TIMEOUT)
.setConnectTimeout(CONNECT_TIMEOUT)
.setSocketTimeout(SOCKET_TIMEOUT).build();
SSLContext sslcontext = SSLContext.getInstance("TLSv1.2");
sslcontext.init(getKeyManager(), null, null);
if (client==null) {
client = HttpClients.custom()
.setDefaultRequestConfig(requestConfig)
.setConnectionManager(poolingConnectionManager()).setMaxConnPerRoute(10)
.setKeepAliveStrategy(connectionKeepAliveStrategy()).
setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE).
setSSLContext(sslcontext)
.build();
}
return client;
}
@Bean
public Runnable idleConnectionMonitor(final PoolingHttpClientConnectionManager connectionManager) {
return new Runnable() {
@Override
@Scheduled(fixedDelay = 80000)
public void run() {
try {
if (connectionManager != null) {
logger.trace("run IdleConnectionMonitor - Closing expired and idle connections...");
connectionManager.closeExpiredConnections();
connectionManager.closeIdleConnections(CLOSE_IDLE_CONNECTION_WAIT_TIME_SECS, TimeUnit.SECONDS);
} else {
logger.trace("run IdleConnectionMonitor - Http Client Connection manager is not initialised");
}
} catch (Exception e) {
logger.error("run IdleConnectionMonitor - Exception occurred. msg={}, e={}", e.getMessage(), e);
}
}
};
}
}
解决方案
推荐阅读
- php - 仅使用 Bootstrap Studio 进行前端开发是否被认为是一种不好的做法?
- swagger - 为什么在:body 参数在 swagger.json 文件中显示多个时间?
- flutter - 参数类型 'num?' 不能分配给参数类型 'num'.dart(argument_type_not_assignable)
- svelte - 转到网址:单击 Sapper
- node.js - 在 mongodb 数组中查找嵌套字段
- ios - Firebase SwiftUI - 使用子集合创建文档
- javascript - 如何使用 javascript 添加 class="lazyload" 和 data-sizes="auto"?
- kubernetes - Kubernetes 裸机:为节点分配外部 IP
- c - int* 动态重新分配在 C 中的大小为 8 时失败?
- java - SSLHandshakeException: sun.security.validator.ValidatorException : 对于某些 URL 的集成测试,但在 Spring Boot 中运行