博客
关于我
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/

你可能感兴趣的文章
Pandas数据可视化怎么做?用实战案例告诉你!
查看>>
Pandas数据处理与分析教程:从基础到实战
查看>>
Pandas数据结构之DataFrame常见操作
查看>>
pandas整合多份csv文件
查看>>
pandas某一列转数组list
查看>>
Pandas模块,我觉得掌握这些就够用了!
查看>>
Pandas玩转文本处理!
查看>>
SpringBoot 整合 Mybatis Plus 实现基本CRUD功能
查看>>
pandas的to_sql方法中使用if_exists=‘replace‘
查看>>
Springboot ppt转pdf——aspose方式
查看>>
pandas读取parquet报错
查看>>
pandas读取数据用来深度学习
查看>>
Pandas进阶大神!从0到100你只差这篇文章!
查看>>
spring5-介绍Spring框架
查看>>
pandas,python - 如何在时间序列中选择特定时间
查看>>
Spring 框架之 AOP 原理深度剖析
查看>>
Pandas:如何按列元素的组合分组,以指示基于不同列的值的同现?
查看>>
Pandas:将一列与数据帧的所有其他列进行比较
查看>>
PANDA:基于多列对数据表的行运行计算,并将输出存储在新列中
查看>>
PandoraFMS 监控软件 SQL注入漏洞复现
查看>>