2011-01-18 11 views
12

Il mio server utilizza i dati da un servizio Web interno per costruire la risposta, in base alle richieste. Sto usando Apache HttpClient 4.1 per fare le richieste. Ogni richiesta iniziale comporterà circa 30 richieste al servizio web. Di questi, 4 - 8 finiranno con socket bloccati in CLOSE_WAIT, che non verranno mai rilasciati. Alla fine questi socket bloccati superano il mio limite e il mio processo si esaurisce nei descrittori di file.Come posso assicurarmi che il mio HttpClient 4.1 non abbia perdite di prese?

Non voglio solo aumentare il mio ulimit (1024), perché questo si limita a mascherare il problema.

Il motivo per cui mi sono spostato su HttpClient è che java.net.HttpUrlConnection si comportava allo stesso modo.

Ho provato a passare a un SingleClientConnManager per richiesta ea chiamare client.getConnectionManager(). Shutdown() su di esso, ma i socket rimangono bloccati.

Dovrei provare a risolvere questo in modo che finisca con 0 socket aperti mentre non ci sono richieste in esecuzione, o dovrei concentrarmi sulla persistenza delle richieste e sul pool?

Per chiarezza sto compresi alcuni dettagli che possono essere rilevanti:

OS: Ubuntu 10.10

JRE: 1.6.0_22

Lingua: Scala 2.8

codice

campione:

val cleaner = Executors.newScheduledThreadPool(1) 
private val client = { 
    val ssl_ctx = SSLContext.getInstance("TLS") 
    val managers = Array[TrustManager](TrustingTrustManager) 
    ssl_ctx.init(null, managers, new java.security.SecureRandom()) 
    val sslSf = new org.apache.http.conn.ssl.SSLSocketFactory(ssl_ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER) 
    val schemeRegistry = new SchemeRegistry() 
    schemeRegistry.register(new Scheme("https", 443, sslSf)) 
    val connection = new ThreadSafeClientConnManager(schemeRegistry) 
    object clean extends Runnable{ 
     override def run = { 
      connection.closeExpiredConnections 
      connection.closeIdleConnections(30, SECONDS) 
     } 
    } 
    cleaner.scheduleAtFixedRate(clean,10,10,SECONDS) 
    val httpClient = new DefaultHttpClient(connection) 
    httpClient.getCredentialsProvider().setCredentials(new AuthScope(AuthScope.ANY), new UsernamePasswordCredentials(username,password)) 
    httpClient 
} 
val get = new HttpGet(uri) 
val entity = client.execute(get).getEntity 
val stream = entity.getContent 
val justForTheExample = IOUtils.toString(stream) 
stream.close() 

Test: netstat -a | grep {myInternalWebServiceName} | grep CLOSE_WAIT

(Liste prese per il mio processo che si trovano in stato CLOSE_WAIT)

Invia un commento discussione:

Questo codice ora dimostra un uso corretto.

risposta

8

Uno ha bisogno di sfrattare proattivamente le connessioni scadute/inattive dal pool di connessioni, poiché le connessioni del modello I/O di blocco non possono reagire agli eventi I/O a meno che siano letti/scritti da. Per i dettagli vedere

http://hc.apache.org/httpcomponents-client-dev/tutorial/html/connmgmt.html#d4e631

+0

Grazie per la risposta, sono d'accordo che la documentazione suggerisce che dovrebbe essere una misura efficace. Il numero di socket orfani in CLOSE_WAIT cresce ancora in modo affidabile con il depuratore off-thread implementato, quindi non è stato efficace. Ho aggiunto alcuni dettagli di implementazione alla mia domanda. –

+0

I ritroso. Non mi ero reso conto che nell'effettivo codice dell'applicazione stavo facendo richieste di immagini supplementari per eseguire la logica di business. Stavano ancora usando i metodi WS.url prima dell'introduzione di HttpClient e stavano lasciando i socket alle spalle. –

+0

Collegamento interrotto. (Si prega di includere titoli e descrizioni con collegamenti.) Forse questa è la stessa informazione? http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html – danorton

2

Ho segnato la risposta di Oleg come corretta, in quanto mette in evidenza un importante punto di utilizzo in merito a HttpClient pool di connessioni.

Per rispondere alla mia specifica domanda originale, tuttavia, che era "Dovrei provare a risolvere 0 socket inutilizzati o provare a massimizzare il pooling?"

Ora che la soluzione di raggruppamento è in posizione e funziona correttamente, il volume di applicazione è aumentato di circa il 150%. Attribuisco questo a non dover rinegoziare SSL e più handshake, riutilizzando invece le connessioni persistenti in accordo con HTTP 1.1.

Vale la pena lavorare per utilizzare il pool come previsto, piuttosto che tentare di hackerare con chiamate ThreadSafeClientConnManager.shutdown() dopo ogni richiesta eccetera.Se, d'altra parte, stavi chiamando host arbitrari e non riusciavi percorsi come me, potresti facilmente scoprire che è necessario fare questo tipo di hackery, dato che la JVM potrebbe sorprendervi con la lunga vita di prese designate CLOSE_WAIT se non stai raccogliendo i rifiuti molto spesso.

1

Ho avuto lo stesso problema e l'ho risolto utilizzando il suggerimento trovato qui: here. L'autore tocca alcune nozioni di base TCP:

Quando una connessione TCP sta per chiudere, la sua finalizzazione viene negoziata da entrambe le parti. Pensa a come rompe un contratto in modo civile. Entrambe le parti firmano il documento e va tutto bene. Nel geek talk, questo viene fatto tramite i messaggi FIN/ACK. Parte A invia un messaggio FIN per indicare che vuole chiudere il socket. La parte B invia un ACK dicendo che ha ricevuto il messaggio e sta considerando la richiesta. Party B quindi pulisce e invia una FIN alla parte A. La parte A risponde con l'ACK e tutti si allontanano.

Il problema si verifica in quando B non invia la FIN. A è piuttosto bloccato ad aspettarlo. Ha avviato la sua sequenza di finalizzazione ed è in attesa che l'altra parte faccia lo stesso.

Ha poi menzioni RFC 2616, 14.10 a suggerire la creazione di un header HTTP per risolvere questo problema:

postMethod.addHeader("Connection", "close"); 

Onestamente, non so davvero le implicazioni di impostare questa intestazione. Ma ha impedito a CLOSE_WAIT di succedere sui miei test unitari.

Problemi correlati