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

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

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;@Configurationpublic 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;@Servicepublic 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;@Servicepublic 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.*;@SpringBootTestpublic 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/

你可能感兴趣的文章
python系列【仅供参考】:python 远程rdp
查看>>
python基础
查看>>
Python基本语法与变量类型
查看>>
python基本数据类型(容器)- tuple list dict set
查看>>
python基本工资的调整方案_Python薪资又涨了!这可咋办!
查看>>
Python基准测试和性能分析内存管理和垃圾回收
查看>>
Python基于RESTful风格的接口自动化测试实战
查看>>
python基于pygame实现跨年烟花效果
查看>>
python基于flask搭建http服务(四)—— Docker容器化部署
查看>>
python基于flask搭建http服务(二)—— 实现Excel上传、数据清洗、入库
查看>>
python基于flask搭建http服务(三)—— 使用gunicorn部署项目
查看>>
python基于flask搭建http服务(一)—— 实现数据查询和Excel导出
查看>>
Python块键盘/鼠标输入
查看>>
python在心理学研究中的应用有哪些_心理学在线研究可用平台简介和应用进展
查看>>
python在使用HTMLTestRunner时,报告为空,错误提示<_io.TextIOWrapper name='<stderr>' mode='w' encoding='utf_8'>...
查看>>
python在gpu上运行_【python】python开启GPU加速
查看>>
Python在def函数中使用for循环
查看>>
Python在def函数中使用for循环
查看>>
Python在Conda环境中,但在Windows虚拟环境中没有激活
查看>>
python系列【仅供参考】:python pip 错误 ModuleNotFoundError: No module named pip._internal 解决办法
查看>>