2014-09-21 12 views
7

a scopo di test/di benchmarking, voglio scrivere un programma Java che fa i seguenti compiti in un ciclo:HttpURLConnection: BindException durante la creazione di molte connessioni

  1. caricare i dati via HTTP GET da un server
  2. (generare una risposta sulla base dei dati ricevuti - non è importante a questo punto)
  3. inviare la risposta tramite HTTP POST allo stesso server

questo ciclo viene eseguito su più thread allo stesso tempo .

Dopo l'avvio, il programma funziona correttamente per un breve periodo di tempo ed è in grado di eseguire ~ 300 cicli per thread al secondo (il server Web viene eseguito sulla stessa macchina). Ma dopo 5-7 secondi, ricevo BindException: Address already in use.

Il riavvio del programma dopo un tempo di raffreddamento di 20-30 secondi comporta lo stesso comportamento; quando lo riavvio immediatamente senza aspettare, si blocca immediatamente ... quindi suppongo che potrebbe essere un problema con risorse associate.

È un approccio rapido e sporco utilizzando HttpURLConnection. Le parti rilevanti:

ottenere i dati dal server web

public String fetchData() throws IOException { 

     HttpURLConnection conn = (HttpURLConnection)url.openConnection(); 
     conn.setUseCaches(false); 
     conn.setRequestMethod("GET"); 

     BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream())); 
     String inputLine; 
     StringBuffer response = new StringBuffer(); 

     while ((inputLine = in.readLine()) != null) { 
      response.append(inputLine); 
     } 

     in.close(); 
     conn.disconnect(); 

     return response.toString(); 
    } 

invio la risposta

public void sendData(byte[] data) throws IOException { 

     HttpURLConnection conn = (HttpURLConnection)url.openConnection(); 
     conn.setUseCaches(false); 
     conn.setRequestMethod("POST"); 
     conn.setDoOutput(true); 

     OutputStream os = conn.getOutputStream(); 
     os.write(data);  
     os.close(); 
     conn.disconnect(); 
    } 

Chiamando entrambi i metodi all'interno del filo

@Override 
public void run() { 
    while(true) { 
     try { 
      String data = fetchData(); 
      String answer = // ... generating answer 
      sendData(answer.getBytes("UTF-8")); 
     } catch (IOException e) { 
      // ... 
     } 
    } 
} 

Non esiste un singolo oggetto URL condiviso tra thread: ogni thread ha una propria istanza URL (tuttavia, ogni istanza punta allo stesso indirizzo).

edit:

Ecco la traccia dello stack della prima eccezione che si verifica:

java.net.BindException: Address already in use: connect 
    at java.net.DualStackPlainSocketImpl.connect0(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 java.net.Socket.connect(Unknown Source) 
    at sun.net.NetworkClient.doConnect(Unknown Source) 
    at sun.net.www.http.HttpClient.openServer(Unknown Source) 
    at sun.net.www.http.HttpClient.openServer(Unknown Source) 
    at sun.net.www.http.HttpClient.<init>(Unknown Source) 
    at sun.net.www.http.HttpClient.New(Unknown Source) 
    at sun.net.www.http.HttpClient.New(Unknown Source) 
    at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(Unknown Source) 
    at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(Unknown Source) 
    at sun.net.www.protocol.http.HttpURLConnection.plainConnect(Unknown Source) 
    at sun.net.www.protocol.http.HttpURLConnection.connect(Unknown Source) 
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(Unknown Source) 
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source) 
    at PayloadProcessor.fetchData(PayloadProcessor.java:66) 
    at PayloadProcessor.run(PayloadProcessor.java:32) 
    at java.lang.Thread.run(Unknown Source) 

Essa si verifica nel metodo fetchdata alla seguente riga:

in = new BufferedReader(new InputStreamReader(conn.getInputStream())); 

Ho rimosso le chiamate a disconnect() (come sottolineato da Aaron) - sfortunatamente, lo stesso problema esiste ancora.


Supponiamo tutto è chiuso in modo corretto (non so se questo è il caso qui, basta lasciare per scontato che) - potrebbe essere il caso che il programma è semplicemente troppo veloce? Ho trovato un altro post su StackOverflow, la "soluzione" era di aggiungere un semplice Thread.sleep - ma dal momento che è un punto di riferimento e dovrebbe funzionare il più velocemente possibile, non mi piace. In che modo gli strumenti di test del carico gestiscono questo problema?


