前段时间发了一篇利用海康SDK抓图的示例,里面提到,开发的时候既可以直接在pom里引用sdk,也可以将sdk源码放到自己的工程里以便修改。
上次发的文章里面,提到了修改sdk源码中的超时时间。这两天又发现了一个新问题,当我利用多线程远程抓图时,由于网络问题,每次抓图都要耗费几秒钟时间,导致后面的线程一直排队,直到异常扔出来。又翻了一下SDK源码,发现厂商给的东西属实有点简单到过分了,单例模式搞了一个简单的CloseableHttpClient
对象,必然是要阻塞的。所以这里记录一下,我们修改HttpUtil
工具类的源码,给CloseableHttpClient
增加一个连接池。
要修改的位置位于HttpUtil
类的wrapClient()
方法。里面的GET、POST方法都是通过调用wrapClient
来获得httpClient
的实例的,所以改这一个地方就可以了。废话不多直接上源码了,创建线程池的部分也是在网上copy的,少部分进行了一些改动,大家根据自己实际情况去修改就可以了。
public static CloseableHttpClient wrapClient(String host, int connectTimeout) throws NoSuchAlgorithmException, KeyManagementException {
if (closeableHttpClient == null) {
synchronized (LOCK) {
if (closeableHttpClient == null) {
SSLConnectionSocketFactory socketFactory = null;
if (host.startsWith("https://")) {
socketFactory = sslClientNew();
}
HttpClientBuilder httpClientBuilder = HttpClients.custom();
httpClientBuilder.evictIdleConnections(1, TimeUnit.MINUTES) // 定期回收空闲连接
.evictExpiredConnections()// 定期回收过期连接
.setSSLSocketFactory(socketFactory)
//.setDefaultRequestConfig(getConfig());
.setDefaultRequestConfig(RequestConfig
.custom()
.setAuthenticationEnabled(false)
.setCircularRedirectsAllowed(false)
.setSocketTimeout(connectTimeout)
.setConnectTimeout(connectTimeout)
.setConnectionRequestTimeout(1000)
.setCookieSpec(CookieSpecs.IGNORE_COOKIES)
.build()) // 设置默认请求配置
.setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE) // 连接重用策略 是否能keepAlive
.setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE)// 长连接配置,即获取长连接生产多长时间
.setRetryHandler(new DefaultHttpRequestRetryHandler(0, false));// 重试次数 默认3次 此处禁用
Registry socketFactoryRegistry =
RegistryBuilder.create()
.register("https", socketFactory)
.register("http", new PlainConnectionSocketFactory()).build();
final int conExpire = 15;// 长连接闲置过期时间,可配置
cm = new PoolingHttpClientConnectionManager(socketFactoryRegistry, null, null, null, conExpire,
TimeUnit.SECONDS);
cm.setDefaultConnectionConfig(ConnectionConfig.DEFAULT);
cm.setMaxTotal(MAX_CONNECTION_NUM);
cm.setDefaultMaxPerRoute(MAX_PER_ROUTE);
// 设置长连接心跳检测,设置超时,禁用nagle算法
cm.setDefaultSocketConfig(SocketConfig.custom().setSoKeepAlive(true).setSoTimeout(connectTimeout).setTcpNoDelay(true).build());
cm.setValidateAfterInactivity(-1);// 每次取重用的连接时禁止连接活性检测,可以提升性能
httpClientBuilder.setConnectionManager(cm);
httpClientBuilder.setConnectionManagerShared(false);
closeableHttpClient = httpClientBuilder.build();
// 过期检测
Thread staleCheckThread = new Thread(() -> {
while (true) {
try {
Thread.sleep(10000);
cm.closeExpiredConnections();
cm.closeIdleConnections(conExpire, TimeUnit.SECONDS);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}, "HttpInvoker-coonection-stale-check-thread");
// 设置成为守护线程
staleCheckThread.setDaemon(true);
staleCheckThread.start();
// 程序退出时,关闭httpClient
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
closeableHttpClient.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}, "srsynchronize-httpInvoker-shutdown-thread"));
return closeableHttpClient;
}
}
}
return closeableHttpClient;
}