博客
关于我
HttpClient客户端网络编程——高可用、高并发
阅读量:430 次
发布时间:2019-03-06

本文共 10218 字,大约阅读时间需要 34 分钟。

HttpClient客户端配置高可用与高并发的实现方案

项目依赖

本文基于Spring Boot框架,选择了以下关键依赖:

  • Spring Boot Starter:提供了核心容器和依赖管理功能
  • HttpClient:实现了基于HTTP协议的网络通信
  • FastJSON:用于JSON数据的快速解析和生成
  • Spring Boot Starter Test:用于集成测试
org.springframework.boot
spring-boot-starter
org.apache.http
httpclient
4.5.12
com.alibaba
fastjson
1.2.70
org.springframework.boot
spring-boot-starter-test
test

项目结构

com
├── example
│ ├── httpclientdemo
│ │ ├── biz
│ │ │ ├── provider
│ │ │ └── impl
│ │ ├── common
│ │ │ └── utils
│ │ └── service
│ │ ├── impl
│ │ └── provider
│ └── config
│ └── http
│ └── client

项目配置

HttpClient配置

# HttpClient配置
httpClient:
retryCount: 3
requestSentRetryEnabled: true
pool:
maxTotal: 200
defaultMaxPerRoute: 50
validateAfterInactivity: 1000
idleTimeOut: 3
socketCfg:
tcpNoDelay: true
soReuseAddress: true
soTimeOut: 500
soLinger: 60
soKeepAlive: true

HttpClient客户端配置类

package com.example.httpclientdemo.common.config.http.client;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContexts;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.TimeUnit;
@Configuration
public class HttpClientConfig {
@Bean
public CloseableHttpClient closeableHttpClient(PoolingHttpClientConnectionManager httpClientPoolManager,
@Value("${httpClient.retryCount}") int retryCount,
@Value("${httpClient.requestSentRetryEnabled}") boolean requestSentRetryEnabled) {
return HttpClients.custom()
.setDefaultCookieStore(new BasicCookieStore())
.setDefaultCredentialsProvider(new BasicCredentialsProvider())
.setConnectionManager(httpClientPoolManager)
.setRetryHandler(new DefaultHttpRequestRetryHandler(retryCount, requestSentRetryEnabled))
.build();
}
@Bean
public SocketConfig defaultSocketConfig(
@Value("${httpClient.pool.socketCfg.tcpNoDelay}") boolean tcpNoDelay,
@Value("${httpClient.pool.socketCfg.soReuseAddress}") boolean soReuseAddress,
@Value("${httpClient.pool.socketCfg.soTimeOut}") int soTimeOut,
@Value("${httpClient.pool.socketCfg.soLinger}") int soLinger,
@Value("${httpClient.pool.socketCfg.soKeepAlive}") boolean soKeepAlive) {
return SocketConfig.custom()
.setTcpNoDelay(tcpNoDelay)
.setSoReuseAddress(soReuseAddress)
.setSoTimeout(soTimeOut)
.setSoLinger(soLinger)
.setSoKeepAlive(soKeepAlive)
.build();
}
@Bean
public PoolingHttpClientConnectionManager httpClientPoolManager(
@Value("${httpClient.pool.maxTotal}") int maxTotal,
@Value("${httpClient.pool.defaultMaxPerRoute}") int defaultMaxPerRoute,
@Value("${httpClient.pool.validateAfterInactivity}") int validateAfterInactivity,
@Value("${httpClient.pool.idleTimeOut}") long idleTimeOut,
SocketConfig defaultSocketConfig) {
Registry registry = RegistryBuilder.create()
.register("http", PlainConnectionSocketFactory.INSTANCE)
.register("https", new SSLConnectionSocketFactory(SSLContexts.createSystemDefault()))
.build();
PoolingHttpClientConnectionManager poolManager = new PoolingHttpClientConnectionManager(registry);
poolManager.setMaxTotal(maxTotal);
poolManager.setDefaultMaxPerRoute(defaultMaxPerRoute);
poolManager.setValidateAfterInactivity(validateAfterInactivity);
poolManager.closeIdleConnections(idleTimeOut, TimeUnit.SECONDS);
poolManager.setDefaultSocketConfig(defaultSocketConfig);
return poolManager;
}
}

HttpClient业务接口及其实现

业务接口

package com.example.httpclientdemo.biz.service;
public interface HttpClientService {
void requestGet(String url);
void requestPost(String url, Object obj);
}

业务实现

