🎨 #3640 【微信支付】使用HttpClient发送http请求时调整为使用连接池的形式

This commit is contained in:
Copilot
2025-08-08 17:47:20 +08:00
committed by GitHub
parent bc6fb7b58e
commit dc46d9de75
5 changed files with 388 additions and 17 deletions

View File

@@ -14,7 +14,16 @@ import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RegExUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
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.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.ssl.SSLContexts;
import javax.net.ssl.SSLContext;
@@ -185,11 +194,32 @@ public class WxPayConfig {
private CloseableHttpClient apiV3HttpClient;
/**
* 用于普通支付接口的可复用HttpClient使用连接池
*/
private CloseableHttpClient httpClient;
/**
* 用于需要SSL证书的支付接口的可复用HttpClient使用连接池
*/
private CloseableHttpClient sslHttpClient;
/**
* 支持扩展httpClientBuilder
*/
private HttpClientBuilderCustomizer httpClientBuilderCustomizer;
private HttpClientBuilderCustomizer apiV3HttpClientBuilderCustomizer;
/**
* HTTP连接池最大连接数默认20
*/
private int maxConnTotal = 20;
/**
* HTTP连接池每个路由的最大连接数默认10
*/
private int maxConnPerRoute = 10;
/**
* 私钥信息
*/
@@ -498,4 +528,111 @@ public class WxPayConfig {
return null;
}
/**
* 初始化使用连接池的HttpClient
*
* @return CloseableHttpClient
* @throws WxPayException 初始化异常
*/
public CloseableHttpClient initHttpClient() throws WxPayException {
if (this.httpClient != null) {
return this.httpClient;
}
// 创建连接池管理器
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
connectionManager.setMaxTotal(this.maxConnTotal);
connectionManager.setDefaultMaxPerRoute(this.maxConnPerRoute);
// 创建HttpClient构建器
org.apache.http.impl.client.HttpClientBuilder httpClientBuilder = HttpClients.custom()
.setConnectionManager(connectionManager);
// 配置代理
configureProxy(httpClientBuilder);
// 提供自定义httpClientBuilder的能力
Optional.ofNullable(httpClientBuilderCustomizer).ifPresent(e -> {
e.customize(httpClientBuilder);
});
this.httpClient = httpClientBuilder.build();
return this.httpClient;
}
/**
* 初始化使用连接池且支持SSL的HttpClient
*
* @return CloseableHttpClient
* @throws WxPayException 初始化异常
*/
public CloseableHttpClient initSslHttpClient() throws WxPayException {
if (this.sslHttpClient != null) {
return this.sslHttpClient;
}
// 初始化SSL上下文
SSLContext sslContext = this.getSslContext();
if (null == sslContext) {
sslContext = this.initSSLContext();
}
// 创建支持SSL的连接池管理器
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
connectionManager.setMaxTotal(this.maxConnTotal);
connectionManager.setDefaultMaxPerRoute(this.maxConnPerRoute);
// 创建HttpClient构建器配置SSL
org.apache.http.impl.client.HttpClientBuilder httpClientBuilder = HttpClients.custom()
.setConnectionManager(connectionManager)
.setSSLSocketFactory(new SSLConnectionSocketFactory(sslContext, new DefaultHostnameVerifier()));
// 配置代理
configureProxy(httpClientBuilder);
// 提供自定义httpClientBuilder的能力
Optional.ofNullable(httpClientBuilderCustomizer).ifPresent(e -> {
e.customize(httpClientBuilder);
});
this.sslHttpClient = httpClientBuilder.build();
return this.sslHttpClient;
}
/**
* 配置HTTP代理
*/
private void configureProxy(org.apache.http.impl.client.HttpClientBuilder httpClientBuilder) {
if (StringUtils.isNotBlank(this.getHttpProxyHost()) && this.getHttpProxyPort() > 0) {
if (StringUtils.isEmpty(this.getHttpProxyUsername())) {
this.setHttpProxyUsername("whatever");
}
// 使用代理服务器 需要用户认证的代理服务器
CredentialsProvider provider = new BasicCredentialsProvider();
provider.setCredentials(new AuthScope(this.getHttpProxyHost(), this.getHttpProxyPort()),
new UsernamePasswordCredentials(this.getHttpProxyUsername(), this.getHttpProxyPassword()));
httpClientBuilder.setDefaultCredentialsProvider(provider)
.setProxy(new HttpHost(this.getHttpProxyHost(), this.getHttpProxyPort()));
}
}
/**
* 获取用于普通支付接口的HttpClient
*
* @return CloseableHttpClient
*/
public CloseableHttpClient getHttpClient() {
return httpClient;
}
/**
* 获取用于SSL支付接口的HttpClient
*
* @return CloseableHttpClient
*/
public CloseableHttpClient getSslHttpClient() {
return sslHttpClient;
}
}

