Tomcat 8.5 WebサーバーにアクセスするJava "client"でパフォーマンステストを実行しています。約13,000のリクエストの後、HTTPリクエストはエラーで失敗し、
org.Apache.http.impl.client.DefaultHttpClient tryConnect
INFO: Retrying connect
Java.net.BindException: Address already in use: connect
at Java.net.DualStackPlainSocketImpl.waitForConnect(Native Method)
at Java.net.DualStackPlainSocketImpl.socketConnect(Unknown Source)
at Java.net.AbstractPlainSocketImpl.doConnect(Unknown Source)
at Java.net.AbstractPlainSocketImpl.connectToAddress(Unknown Source)
at Java.net.AbstractPlainSocketImpl.connect(Unknown Source)
at Java.net.PlainSocketImpl.connect(Unknown Source)
at Java.net.SocksSocketImpl.connect(Unknown Source)
at Java.net.Socket.connect(Unknown Source)
at org.Apache.http.conn.scheme.PlainSocketFactory.connectSocket(PlainSocketFactory.Java:127)
at org.Apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.Java:180)
at org.Apache.http.impl.conn.ManagedClientConnectionImpl.open(ManagedClientConnectionImpl.Java:294)
at org.Apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.Java:643)
at org.Apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.Java:479)
at org.Apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.Java:906)
at org.Apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.Java:805)
at org.Apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.Java:784)
コードは、
for (int i = 0; i < 15000; i++) {
try {
if (i % 1000 == 0) System.out.println("Iterations: " + Integer.toString(i));
HttpGet request = new HttpGet("http://localhost:9080");
DefaultHttpClient client = new DefaultHttpClient();
HttpResponse response = client.execute(request, new BasicHttpContext());
HttpEntity entity = response.getEntity();
EntityUtils.consume(entity);
} catch (Exception e) {
e.printStackTrace();
System.out.println("Iterations: " + Integer.toString(i));
System.exit(1);
}
}
DefaultHttpClientをキャッシュした場合、エラーは発生しません。また試しました、
request.releaseConnection();
client.getConnectionManager().shutdown();
しかし、エラーは変わりません。エラーはクライアントが原因とは思われません。 URLで別のウェブサイトにアクセスしても問題ありません。ファイルハンドルまたはソケットリソースなどが不足しているWindowsのTomcatが原因のようです。クラッシュした直後に別のプロセスとして再度実行すると、13,000回ではなく1回の実行で失敗するため、Tomcatでリソースが不足していることが問題です。 DefaultHttpClientが接続を閉じていないか、GCが発生するまでTomcatが接続を解放していないようです。
HTTPClient 4.2.5の使用
なぜそれが発生するのか、またはどのように修正するのですか?
ベストプラクティスは、MultiThread HttpClient
を使用することです。以下はうまくいくはずです:
package com.demo.httpclient;
import Java.net.URI;
import Java.util.ArrayList;
import Java.util.List;
import Java.util.concurrent.Callable;
import Java.util.concurrent.ExecutorService;
import Java.util.concurrent.Executors;
import Java.util.concurrent.Future;
import org.Apache.http.HttpEntity;
import org.Apache.http.HttpResponse;
import org.Apache.http.client.HttpClient;
import org.Apache.http.client.methods.HttpGet;
import org.Apache.http.impl.client.DefaultHttpClient;
import org.Apache.http.impl.conn.DefaultClientConnection;
import org.Apache.http.impl.conn.PoolingClientConnectionManager;
import org.Apache.http.util.EntityUtils;
public class ThreadedHttpClientTest {
private static URI rootUri = URI.create("http://localhost:8080/");
private static int worker = 100;
private static int count = 15000;
private static HttpClient httpClient;
public static void main(String[] args) throws Exception {
long startTime = System.currentTimeMillis();
PoolingClientConnectionManager poolingClientConnectionManager = new PoolingClientConnectionManager();
poolingClientConnectionManager.setMaxTotal(worker);
poolingClientConnectionManager.setDefaultMaxPerRoute(worker);
httpClient = new DefaultHttpClient(poolingClientConnectionManager);
List<Callable<Void>> workers = new ArrayList<Callable<Void>>();
for (int i = 0; i < count; i++) {
workers.add(new WorkerThread(httpClient, rootUri.toString()));
}
ExecutorService pool = Executors.newFixedThreadPool(worker);
int i=0;
for (Future<Void> future : pool.invokeAll(workers)) {
future.get();
System.out.println("Response " + i++);
}
System.out.println("Time Taken :: " + (System.currentTimeMillis() - startTime) + "ms");
pool.shutdown();
}
static class WorkerThread implements Callable<Void> {
HttpClient client;
String url;
public WorkerThread(HttpClient httpClient, String url) {
this.client = httpClient;
this.url = url;
}
@Override
public Void call() throws Exception {
HttpGet get = new HttpGet(url);
HttpResponse response = client.execute(get, new DefaultClientConnection());
HttpEntity entity = response.getEntity();
EntityUtils.consume(entity);
return null;
}
}
}