diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpClientUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpClientUtil.java deleted file mode 100644 index 954701f..0000000 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpClientUtil.java +++ /dev/null @@ -1,324 +0,0 @@ -package com.ruoyi.common.utils.http; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.NameValuePair; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.conn.ssl.DefaultHostnameVerifier; -import org.apache.http.conn.util.PublicSuffixMatcher; -import org.apache.http.conn.util.PublicSuffixMatcherLoader; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.message.BasicNameValuePair; -import org.apache.http.util.EntityUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.alibaba.fastjson2.JSON; -import com.ruoyi.common.utils.spring.SpringUtils; - -public class HttpClientUtil { - - private static final Logger log = LoggerFactory.getLogger(HttpClientUtil.class); - /** - * 默认参数设置 - * setConnectTimeout:设置连接超时时间,单位毫秒。 - * setConnectionRequestTimeout:设置从connect Manager获取Connection 超时时间,单位毫秒。 - * setSocketTimeout:请求获取数据的超时时间,单位毫秒。访问一个接口,多少时间内无法返回数据,就直接放弃此次调用。 暂时定义15分钟 - */ - private static RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(600000) - .setConnectTimeout(600000) - .setConnectionRequestTimeout(600000).build(); - - private static CloseableHttpClient client = SpringUtils.getBean("closeableHttpClient"); - - /** - * 发送 post请求 - * - * @param httpUrl 地址 - */ - public static String sendHttpPost(String httpUrl) { - HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPost - return sendHttpPost(httpPost); - } - - /** - * 发送 post请求 - * - * @param httpUrl 地址 - * @param params 参数(格式:key1=value1&key2=value2) - */ - public static String sendHttpPost(String httpUrl, String params) { - HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPost - try { - // 设置参数 - StringEntity stringEntity = new StringEntity(params, "UTF-8"); - stringEntity.setContentType("application/x-www-form-urlencoded"); - httpPost.setEntity(stringEntity); - } catch (Exception e) { - e.printStackTrace(); - } - return sendHttpPost(httpPost); - } - - public static String sendHttpPost(String httpUrl, Object object) { - HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPost - String jsonString = JSON.toJSONString(object); - httpPost.setHeader("Accept", ContentType.APPLICATION_JSON.getMimeType()); - httpPost.setHeader("Content-Type", ContentType.APPLICATION_JSON.getMimeType()); - log.debug(jsonString); - try { - - // 设置参数 - StringEntity stringEntity = new StringEntity(jsonString, ContentType.APPLICATION_JSON); - - httpPost.setEntity(stringEntity); - } catch (Exception e) { - e.printStackTrace(); - } - return sendHttpPost(httpPost); - } - - public static String sendHttpPost(String httpUrl, Object data, Map headersMap) { - HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPost - String jsonString = JSON.toJSONString(data); - httpPost.setHeader("Accept", ContentType.APPLICATION_JSON.getMimeType()); - httpPost.setHeader("Content-Type", ContentType.APPLICATION_JSON.getMimeType()); - for (String keySet : headersMap.keySet()) { - httpPost.setHeader(keySet, headersMap.get(keySet)); - } - log.debug(jsonString); - try { - // 设置参数 - StringEntity stringEntity = new StringEntity(jsonString, ContentType.APPLICATION_JSON); - - httpPost.setEntity(stringEntity); - } catch (Exception e) { - e.printStackTrace(); - } - return sendHttpPost(httpPost); - } - - /** - * 发送 post请求 - * - * @param httpUrl 地址 - * @param maps 参数 - */ - public static String sendHttpPost(String httpUrl, Map maps) { - HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPost - // 创建参数队列 - List nameValuePairs = new ArrayList(); - for (String key : maps.keySet()) { - nameValuePairs.add(new BasicNameValuePair(key, maps.get(key))); - } - try { - httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, "UTF-8")); - } catch (Exception e) { - e.printStackTrace(); - } - return sendHttpPost(httpPost); - } - - /** - * 发送 get请求 - * - * @param httpUrl - */ - public static String sendHttpGet(String httpUrl) { - HttpGet httpGet = new HttpGet(httpUrl);// 创建get请求 - return sendHttpGet(httpGet); - } - - /** - * 发送 get请求Https - * - * @param httpUrl - */ - public static String sendHttpsGet(String httpUrl) { - HttpGet httpGet = new HttpGet(httpUrl);// 创建get请求 - return sendHttpsGet(httpGet); - } - - /** - * 发送Post请求 - * - * @param httpPost - * @return - */ - private static String sendHttpPost(HttpPost httpPost) { - CloseableHttpClient httpClient = null; - CloseableHttpResponse response = null; - HttpEntity entity = null; - String responseContent = null; - try { - // 创建默认的httpClient实例 - // httpClient = HttpClients.createDefault(); - httpPost.setConfig(requestConfig); - // 执行请求 - long execStart = System.currentTimeMillis(); - response = client.execute(httpPost); - long execEnd = System.currentTimeMillis(); - log.debug("=================执行post请求耗时:" + (execEnd - execStart) + "ms"); - long getStart = System.currentTimeMillis(); - entity = response.getEntity(); - responseContent = EntityUtils.toString(entity, "UTF-8"); - long getEnd = System.currentTimeMillis(); - log.debug("=================获取响应结果耗时:" + (getEnd - getStart) + "ms"); - } catch (Exception e) { - e.printStackTrace(); - } finally { - try { - // 关闭连接,释放资源 - if (response != null) { - response.close(); - } - if (httpClient != null) { - httpClient.close(); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - return responseContent; - } - - /** - * 发送Get请求 - * - * @param httpGet - * @return - */ - private static String sendHttpGet(HttpGet httpGet) { - CloseableHttpClient httpClient = null; - CloseableHttpResponse response = null; - HttpEntity entity = null; - String responseContent = null; - try { - // 创建默认的httpClient实例. - - httpGet.setConfig(requestConfig); - // 执行请求 - response = client.execute(httpGet); - entity = response.getEntity(); - responseContent = EntityUtils.toString(entity, "UTF-8"); - } catch (Exception e) { - e.printStackTrace(); - } finally { - try { - // 关闭连接,释放资源 - if (response != null) { - response.close(); - } - if (httpClient != null) { - httpClient.close(); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - return responseContent; - } - - /** - * 发送Get请求Https - * - * @param httpGet - * @return - */ - private static String sendHttpsGet(HttpGet httpGet) { - CloseableHttpClient httpClient = null; - CloseableHttpResponse response = null; - HttpEntity entity = null; - String responseContent = null; - try { - // 创建默认的httpClient实例. - PublicSuffixMatcher publicSuffixMatcher = PublicSuffixMatcherLoader - .load(new URL(httpGet.getURI().toString())); - DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(publicSuffixMatcher); - httpClient = HttpClients.custom().setSSLHostnameVerifier(hostnameVerifier).build(); - httpGet.setConfig(requestConfig); - // 执行请求 - response = httpClient.execute(httpGet); - entity = response.getEntity(); - responseContent = EntityUtils.toString(entity, "UTF-8"); - } catch (Exception e) { - e.printStackTrace(); - } finally { - try { - // 关闭连接,释放资源 - if (response != null) { - response.close(); - } - if (httpClient != null) { - httpClient.close(); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - return responseContent; - } - - /** - * 发送xml数据 - * - * @param url - * @param xmlData - * @return - * @throws ClientProtocolException - * @throws IOException - */ - public static HttpResponse sendXMLDataByPost(String url, String xmlData) - throws ClientProtocolException, IOException { - - HttpPost httppost = new HttpPost(url); - StringEntity entity = new StringEntity(xmlData); - httppost.setEntity(entity); - httppost.setHeader("Content-Type", "text/xml;charset=UTF-8"); - HttpResponse response = client.execute(httppost); - return response; - } - - /** - * 获得响应HTTP实体内容 - * - * @param response - * @return - * @throws IOException - * @throws UnsupportedEncodingException - */ - public static String getHttpEntityContent(HttpResponse response) throws IOException, UnsupportedEncodingException { - HttpEntity entity = response.getEntity(); - if (entity != null) { - InputStream is = entity.getContent(); - BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8")); - String line = br.readLine(); - StringBuilder sb = new StringBuilder(); - while (line != null) { - sb.append(line + "\n"); - line = br.readLine(); - } - return sb.toString(); - } - return ""; - } - -} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpConf.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpConf.java new file mode 100644 index 0000000..8677730 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpConf.java @@ -0,0 +1,33 @@ +package com.ruoyi.common.utils.http; + +/** + * http 配置信息 + * + * @author ruoyi + */ +public class HttpConf +{ + // 获取连接的最大等待时间 + public static int WAIT_TIMEOUT = 10000; + + // 连接超时时间 + public static int CONNECT_TIMEOUT = 10000; + + // 读取超时时间 + public static int SO_TIMEOUT = 60000; + + // 最大连接数 + public static int MAX_TOTAL_CONN = 200; + + // 每个路由最大连接数 + public static int MAX_ROUTE_CONN = 150; + + // 重试次数 + public static int RETRY_COUNT = 3; + + // EPTWebServes地址 + public static String EPTWEBSERVES_URL; + + // tomcat默认keepAliveTimeout为20s + public static int KEEP_ALIVE_TIMEOUT; +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java index 4126b2f..537fe1b 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java @@ -10,7 +10,11 @@ import java.net.SocketTimeoutException; import java.net.URL; import java.net.URLConnection; import java.nio.charset.StandardCharsets; +import java.security.cert.CertificateException; import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; @@ -19,6 +23,31 @@ import javax.net.ssl.SSLSession; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.NameValuePair; +import org.apache.http.client.HttpRequestRetryHandler; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.config.RequestConfig.Builder; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.conn.ConnectTimeoutException; +import org.apache.http.conn.ConnectionKeepAliveStrategy; +import org.apache.http.conn.HttpClientConnectionManager; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.TrustStrategy; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.DefaultHttpRequestRetryHandler; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.protocol.HttpContext; +import org.apache.http.ssl.SSLContextBuilder; +import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,6 +63,19 @@ public class HttpUtils { private static final Logger log = LoggerFactory.getLogger(HttpUtils.class); + public static RequestConfig requestConfig; + + private static CloseableHttpClient httpClient; + + private static PoolingHttpClientConnectionManager connMgr; + + private static IdleConnectionMonitorThread idleThread; + + static + { + HttpUtils.initClient(); + } + /** * 向指定 URL 发送GET方法的请求 * @@ -219,7 +261,7 @@ public class HttpUtils String ret = ""; while ((ret = br.readLine()) != null) { - if (ret != null && !"".equals(ret.trim())) + if (ret != null && !ret.trim().equals("")) { result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8)); } @@ -274,4 +316,429 @@ public class HttpUtils return true; } } + + /** + * 获取httpClient + * + * @return + */ + public static CloseableHttpClient getHttpClient() + { + if (httpClient != null) + { + return httpClient; + } + else + { + return HttpClients.createDefault(); + } + } + + /** + * 创建连接池管理器 + * + * @return + */ + private static PoolingHttpClientConnectionManager createConnectionManager() + { + + PoolingHttpClientConnectionManager connMgr = new PoolingHttpClientConnectionManager(); + // 将最大连接数增加到 + connMgr.setMaxTotal(HttpConf.MAX_TOTAL_CONN); + // 将每个路由基础的连接增加到 + connMgr.setDefaultMaxPerRoute(HttpConf.MAX_ROUTE_CONN); + + return connMgr; + } + + /** + * 根据当前配置创建HTTP请求配置参数。 + * + * @return 返回HTTP请求配置。 + */ + private static RequestConfig createRequestConfig() + { + Builder builder = RequestConfig.custom(); + builder.setConnectionRequestTimeout(StringUtils.nvl(HttpConf.WAIT_TIMEOUT, 10000)); + builder.setConnectTimeout(StringUtils.nvl(HttpConf.CONNECT_TIMEOUT, 10000)); + builder.setSocketTimeout(StringUtils.nvl(HttpConf.SO_TIMEOUT, 60000)); + return builder.build(); + } + + /** + * 创建默认的HTTPS客户端,信任所有的证书。 + * + * @return 返回HTTPS客户端,如果创建失败,返回HTTP客户端。 + */ + private static CloseableHttpClient createHttpClient(HttpClientConnectionManager connMgr) + { + try + { + final SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() + { + @Override + public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException + { + // 信任所有 + return true; + } + }).build(); + final SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext); + + // 重试机制 + HttpRequestRetryHandler retryHandler = new DefaultHttpRequestRetryHandler(HttpConf.RETRY_COUNT, true); + ConnectionKeepAliveStrategy connectionKeepAliveStrategy = new ConnectionKeepAliveStrategy() + { + @Override + public long getKeepAliveDuration(HttpResponse httpResponse, HttpContext httpContext) + { + return HttpConf.KEEP_ALIVE_TIMEOUT; // tomcat默认keepAliveTimeout为20s + } + }; + httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).setConnectionManager(connMgr) + .setDefaultRequestConfig(requestConfig).setRetryHandler(retryHandler) + .setKeepAliveStrategy(connectionKeepAliveStrategy).build(); + } + catch (Exception e) + { + log.error("Create http client failed", e); + httpClient = HttpClients.createDefault(); + } + + return httpClient; + } + + /** + * 初始化 只需调用一次 + */ + public synchronized static CloseableHttpClient initClient() + { + if (httpClient == null) + { + connMgr = createConnectionManager(); + requestConfig = createRequestConfig(); + // 初始化httpClient连接池 + httpClient = createHttpClient(connMgr); + // 清理连接池 + idleThread = new IdleConnectionMonitorThread(connMgr); + idleThread.start(); + } + + return httpClient; + } + + /** + * 关闭HTTP客户端。 + * + * @param httpClient HTTP客户端。 + */ + public synchronized static void shutdown() + { + try + { + if (idleThread != null) + { + idleThread.shutdown(); + idleThread = null; + } + } + catch (Exception e) + { + log.error("httpclient connection manager close", e); + } + + try + { + if (httpClient != null) + { + httpClient.close(); + httpClient = null; + } + } + catch (IOException e) + { + log.error("httpclient close", e); + } + } + + /** + * 请求上游 GET提交 + * + * @param uri + * @throws IOException + */ + public static String getCall(final String uri) throws Exception + { + + return getCall(uri, null, Constants.UTF8); + } + + /** + * 请求上游 GET提交 + * + * @param uri + * @param contentType + * @throws IOException + */ + public static String getCall(final String uri, String contentType) throws Exception + { + + return getCall(uri, contentType, Constants.UTF8); + } + + /** + * 请求上游 GET提交 + * + * @param uri + * @param contentType + * @param charsetName + * @throws IOException + */ + public static String getCall(final String uri, String contentType, String charsetName) throws Exception + { + + final String url = uri; + final HttpGet httpGet = new HttpGet(url); + httpGet.setConfig(requestConfig); + if (!StringUtils.isEmpty(contentType)) + { + httpGet.addHeader("Content-Type", contentType); + } + final CloseableHttpResponse httpRsp = getHttpClient().execute(httpGet); + try + { + if (httpRsp.getStatusLine().getStatusCode() == HttpStatus.SC_OK + || httpRsp.getStatusLine().getStatusCode() == HttpStatus.SC_FORBIDDEN) + { + final HttpEntity entity = httpRsp.getEntity(); + final String rspText = EntityUtils.toString(entity, charsetName); + EntityUtils.consume(entity); + return rspText; + } + else + { + throw new IOException("HTTP StatusCode=" + httpRsp.getStatusLine().getStatusCode()); + } + } + finally + { + try + { + httpRsp.close(); + } + catch (Exception e) + { + log.error("关闭httpRsp异常", e); + } + } + } + + /** + * 请求上游 POST提交 + * + * @param uri + * @param paramsMap + * @throws IOException + */ + public static String postCall(final String uri, Map paramsMap) throws Exception + { + return postCall(uri, null, paramsMap, Constants.UTF8); + } + + /** + * 请求上游 POST提交 + * + * @param uri + * @param contentType + * @param paramsMap + * @throws IOException + */ + public static String postCall(final String uri, String contentType, Map paramsMap) throws Exception + { + + return postCall(uri, contentType, paramsMap, Constants.UTF8); + } + + /** + * 请求上游 POST提交 + * + * @param uri + * @param contentType + * @param paramsMap + * @param charsetName + * @throws IOException + */ + public static String postCall(final String uri, String contentType, Map paramsMap, + String charsetName) throws Exception + { + + final String url = uri; + final HttpPost httpPost = new HttpPost(url); + httpPost.setConfig(requestConfig); + if (!StringUtils.isEmpty(contentType)) + { + httpPost.addHeader("Content-Type", contentType); + } + // 添加参数 + List list = new ArrayList(); + if (paramsMap != null) + { + for (Map.Entry entry : paramsMap.entrySet()) + { + list.add(new BasicNameValuePair(entry.getKey(), (String) entry.getValue())); + } + } + httpPost.setEntity(new UrlEncodedFormEntity(list, charsetName)); + + final CloseableHttpResponse httpRsp = getHttpClient().execute(httpPost); + + try + { + if (httpRsp.getStatusLine().getStatusCode() == HttpStatus.SC_OK) + { + final HttpEntity entity = httpRsp.getEntity(); + final String rspText = EntityUtils.toString(entity, charsetName); + EntityUtils.consume(entity); + return rspText; + } + else + { + throw new IOException("HTTP StatusCode=" + httpRsp.getStatusLine().getStatusCode()); + } + } + finally + { + try + { + httpRsp.close(); + } + catch (Exception e) + { + log.error("关闭httpRsp异常", e); + } + } + } + + /** + * 请求上游 POST提交 + * + * @param uri + * @param param + * @throws IOException + */ + public static String postCall(final String uri, String param) throws Exception + { + + return postCall(uri, null, param, Constants.UTF8); + } + + /** + * 请求上游 POST提交 + * + * @param uri + * @param contentType + * @param param + * @throws IOException + */ + public static String postCall(final String uri, String contentType, String param) throws Exception + { + + return postCall(uri, contentType, param, Constants.UTF8); + } + + /** + * 请求上游 POST提交 + * + * @param uri + * @param contentType + * @param param + * @param charsetName + * @throws IOException + */ + public static String postCall(final String uri, String contentType, String param, String charsetName) + throws Exception + { + + final String url = uri; + final HttpPost httpPost = new HttpPost(url); + httpPost.setConfig(requestConfig); + if (!StringUtils.isEmpty(contentType)) + { + httpPost.addHeader("Content-Type", contentType); + } + else + { + httpPost.addHeader("Content-Type", "application/json"); + } + // 添加参数 + StringEntity paramEntity = new StringEntity(param, charsetName); + httpPost.setEntity(paramEntity); + + final CloseableHttpResponse httpRsp = getHttpClient().execute(httpPost); + + try + { + if (httpRsp.getStatusLine().getStatusCode() == HttpStatus.SC_OK) + { + final HttpEntity entity = httpRsp.getEntity(); + final String rspText = EntityUtils.toString(entity, charsetName); + EntityUtils.consume(entity); + return rspText; + } + else + { + throw new IOException("HTTP StatusCode=" + httpRsp.getStatusLine().getStatusCode()); + } + } + finally + { + try + { + httpRsp.close(); + } + catch (Exception e) + { + log.error("关闭httpRsp异常", e); + } + } + } + + /** + * 判断HTTP异常是否为读取超时。 + * + * @param e 异常对象。 + * @return 如果是读取引起的异常(而非连接),则返回true;否则返回false。 + */ + public static boolean isReadTimeout(final Throwable e) + { + return (!isCausedBy(e, ConnectTimeoutException.class) && isCausedBy(e, SocketTimeoutException.class)); + } + + /** + * 检测异常e被触发的原因是不是因为异常cause。检测被封装的异常。 + * + * @param e 捕获的异常。 + * @param cause 异常触发原因。 + * @return 如果异常e是由cause类异常触发,则返回true;否则返回false。 + */ + public static boolean isCausedBy(final Throwable e, final Class cause) + { + if (cause.isAssignableFrom(e.getClass())) + { + return true; + } + else + { + Throwable t = e.getCause(); + while (t != null && t != e) + { + if (cause.isAssignableFrom(t.getClass())) + { + return true; + } + t = t.getCause(); + } + return false; + } + } } \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/IdleConnectionMonitorThread.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/IdleConnectionMonitorThread.java new file mode 100644 index 0000000..8b7669d --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/IdleConnectionMonitorThread.java @@ -0,0 +1,73 @@ +package com.ruoyi.common.utils.http; + +import java.util.concurrent.TimeUnit; + +import org.apache.http.conn.HttpClientConnectionManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 连接池清理 + * + * @author ruoyi + */ +public class IdleConnectionMonitorThread extends Thread +{ + private static final Logger log = LoggerFactory.getLogger(IdleConnectionMonitorThread.class); + + private final HttpClientConnectionManager connMgr; + + private volatile boolean shutdown; + + public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr) + { + super(); + this.shutdown = false; + this.connMgr = connMgr; + } + + @Override + public void run() + { + while (!shutdown) + { + try + { + synchronized (this) + { + // 每5秒检查一次关闭连接 + wait(HttpConf.KEEP_ALIVE_TIMEOUT / 4); + // 关闭失效的连接 + connMgr.closeExpiredConnections(); + // 可选的, 关闭20秒内不活动的连接 + connMgr.closeIdleConnections(HttpConf.KEEP_ALIVE_TIMEOUT, TimeUnit.MILLISECONDS); + // log.debug("关闭失效的连接"); + } + } + catch (Exception e) + { + log.error("关闭失效连接异常", e); + } + } + } + + public void shutdown() + { + shutdown = true; + if (connMgr != null) + { + try + { + connMgr.shutdown(); + } + catch (Exception e) + { + log.error("连接池异常", e); + } + } + synchronized (this) + { + notifyAll(); + } + } +} \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java index 8f06930..913561b 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java @@ -1,26 +1,10 @@ package com.ruoyi.framework.config; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor; -import javax.net.ssl.SSLContext; - import org.apache.commons.lang3.concurrent.BasicThreadFactory; -import org.apache.http.config.Registry; -import org.apache.http.config.RegistryBuilder; -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.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.apache.http.ssl.SSLContextBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @@ -33,7 +17,8 @@ import com.ruoyi.common.utils.Threads; * @author ruoyi **/ @Configuration -public class ThreadPoolConfig { +public class ThreadPoolConfig +{ // 核心线程池大小 private int corePoolSize = 50; @@ -47,7 +32,8 @@ public class ThreadPoolConfig { private int keepAliveSeconds = 300; @Bean(name = "threadPoolTaskExecutor") - protected ThreadPoolTaskExecutor threadPoolTaskExecutor() { + public ThreadPoolTaskExecutor threadPoolTaskExecutor() + { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setMaxPoolSize(maxPoolSize); executor.setCorePoolSize(corePoolSize); @@ -62,43 +48,18 @@ public class ThreadPoolConfig { * 执行周期性或定时任务 */ @Bean(name = "scheduledExecutorService") - protected ScheduledExecutorService scheduledExecutorService() { + protected ScheduledExecutorService scheduledExecutorService() + { return new ScheduledThreadPoolExecutor(corePoolSize, new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(), - new ThreadPoolExecutor.CallerRunsPolicy()) { + new ThreadPoolExecutor.CallerRunsPolicy()) + { @Override - protected void afterExecute(Runnable r, Throwable t) { + protected void afterExecute(Runnable r, Throwable t) + { super.afterExecute(r, t); Threads.printException(r, t); } }; } - - @Bean(name = "closeableHttpClient") - protected CloseableHttpClient closeableHttpClient() throws Exception { - SSLContextBuilder sslContextBuilder = new SSLContextBuilder(); - sslContextBuilder.loadTrustMaterial(null, new TrustStrategy() { - // 信任所有 - @Override - public boolean isTrusted(X509Certificate[] chain, - String authType) throws CertificateException { - return true; - } - }); - SSLContext sslContext = sslContextBuilder.build(); - SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext, - new String[] { "SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2" }, - null, NoopHostnameVerifier.INSTANCE); - Registry register = null; - register = RegistryBuilder.create() - .register("http", PlainConnectionSocketFactory.INSTANCE) - .register("https", socketFactory).build(); - HttpClientBuilder custom = HttpClients.custom(); - PoolingHttpClientConnectionManager pool = new PoolingHttpClientConnectionManager(register); - pool.setMaxTotal(maxPoolSize); - // // 默认的每个路由的最大连接数 - pool.setDefaultMaxPerRoute(corePoolSize); - custom.setConnectionManager(pool); - return custom.build(); - } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java index 095b865..f70ba5b 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java @@ -3,6 +3,9 @@ package com.ruoyi.framework.manager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; + +import com.ruoyi.common.utils.http.HttpUtils; + import jakarta.annotation.PreDestroy; /** @@ -19,6 +22,7 @@ public class ShutdownManager public void destroy() { shutdownAsyncManager(); + HttpUtils.shutdown(); } /**