View File

@@ -52,15 +52,15 @@ public class WxPayServiceApacheHttpImpl extends BaseWxPayServiceImpl {
@Override
public byte[] postForBytes(String url, String requestStr, boolean useKey) throws WxPayException {
try {
HttpClientBuilder httpClientBuilder = createHttpClientBuilder(useKey);
HttpPost httpPost = this.createHttpPost(url, requestStr);
try (CloseableHttpClient httpClient = httpClientBuilder.build()) {
final byte[] bytes = httpClient.execute(httpPost, ByteArrayResponseHandler.INSTANCE);
final String responseData = Base64.getEncoder().encodeToString(bytes);
this.logRequestAndResponse(url, requestStr, responseData);
wxApiData.set(new WxPayApiData(url, requestStr, responseData, null));
return bytes;
}
CloseableHttpClient httpClient = this.createHttpClient(useKey);
// 使用连接池的客户端,不需要手动关闭
final byte[] bytes = httpClient.execute(httpPost, ByteArrayResponseHandler.INSTANCE);
final String responseData = Base64.getEncoder().encodeToString(bytes);
this.logRequestAndResponse(url, requestStr, responseData);
wxApiData.set(new WxPayApiData(url, requestStr, responseData, null));
return bytes;
} catch (Exception e) {
this.logError(url, requestStr, e);
wxApiData.set(new WxPayApiData(url, requestStr, null, e.getMessage()));
@@ -71,17 +71,17 @@ public class WxPayServiceApacheHttpImpl extends BaseWxPayServiceImpl {
@Override
public String post(String url, String requestStr, boolean useKey) throws WxPayException {
try {
HttpClientBuilder httpClientBuilder = this.createHttpClientBuilder(useKey);
HttpPost httpPost = this.createHttpPost(url, requestStr);
try (CloseableHttpClient httpClient = httpClientBuilder.build()) {
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
String responseString = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
this.logRequestAndResponse(url, requestStr, responseString);
if (this.getConfig().isIfSaveApiData()) {
wxApiData.set(new WxPayApiData(url, requestStr, responseString, null));
}
return responseString;
CloseableHttpClient httpClient = this.createHttpClient(useKey);
// 使用连接池的客户端,不需要手动关闭
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
String responseString = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
this.logRequestAndResponse(url, requestStr, responseString);
if (this.getConfig().isIfSaveApiData()) {
wxApiData.set(new WxPayApiData(url, requestStr, responseString, null));
}
return responseString;
} finally {
httpPost.releaseConnection();
}
@@ -281,6 +281,26 @@ public class WxPayServiceApacheHttpImpl extends BaseWxPayServiceImpl {
return apiV3HttpClient;
}
CloseableHttpClient createHttpClient(boolean useKey) throws WxPayException {
if (useKey) {
// 使用SSL连接池客户端
CloseableHttpClient sslHttpClient = this.getConfig().getSslHttpClient();
if (null == sslHttpClient) {
this.getConfig().initSslHttpClient();
sslHttpClient = this.getConfig().getSslHttpClient();
}
return sslHttpClient;
} else {
// 使用普通连接池客户端
CloseableHttpClient httpClient = this.getConfig().getHttpClient();
if (null == httpClient) {
this.getConfig().initHttpClient();
httpClient = this.getConfig().getHttpClient();
}
return httpClient;
}
}
private static StringEntity createEntry(String requestStr) {
return new StringEntity(requestStr, ContentType.create(APPLICATION_JSON, StandardCharsets.UTF_8));
//return new StringEntity(new String(requestStr.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));