httpclient实现http连接池

HTTP连接池是一种优化网络通信性能的技术,通过复用已建立的TCP连接减少重复握手开销,提升资源利用率。以下是关键要点:

核心原理与优势

  1. 连接复用机制

    • 维护活跃连接队列,避免每次请求重复TCP三次握手/SSL协商,降低延迟。
    • 典型场景:高频短请求(如API调用)性能提升可达300%。
  2. 资源控制能力

    • 限制最大连接数防止服务端过载,支持动态扩容应对流量峰值。
    • 内置连接有效性检测与自动重试,增强健壮性。

HttpClientConfig 配置中,使用了 Apache HttpClient 的 PoolingHttpClientConnectionManager 作为连接池管理器。其连接释放规则主要由以下几个方面决定:

1. 连接的生命周期

  • 空闲连接:连接池会自动管理空闲连接。当连接长时间未被使用时,连接池可以关闭这些空闲连接以释放资源。
  • 过期连接:如果服务器关闭了连接(比如 Keep-Alive 超时),连接池会检测到并清理这些已失效的连接。

2. 连接释放的时机

  • 请求完成后:当你通过 CloseableHttpClient 执行完一次 HTTP 请求后,连接不会被关闭,而是被"归还"到连接池中,供下次复用。
  • 显式关闭:如果你手动调用了 CloseableHttpResponse.close(),会释放底层连接到连接池。
  • 连接池自动清理:连接池会定期清理已过期或空闲时间过长的连接(需要在应用中显式调用 closeExpiredConnections() 和 closeIdleConnections(),或者通过后台线程自动清理)。

3. 相关参数

  • setMaxTotal(50):连接池最大连接数为 50。
  • setDefaultMaxPerRoute(20):每个路由(目标主机)最大连接数为 20。
  • (可选)RequestConfig 的超时设置(如连接超时、请求超时、读取超时)会影响连接的生命周期,但不会直接导致连接被关闭,只是影响请求的超时行为。

4. 连接池释放的最佳实践

  • 及时关闭响应:每次请求后,务必关闭 CloseableHttpResponse,否则连接不会被归还到池中,可能导致连接泄漏。
  • 定期清理:可以通过定时任务调用 PoolingHttpClientConnectionManager 的 closeExpiredConnections() 和 closeIdleConnections(long idleTime, TimeUnit t) 方法,清理无效连接。

1、引入pom

XML 复制代码
     <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.14</version>
        </dependency>

2、监控连接池情况

java 复制代码
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

@Component
public class HttpClientPoolMonitor {

    @Resource
    private PoolingHttpClientConnectionManager manager;

    @Scheduled(fixedRate = 5000)
    public void reportStats() {
        int total = manager.getTotalStats().getLeased() + manager.getTotalStats().getAvailable();
        System.out.println("[HttpClientPool] Leased: " + manager.getTotalStats().getLeased()
                + ", Available: " + manager.getTotalStats().getAvailable()
                + ", Max: " + manager.getMaxTotal()
                + ", Total: " + total);
        int a = 0;
    }
} 

在HttpClient连接池中,这些参数分别表示以下含义:

  1. Leased‌:当前正在被使用的连接数量,反映活跃连接状态
  2. Available‌:连接池中可立即复用的空闲连接数量
  3. Max‌:连接池允许创建的最大连接总数(maxTotal),控制总体资源消耗
  4. Total‌:当前连接池中连接总数(Leased + Available),反映实际连接占用情况

连接池的关键工作机制:

  • 当Leased达到Max时,新请求需要等待可用连接
  • Available连接会被优先复用,减少新建连接开销
  • 合理设置Max值需要平衡并发需求和系统资源

3、连接池配置