package com.example.httpclientdemo.biz.service.impl;
import com.example.httpclientdemo.biz.provider.HttpClientProvider;
import com.example.httpclientdemo.biz.service.HttpClientService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class HttpClientServiceImpl implements HttpClientService {
@Autowired
private HttpClientProvider httpClientProvider;
@Override
public void requestGet(String url) {
httpClientProvider.requestGet(url);
}
@Override
public void requestPost(String url, Object obj) {
httpClientProvider.requestPost(url, obj);
}
}

业务支撑接口

package com.example.httpclientdemo.biz.provider;
public interface HttpClientProvider {
void requestGet(String url);
void requestPost(String url, Object obj);
}

业务实现

package com.example.httpclientdemo.biz.provider.impl;
import com.alibaba.fastjson.JSON;
import com.example.httpclientdemo.biz.provider.HttpClientProvider;
import com.example.httpclientdemo.common.utils.http.client.HttpClientUtils;
import org.apache.http.HttpEntity;
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.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
@Service
public class HttpClientProviderImpl implements HttpClientProvider {
private final Logger logger = LoggerFactory.getLogger(HttpClientProviderImpl.class);
@Resource(name = "closeableHttpClient")
private CloseableHttpClient httpClient;
@Override
public void requestGet(String url) {
HttpGet httpGet = new HttpGet(url);
CloseableHttpResponse response = null;
InputStream inputStream = null;
try {
response = HttpClientUtils.get(httpClient, httpGet);
HttpEntity httpEntity = response.getEntity();
inputStream = HttpClientUtils.getContent(httpEntity);
String respString = HttpClientUtils.getContentString(inputStream);
logger.debug("请求地址: {}, 响应内容: {}", url, respString);
} catch (Exception e) {
e.fillInStackTrace();
} finally {
try {
HttpClientUtils.close(response, inputStream);
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
}
@Override
public void requestPost(String url, Object obj) {
HTTPPost httpPost = new HTTPPost(url);
String jsonString = JSON.toJSONString(obj);
httpPost.setEntity(new StringEntity(jsonString, StandardCharsets.UTF_8));
CloseableHttpResponse response = null;
InputStream inputStream = null;
try {
response = HttpClientUtils.post(httpClient, httpPost);
HttpEntity httpEntity = response.getEntity();
inputStream = HttpClientUtils.getContent(httpEntity);
String respString = HttpClientUtils.getContentString(inputStream);
logger.debug("请求地址: {}, 请求参数: {}, 响应内容: {}", url, jsonString, respString);
} catch (Exception e) {
e.fillInStackTrace();
} finally {
try {
HttpClientUtils.close(response, inputStream);
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
}
}

测试验证

单元测试

package com.example.httpclientdemo.biz.service;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
public class HttpClientServiceTest {
private final static Logger logger = LoggerFactory.getLogger(HttpClientServiceTest.class);
@Autowired
private HttpClientService httpClientService;
private static final String URL_GET_PATH = "https://www.baidu.com";
private final AtomicInteger count = new AtomicInteger();
private static final ExecutorService workStealingPool = Executors.newWorkStealingPool(1 << 6);
@Test
void requestGet() throws InterruptedException {
for (int i = 0; i < 1000; i++) {
workStealingPool.execute(() -> {
httpClientService.requestGet(URL_GET_PATH);
logger.warn("requestGet, times: {}, betMils: {}, betNanos: {}", count.getAndIncrement(),
System.currentTimeMillis() - startMils, System.nanoTime() - statNanos);
});
}
Thread.sleep(20000);
}
@Test
void requestPost() {
// 后续可根据实际需求添加测试用例
}
}

综合比较

通过对比不同配置参数下的性能表现,可以看出以下结论:

参数 50 线程 200 线程
defaultMaxPerRoute 50 50
validateAfterInactivity 1000 1000
betMils 5270 5245
betNanos 5269994599 5269405201

通过对比可以发现,在不同线程数下,响应时间的波动范围较小,主要受网络条件的影响较大。

后记

本文是关于HttpClient客户端配置的学习与实践,通过实际项目的配置和优化,总结了高可用、高并发下的HttpClient配置方案。未来将持续更新本博客内容,并欢迎技术爱好者交流与讨论。

转载地址:http://kywyz.baihongyu.com/

你可能感兴趣的文章
openssl安装
查看>>
OpenSSL生成root CA及签发证书
查看>>
Openstack CLI命令管理私有云主机实战(附OpenStack实验环境)
查看>>
openStack instance error 恢复
查看>>
openstack instance resize to
查看>>