Tengo 1000 hilos de Java dedicados donde cada hilo sondea una url correspondiente cada un segundo.BindException/Demasiados archivos abiertos durante el uso de HttpClient bajo carga
public class Poller {
public static Node poll(Node node) {
GetMethod method = null;
try {
HttpClient client = new HttpClient(new SimpleHttpConnectionManager(true));
......
} catch (IOException ex) {
ex.printStackTrace();
} finally {
method.releaseConnection();
}
}
}
Los hilos se ejecutan cada segundo:
for (int i=0; i <1000; i++) {
MyThread thread = threads.get(i) // threads is a static field
if(thread.isAlive()) {
// If the previous thread is still running, let it run.
} else {
thread.start();
}
}
El problema es que si corro el trabajo cada segundo consigo excepciones al azar como éstas:
java.net.BindException: Address already in use
INFO httpclient.HttpMethodDirector: I/O exception (java.net.BindException) caught when processing request: Address already in use
INFO httpclient.HttpMethodDirector: Retrying request
pero si ejecuta el trabajo cada 2 segundos o más, todo funciona bien.
Incluso traté de cerrar la instancia de SimpleHttpConnectionManager() usando shutDown() sin ningún efecto.
Si hago netstat, veo miles de conexiones TCP en estado TIME_WAIT, lo que significa que se han cerrado y están despejando.
Así que para limitar el nº de conexiones, He intentado utilizar una única instancia de HttpClient y utilizar de esta manera:
public class MyHttpClientFactory {
private static MyHttpClientFactory instance = new HttpClientFactory();
private MultiThreadedHttpConnectionManager connectionManager;
private HttpClient client;
private HttpClientFactory() {
init();
}
public static HttpClientFactory getInstance() {
return instance;
}
public void init() {
connectionManager = new MultiThreadedHttpConnectionManager();
HttpConnectionManagerParams managerParams = new HttpConnectionManagerParams();
managerParams.setMaxTotalConnections(1000);
connectionManager.setParams(managerParams);
client = new HttpClient(connectionManager);
}
public HttpClient getHttpClient() {
if (client != null) {
return client;
} else {
init();
return client;
}
}
}
Sin embargo después de correr durante exactamente 2 horas, se comienza a lanzar 'demasiados archivos abiertos' y eventualmente no puede hacer nada en absoluto.
ERROR java.net.SocketException: Too many open files
INFO httpclient.HttpMethodDirector: I/O exception (java.net.SocketException) caught when processing request: Too many open files
INFO httpclient.HttpMethodDirector: Retrying request
yo debería ser capaz de aumentar el nº de conexiones permitidas y hacer que funcione, pero yo sólo estaría prolongando el mal. ¿Alguna idea de cuál es la mejor práctica para usar HttpClient en una situación como la anterior?
Por cierto, todavía estoy en HttpClient3.1.
@Langali: ¡Oh, eso me enseñará a leer una publicación por completo! Otra cosa a considerar es si su límite de sistema operativo en el número de archivos abiertos por proceso es tal vez demasiado bajo. Extendimos la nuestra cuando descubrimos que Glassfish estaba usando casi toda su asignación para hacer cargas de clase, etc. Eso resolvió el problema para nosotros. –