HTTP連接池是一種優化網絡通信性能的技術,通過復用已建立的TCP連接減少重復握手開銷,提升資源利用率。以下是關鍵要點:
核心原理與優勢
-
?連接復用機制?
- 維護活躍連接隊列,避免每次請求重復TCP三次握手/SSL協商,降低延遲。
- 典型場景:高頻短請求(如API調用)性能提升可達300%。
-
?資源控制能力?
- 限制最大連接數防止服務端過載,支持動態擴容應對流量峰值。
- 內置連接有效性檢測與自動重試,增強健壯性。
----------------
?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
<dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.14</version></dependency>
2、監控連接池情況
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 {@Resourceprivate 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連接池中,這些參數分別表示以下含義:
- ?Leased?:當前正在被使用的連接數量,反映活躍連接狀態
- ?Available?:連接池中可立即復用的空閑連接數量
- ?Max?:連接池允許創建的最大連接總數(maxTotal),控制總體資源消耗
- ?Total?:當前連接池中連接總數(Leased + Available),反映實際連接占用情況
連接池的關鍵工作機制:
- 當Leased達到Max時,新請求需要等待可用連接
- Available連接會被優先復用,減少新建連接開銷
- 合理設置Max值需要平衡并發需求和系統資源
3、連接池配置
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 {@Beanpublic PoolingHttpClientConnectionManager poolingHttpClientConnectionManager() {PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager();manager.setMaxTotal(50); // 最大連接數manager.setDefaultMaxPerRoute(20); // 每個路由最大連接數return manager;}/* @Beanpublic CloseableHttpClient httpClient(PoolingHttpClientConnectionManager manager) {return HttpClients.custom().setConnectionManager(manager).build();}*///設置超時時間@Beanpublic 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
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 {@Resourceprivate 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
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 {@Beanpublic 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";}@Resourceprivate 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);}
}