java 复制代码
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class HttpClientConfig {

    @Bean
    public PoolingHttpClientConnectionManager poolingHttpClientConnectionManager() {
        PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager();
        manager.setMaxTotal(50); // 最大连接数
        manager.setDefaultMaxPerRoute(20); // 每个路由最大连接数
        return manager;
    }

 /*   @Bean
    public CloseableHttpClient httpClient(PoolingHttpClientConnectionManager manager) {
        return HttpClients.custom()
                .setConnectionManager(manager)
                .build();
    }*/

//设置超时时间

    @Bean
    public CloseableHttpClient httpClient(PoolingHttpClientConnectionManager manager) {
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectTimeout(2000)
                .setConnectionRequestTimeout(2000)
                .setSocketTimeout(2000)
                .build();

        return HttpClients.custom()
                .setConnectionManager(manager)
                .setDefaultRequestConfig(requestConfig)
                .build();
    }
} 

4、demo

java 复制代码
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Date;

@Service
public class HttpClientDemoService {

    @Resource
    private CloseableHttpClient httpClient;

    public String doGet(String url) {
        try {
            HttpGet request = new HttpGet(url);
            try (CloseableHttpResponse response = httpClient.execute(request)) {
                BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
                StringBuilder result = new StringBuilder();
                String line;
                while ((line = reader.readLine()) != null) {
                    result.append(line);
                }
                return result.toString();
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
//超时设置
public String doGet2(String url) {
    try {
        // 设置超时时间
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectTimeout(5000)      // 连接超时,单位毫秒
                .setConnectionRequestTimeout(3000) // 从连接池获取连接超时
                .setSocketTimeout(10000)      // 读取超时
                .build();

        HttpGet request = new HttpGet(url);
        request.setConfig(requestConfig);

        try (CloseableHttpResponse response = httpClient.execute(request)) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
            StringBuilder result = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                result.append(line);
            }
            return result.toString();
        }
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

    @Scheduled(fixedRate = 50)
    public void scheduledTask() {
       // System.out.println("new Date() = " + new Date());


       // System.out.println("a + new Date() = " + a + new Date());

    for (int i = 0; i < 10; i++) {
        new Thread(this::callHTttp).start();
    }


    }

    public void callHTttp(){
        String url = "http://127.0.0.1:8080/api/producer/send?message=HelloWorld!";
        url = "https://devapi.qweather.com/v7/weather/3d?location=北京&key=YOUR_KEY";
        url = "http://127.0.0.1:7700/openApi/test";
        String a =  doGet(url);
        System.out.println("a = " +a);
    }
} 

5、定时调用http

java 复制代码
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestTemplateConfig {
    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder.build();
    }
}


import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@RestController
public class TestController {

    public String test(){
        return "hello world";
    }
    @Resource
    private RestTemplate restTemplate;

    @GetMapping("/api/producer/send")
    public String send() {
        return "Message sent!";
    }

    @Scheduled(fixedRate = 5000)
    public void scheduledTask() {
        String url = "http://127.0.0.1:8080/api/producer/send?message=Hello, World!";
        //String result = restTemplate.getForObject(url, String.class);
        //System.out.println("HTTP GET Response: " + result);
    }
}
相关推荐
sweet丶5 分钟前
MQTT消息通道-基础篇
网络协议
yychen_java1 小时前
当算法成为武器:AI泛滥时代的多维危机透视与治理路径
网络·人工智能·ai
漫途科技1 小时前
精准盯防危房隐患,智守人居安全|MTB46-4-2A 4G数据采集终端专项应用方案
网络·安全
奥利奥夹心脆芙2 小时前
辅助排查 HTTP 接口代码报错,实操完整案例分享
http
Misnearch2 小时前
抓包Packet Capture
网络·抓包
zhangfeng11332 小时前
ps aux讲解,结合国家超算中心 hpc apptainer
linux·服务器·网络
吠品3 小时前
一次 Nginx 报错 unexpected end of file 的排查记录
网络协议·https·ssl
代码中介商3 小时前
TLS握手全解析:从1.2到1.3的加密演进
网络·网络协议·http
xlq223223 小时前
66.ip
网络·网络协议·tcp/ip
tudoSearcher3 小时前
手机、平板、电脑同时控制Claude Code / Codex ?:Paseo实战指南
网络·开源·开源软件·个人开发·ai编程