2010-05-26 15 views
5

Ho 1000 thread Java dedicati in cui ogni thread esegue il polling di un url corrispondente ogni secondo.BindException/Troppi file aperti durante l'utilizzo di HttpClient sotto carico

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(); 
     } 
    } 
} 

I fili gestiscono ogni secondo:

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(); 
    } 
} 

Il problema è se corro il lavoro ogni secondo ottengo eccezioni casuali come questi:

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 

Ma se io eseguire il lavoro ogni 2 secondi o più, tutto funziona correttamente.

Ho anche provato a chiudere l'istanza di SimpleHttpConnectionManager() usando shutDown() senza alcun effetto.

Se eseguo netstat, vedo migliaia di connessioni TCP nello stato TIME_WAIT, il che significa che sono state chiuse e si stanno cancellando.

Quindi, per limitare il no di connessioni, ho provato ad utilizzare una singola istanza di HttpClient e usarlo in questo modo:

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; 
       } 
     } 
} 

Tuttavia dopo l'esecuzione per esattamente 2 ore, inizia gettando 'troppi file aperti' e alla fine non può fare nulla.

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 

dovrei essere in grado di aumentare il no di connessioni consentite e farlo funzionare, ma mi sarebbe solo prolungare il male. Qualche idea su quale sia la migliore pratica per usare HttpClient in una situazione come quella precedente?

Btw, sono ancora su HttpClient3.1.

risposta

2

Non c'è niente di sbagliato nel primo errore. Hai esaurito le porte empiriche disponibili. Ogni connessione TCP può rimanere nello stato TIME_WAIT per 2 minuti. Genera 2000/secondi. Presto o tardi, il socket non riesce a trovare alcuna porta locale inutilizzata e si otterrà quell'errore. TIME_WAIT progettato esattamente per questo scopo. Senza di esso, il tuo sistema potrebbe dirottare una connessione precedente.

Il secondo errore indica che ci sono troppi socket aperti. Su alcuni sistemi, esiste un limite di file aperti 1K. Forse hai appena raggiunto quel limite a causa di socket persistenti e altri file aperti. Su Linux, è possibile modificare questo limite utilizzando

ulimit -n 2048 

Ma questo è limitato da un valore massimo del sistema.

3

Questo è successo a noi alcuni mesi fa. Innanzitutto, ricontrolla per assicurarti di chiamare davvero releaseConnection() ogni volta. Ma anche in questo caso, il sistema operativo in realtà non recupera le connessioni TCP in una sola volta. La soluzione è utilizzare il client HTTP Apache MultiThreadedHttpConnectionManager. Questo raggruppa e riutilizza le connessioni.

Vedere http://hc.apache.org/httpclient-3.x/performance.html per ulteriori suggerimenti sulle prestazioni.

Aggiornamento: Whoops, non ho letto l'esempio di codice inferiore. Se si sta eseguendo releaseConnection() e si utilizza MultiThreadedHttpConnectionManager, considerare se il limite del sistema operativo sui file aperti per processo è impostato su un valore sufficientemente elevato. Abbiamo avuto anche questo problema e avevamo bisogno di estendere un po 'il limite.

+0

@Langali: Oh duh, questo mi insegnerà a leggere un post completamente! Un'altra cosa da considerare è se il limite del sistema operativo sul numero di file aperti per processo è forse impostato un po 'troppo basso. Abbiamo esteso la nostra quando abbiamo scoperto che Glassfish utilizzava quasi tutto il suo lotto per eseguire il caricamento delle classi, ecc. Abbiamo risolto il problema per noi. –

0

Come sudo o root modificare il file /etc/security/limits.conf.Alla fine del file appena sopra "# Fine del file" immettere i seguenti valori: * nofile morbido 65535 * nofile duro 65535 Questo imposterà il numero di file aperti a illimitato.

Problemi correlati