Cấu trúc thiết kế trong HttpClient
Apache HttpClient được xây dựng dựa trên các mẫu thiết kế (design patterns) kinh điển để đảm bảo tính linh hoạt. Thư viện này sử dụng Facade mẫu thông qua class HttpClient nhằm che giấu sự phức tạp của hệ thống con phía sau. Bên cạnh đó, các bộ chặn giao thức (Protocol Interceptors) áp dụng Decorator mẫu, cho phép người dùng thêm các xử lý tùy chỉnh vào yêu cầu hoặc phản hồi mà không làm thay đổi logic cốt lõi.
Quản lý Header và Entity
Khi làm việc với các Header có nhiều phần tử, việc sử dụng HeaderElementIterator sẽ hiệu quả hơn so với việc phân tách thủ công:
HeaderIterator headerIt = response.headerIterator("Set-Cookie");
HeaderElementIterator elementIt = new BasicHeaderElementIterator(headerIt);
while (elementIt.hasNext()) {
HeaderElement element = elementIt.nextElement();
System.out.println(element.getName() + " = " + element.getValue());
}
Một sai lầm phổ biến là sử dụng EntityUtils.toString() cho các phản hồi có dung lượng lớn, dẫn đến tốn bộ nhớ. Cách tiếp cận an toàn là kiểm tra kích thước trước khi xử lý:
try (CloseableHttpClient client = HttpClients.createDefault()) {
HttpGet request = new HttpGet("http://api.example.com/data");
try (CloseableHttpResponse resp = client.execute(request)) {
HttpEntity entity = resp.getEntity();
if (entity != null) {
long contentLength = entity.getContentLength();
// Chỉ đọc trực tiếp nếu nội dung nhỏ hơn 4KB
if (contentLength != -1 && contentLength < 4096) {
String body = EntityUtils.toString(entity);
System.out.println(body);
} else {
// Sử dụng Stream để xử lý dữ liệu lớn
InputStream is = entity.getContent();
// Xử lý luồng tại đây
}
}
}
}
Nếu cần đọc nội dung của một HttpEntity nhiều lần, bạn nên bao bọc nó trong BufferedHttpEntity để lưu trữ dữ liệu vào bộ nhớ đệm.
Quản lý kết nối và Pool
Việc khởi tạo kết nối mới cho mỗi yêu cầu HTTP là một thao tác tốn kém. HttpClient cung cấp PoolingHttpClientConnectionManager để quản lý và tái sử dụng các kết nối trong môi trường đa luồng.
PoolingHttpClientConnectionManager poolManager = new PoolingHttpClientConnectionManager();
// Cấu hình tối đa 100 kết nối tổng cộng
poolManager.setMaxTotal(100);
// Tối đa 20 kết nối cho mỗi route (host) cụ thể
poolManager.setDefaultMaxPerRoute(20);
CloseableHttpClient customClient = HttpClients.custom()
.setConnectionManager(poolManager)
.build();
Để tránh việc các kết nối bị "treo" hoặc hết hạn trong pool, bạn nên có một luồng giám sát riêng để dọn dẹp:
public class ConnectionWatchdog extends Thread {
private final HttpClientConnectionManager manager;
private volatile boolean isStopped;
public ConnectionWatchdog(HttpClientConnectionManager manager) {
this.manager = manager;
}
@Override
public void run() {
try {
while (!isStopped) {
synchronized (this) {
wait(10000); // Kiểm tra mỗi 10 giây
manager.closeExpiredConnections();
manager.closeIdleConnections(45, TimeUnit.SECONDS);
}
}
} catch (InterruptedException ex) {
// Ngắt quãng
}
}
public void halt() {
isStopped = true;
synchronized (this) {
notifyAll();
}
}
}
Xác thực và Quản lý Trạng thái
HttpClient hỗ trợ nhiều cơ chế xác thực như Basic, Digest, NTLM và Kerberos. Để quản lý thông tin đăng nhập, chúng ta sử dụng CredentialsProvider:
CredentialsProvider creds = new BasicCredentialsProvider();
creds.setCredentials(
new AuthScope("remote-host", 443),
new UsernamePasswordCredentials("admin", "secret_pass")
);
HttpClientContext apiContext = HttpClientContext.create();
apiContext.setCredentialsProvider(creds);
// Thực thi với context để duy trì trạng thái xác thực
CloseableHttpResponse response = client.execute(new HttpGet("/secure-api"), apiContext);
Đối với Cookie, bạn có thể thiết lập các chính sách khác nhau (như Standard hoặc Ignore cookies) thông qua RequestConfig để kiểm soát cách trình duyệt giả lập xử lý dữ liệu phiên.
Thực thi bất đồng bộ với FutureRequestExecutionService
Để gửi nhiều yêu cầu cùng lúc mà không làm tắc nghẽn luồng chính, bạn có thể kết hợp HttpClient với ExecutorService:
ExecutorService threadPool = Executors.newFixedThreadPool(10);
FutureRequestExecutionService asyncService =
new FutureRequestExecutionService(customClient, threadPool);
HttpRequestFutureTask<Boolean> task = asyncService.execute(
new HttpGet("https://google.com"),
HttpClientContext.create(),
response -> response.getStatusLine().getStatusCode() == 200
);
// Lấy kết quả sau đó
boolean isSuccess = task.get();
Cơ chế này cho phép theo dõi các thông số như taskDuration (thời gian thực hiện tác vụ) và requestDuration (thời gian phản hồi HTTP thực tế) để tối ưu hóa hệ thống.