Ho ridotto la quantità di thread a 1, anche se il problema si verifica.

+1

Inserendo qui la traccia di stack completa dell'errore potrebbe essere utile. Ad ogni modo, la prima cosa che ti consiglierei è di racchiudere le invocazioni _disconnect_ in una clausola _finally_: Forse ci sono così tante eccezioni che le connessioni non vengono chiuse in modo propizio, causando una perdita. –

+0

Grazie, ho aggiunto la traccia dello stack. Hai ragione, c'erano davvero molte eccezioni - ma a scopo di debug, ora termino il programma dopo che la prima eccezione si è sollevata (non mostrata nel codice sopra) e continua ad avere lo stesso problema. La prima eccezione è già una BindException. – ceran

+0

Si prega di aggiungere i risultati del comando 'netstat -anpt | grep 'o il suo analogo nel tuo sistema operativo. – user1516873

risposta

2

Sembra che si stiano esaurendo le porte locali. È possibile che ci siano troppe connessioni in spawn in parallelo e che non rilascino le loro risorse abbastanza velocemente da poter riutilizzare altri thread?

Mi piacerebbe sbarazzarsi della chiamata a disconnect() in quanto praticamente disabiliterà il pool di connessioni per voi. Da i documenti:

Indica che altre richieste al server sono improbabili nel prossimo futuro. Chiamare disconnect() non dovrebbe implicare che questa istanza HttpURLConnection possa essere riutilizzata per altre richieste.

+0

Grazie. Ho rimosso entrambe le chiamate a 'disconnect()' ma sfortunatamente non ha cambiato nulla. Condivido la tua opinione riguardo la mancanza di porti, ma non so perché succede. Il programma è troppo veloce? Vedi la mia modifica – ceran

+0

Hmm ... qualcosa è decisamente sbagliato. Potresti mostrarci cosa c'è nel tuo blocco di cattura? – Aaron

1

Penso che si stia esaurendo la porta locale per verificare ciò: Esegui il programma ed esegui il comando netstat. Prova a utilizzare il pool di connessioni per la connessione http HTTP connection pooling using HttpClient

+0

Inoltre, consiglierei di utilizzare HttpClient anziché HttpURLConnection diretto. Ci sono tutti i tipi di cose orribili che possono andare storto usando HttpURLConnection direttamente che HttpClient si prenderà cura di te. – hooknc

0

Sembra che tu stia finendo le porte locali. Per evitare ciò, è necessario assicurarsi che le connessioni vengano riutilizzate. Java proverà a do this for you, ma è necessario leggere l'intera risposta per ogni richiesta.

Mentre la funzione fetchData legge le risposte, non lo è sendData. Sebbene non sia necessario il corpo di risposta qui, dovresti leggerlo comunque (fino alla fine) e quindi chiudere il corrispondente InputStream. Quindi questa connessione potrebbe essere riutilizzata.

Inoltre non disconnect le connessioni.

This question potrebbe essere utile.

0

Per recuperare e inviare dati, è necessario creare uno HttpURLConnection per ogni fetchData e sendData chiamata. Ciò significa che stai facendo troppe connessioni al server. Come già sottolineato, ciò renderà disponibili tutti i numeri di porta locali disponibili.

Basta creare uno HttpURLConnection per thread. Suggerisco vivamente di refactoring del codice dato come segue:

@Override 
public void run() { 
    HttpURLConnection conn = (HttpURLConnection)url.openConnection(); 
    while(true) { 
     try { 
      String data = fetchData(conn); 
      String answer = // ... generating answer 
      sendData(conn, answer.getBytes("UTF-8")); 
     } catch (IOException e) { 
      // ... 
     } 
    } 
} 

Che è, si crea l'oggetto HttpURLConnection una volta al di fuori del ciclo while, e passarlo al fetchData e sendData come argomento.

0

La soluzione semplice è solo introdurre un ritardo prima di rispondere.

String data = fetchData(conn); 
String answer = // ... generating answer 
Thread.sleep(1000); 
sendData(conn, answer.getBytes("UTF-8")); 
Problemi correlati