Ho uno stream di input e voglio fare 2 richieste di rete per HTTPS
prima di passare il risultato ad un'altra parte del programma. Il throughput tipico è 50 al secondo.Come faccio a fare un gran numero di richieste HTTPS simultanee in Clojure (/ Java)
for each input:
HTTP request A
HTTP request B
pass event on with (A.body and B.body)
Sto usando il cliente http-kit
, che è asincrono per impostazione predefinita. Restituisce una promessa e può anche richiedere una richiamata. Http-kit utilizza Java NIO (vedi here e here)
La velocità delle richieste in arrivo, combinata con il tempo necessario per effettuare una richiesta, è sufficientemente elevata da dover essere eseguita in modo asincrono.
Ho provato 3 approcci:
- Quando un evento entra, metterlo su un canale. Un numero di routine
go
che escono dal canale. Ogni richiesta di esecuzione che "blocca" il goblock da parte dideref
promesse da richieste HTTP. Questo non funziona perché non penso che la promessa funzioni bene con i thread. - Quando arriva un evento, avvia immediatamente un
future
, che "blocca" sulle promesse asincrone. Ciò si traduce in molto alto utilizzo della CPU. In più, la fame di risorse di rete in qualche modo. - Quando arriva un evento, attivare immediatamente la richiesta
http-kit
per la richiesta A, passando una richiamata che effettua la richiesta B, passando una richiamata che passa l'evento. Ciò ha causato un errore di memoria insufficiente dopo alcune ore.
Questi funzionano e gestiscono la capacità per un po '. Alla fine tutti si schiantano. L'incidente più recente, dopo circa 12 ore:
Mar 10, 2016 2:05:59 AM com.mchange.v2.async.ThreadPoolAsynchronousRunner$DeadlockDetector run
WARNING: com[email protected]1bc8a7f5 -- APPARENT DEADLOCK!!! Creating emergency threads for unassigned pending
tasks!
Mar 10, 2016 3:38:38 AM com.mchange.v2.async.ThreadPoolAsynchronousRunner$DeadlockDetector run
WARNING: com[email protected]1bc8a7f5 -- APPARENT DEADLOCK!!! Complete Status:
Managed Threads: 3
Active Threads: 1
Active Tasks:
com[email protected]65d8b232 (com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread-#0)
Pending Tasks:
[email protected]0d
Pool thread stack traces:
Thread[com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread-#0,5,main]
com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:560)
Thread[com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread-#1,5,main]
java.lang.Object.wait(Native Method)
com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:534)
Thread[com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread-#2,5,main]
java.lang.Object.wait(Native Method)
com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:534)
Thu Mar 10 04:38:34 UTC 2016 [client-loop] ERROR - select exception, should not happen
java.lang.OutOfMemoryError: Java heap space
at java.io.ByteArrayOutputStream.<init>(ByteArrayOutputStream.java:77)
at sun.security.ssl.OutputRecord.<init>(OutputRecord.java:76)
at sun.security.ssl.EngineOutputRecord.<init>(EngineOutputRecord.java:65)
at sun.security.ssl.HandshakeOutStream.<init>(HandshakeOutStream.java:63)
at sun.security.ssl.Handshaker.activate(Handshaker.java:514)
at sun.security.ssl.SSLEngineImpl.kickstartHandshake(SSLEngineImpl.java:717)
at sun.security.ssl.SSLEngineImpl.beginHandshake(SSLEngineImpl.java:743)
at org.httpkit.client.HttpClient.finishConnect(HttpClient.java:310)
at org.httpkit.client.HttpClient.run(HttpClient.java:375)
at java.lang.Thread.run(Thread.java:745)
Mar 10, 2016 4:56:34 AM baleen.events invoke
SEVERE: Thread error: Java heap space
java.lang.OutOfMemoryError: Java heap space
Mar 10, 2016 5:00:43 AM baleen.events invoke
SEVERE: Thread error: Java heap space
java.lang.OutOfMemoryError: Java heap space
Mar 10, 2016 4:58:25 AM baleen.events invoke
SEVERE: Thread error: Java heap space
java.lang.OutOfMemoryError: Java heap space
Non so quale sia la causa del fallimento. Potrebbe darsi che ci siano troppe chiusure a cui si è aggrappata, o una graduale perdita di risorse, o la fame di thread.
Domande
Does rendendo 50 richieste HTTP al secondo, ognuno dei quali potrebbe prendere 200ms, il che significa che ci potrebbero essere 100 richieste in volo in un dato momento, suona come un onere eccessivo?
Come faccio a fare questo in modo che gestisca il throughput ed è robusto?
EDIT
YourKit profiler mi dice che ho circa 2 GB di char[]
s tramite org.httpkit.client.Handler
s tramite java.util.concurrent.FutureTask
s che suggerisce che i riferimenti a vecchi gestori (cioè richieste) vengono mantenuti in qualche modo. L'intera ragione per cercare di usare le callback era evitare (anche se in qualche modo potrebbero essere bloccati nelle chiusure)
OutOfMemoryError indica che c'è un problema nella memorizzazione della memoria ... ma non possiamo fare a meno di vedere il codice o scrivere una soluzione completa da zero. Cercherei di aggrapparmi alla testa di una sequenza infinita, o non pulire risorse come le connessioni. –
Mi chiedevo se potesse essere conservato un buffer, ma per quanto posso dire la garbage collection dovrebbe gestire la liberazione di memoria/buffer esterni che, ad es. NIO aveva assegnato. Quello che succede a valle è praticamente solo un inserimento nel database e un inserimento su un canale. – Joe
Ho pensato di postare il codice ma è abbastanza complicato e ci vorrebbe circa un giorno per sapere se avevo replicato il problema in una versione semplificata. – Joe