服务器上的 API 支持
API 支持一次请求多个 ID 的可能性很小(例如,使用 http://endpoint:80/.../itemId1,itemId2,itemId3 形式的 URL)。检查 API 文档,看看这是否可用,因为如果是这样,那将是最好的解决方案。
持久连接
看起来 Apache HttpClient 默认使用持久(“保持活动”)连接(请参阅 @kichik's comment 中链接的 Connection Management tutorial)。 logging facilities 可以帮助验证连接是否被多个请求重用。
要释放客户端,请使用close() 方法。来自2.3.4. Connection manager shutdown:
当不再需要 HttpClient 实例并且即将超出范围时,关闭其连接管理器以确保关闭由管理器保持活动状态的所有连接并释放由这些连接分配的系统资源,这一点很重要。
CloseableHttpClient httpClient = <...>
httpClient.close();
持久连接消除了建立新连接的开销,但正如您所注意到的,客户端在发送下一个请求之前仍会等待响应。
多线程和连接池
您可以使您的程序多线程并使用PoolingHttpClientConnectionManager 来控制与服务器建立的连接数。以下是基于2.3.3. Pooling connection manager 和2.4. Multithreaded request execution 的示例:
import java.io.*;
import org.apache.http.*;
import org.apache.http.client.*;
import org.apache.http.client.methods.*;
import org.apache.http.client.protocol.*;
import org.apache.http.impl.client.*;
import org.apache.http.impl.conn.*;
import org.apache.http.protocol.*;
// ...
PoolingHttpClientConnectionManager cm =
new PoolingHttpClientConnectionManager();
cm.setMaxTotal(200); // increase max total connection to 200
cm.setDefaultMaxPerRoute(20); // increase max connection per route to 20
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(cm)
.build();
String[] urisToGet = { ... };
// start a thread for each URI
// (if there are many URIs, a thread pool would be better)
Thread[] threads = new Thread[urisToGet.length];
for (int i = 0; i < threads.length; i++) {
HttpGet httpget = new HttpGet(urisToGet[i]);
threads[i] = new Thread(new GetTask(httpClient, httpget));
threads[i].start();
}
// wait for all the threads to finish
for (int i = 0; i < threads.length; i++) {
threads[i].join();
}
class GetTask implements Runnable {
private final CloseableHttpClient httpClient;
private final HttpContext context;
private final HttpGet httpget;
public GetTask(CloseableHttpClient httpClient, HttpGet httpget) {
this.httpClient = httpClient;
this.context = HttpClientContext.create();
this.httpget = httpget;
}
@Override
public void run() {
try {
CloseableHttpResponse response = httpClient.execute(
httpget, context);
try {
HttpEntity entity = response.getEntity();
} finally {
response.close();
}
} catch (ClientProtocolException ex) {
// handle protocol errors
} catch (IOException ex) {
// handle I/O errors
}
}
}
多线程有助于使链路饱和(保持尽可能多的数据流动),因为当一个线程发送请求时,其他线程可以接收响应并利用下行链路。
流水线
HTTP/1.1 支持pipelining,它在一个连接上发送多个请求,而无需等待响应。 Asynchronous I/O based on NIO tutorial 在3.10. Pipelined request execution 部分中有一个示例:
HttpProcessor httpproc = <...>
HttpAsyncRequester requester = new HttpAsyncRequester(httpproc);
HttpHost target = new HttpHost("www.apache.org");
List<BasicAsyncRequestProducer> requestProducers = Arrays.asList(
new BasicAsyncRequestProducer(target, new BasicHttpRequest("GET", "/index.html")),
new BasicAsyncRequestProducer(target, new BasicHttpRequest("GET", "/foundation/index.html")),
new BasicAsyncRequestProducer(target, new BasicHttpRequest("GET", "/foundation/how-it-works.html"))
);
List<BasicAsyncResponseConsumer> responseConsumers = Arrays.asList(
new BasicAsyncResponseConsumer(),
new BasicAsyncResponseConsumer(),
new BasicAsyncResponseConsumer()
);
HttpCoreContext context = HttpCoreContext.create();
Future<List<HttpResponse>> future = requester.executePipelined(
target, requestProducers, responseConsumers, pool, context, null);
HttpCore Examples(“Pipelined HTTP GET requests”)中有此示例的完整版本。
较旧的 Web 服务器可能无法正确处理流水线请求。