Programmazione asincrona composta in Java senza troppo GC

3

Supponiamo che io stia scrivendo una libreria in Java per eseguire chiamate di rete. Voglio che sia efficiente per i casi come proxy inversi altamente caricati ecc (da 10 a 100 Krps che entrano ed escono, richieste client servite nello stesso DC in meno di 1 ms, elaborazione relativamente semplice dei risultati), inoltre, ogni richiesta in arrivo potrebbe iniziare diversi in uscita e combinare i risultati in modo semplice.

La libreria utilizza internamente socket client non bloccanti, ma voglio esporre qualche astrazione di livello superiore agli utenti di questa libreria. Ho iniziato con CompletableFuture , ma mi sembra che possa generare troppa spazzatura: ogni "composizione" ( thenCompose ecc.) Crea un nuovo oggetto; inoltre, incoraggia l'uso di lambda che sono anche oggetti separati (giusto?). Il futuro finale potrebbe consistere in diverse fasi (3-5 in realtà: raccoglie, tentativi ecc.), Quindi sembra un po 'troppi oggetti per richiesta.

Quali altre opzioni ho che fornirebbero almeno una certa "composibilità" (leggi: può essere usata dai client di libreria, e forse dalla libreria stessa) ma genererà meno spazzatura?

È possibile avere un ciclo di eventi "riutilizzabile" di sorta? Oppure il paradigma del flusso (reattivo) può aiutare? Si dicono cose positive sulle coroutine che sono equiparate alle macchine di stato per le lingue che non le supportano in modo nativo, ma ho difficoltà a immaginare come l'implementazione potrebbe anche sembrare per uno scenario come "chiama diversi servizi con riprova, una volta terminato alcuni / tutti, restituisce un risultato, fornisce un mezzo per i client della libreria che possono attingere a ciò o migliorarlo "... Potremmo avere un numero di processori collegati da code (che bloccano la coda nella forma più semplice), ma ho dei dubbi su l'efficienza di tale sistema: queste code non arrivano gratis, ci saranno elementi aggiunti, elementi tagliati, tutti questi migreranno attraverso l'intero "gasdotto" ... O forse Disruptor è la mia unica speranza? (Non che io capisca come applicarlo qui.)

Per questi approcci lo pseudocodice o uno schema che descrive come potrebbe funzionare o un collegamento a una spiegazione è molto apprezzato perché in qualche modo li conosco "su" ma non capisco come usarli o se funzioneranno nel mio Astuccio. O forse c'è un approccio ancora migliore che ho completamente perso? ..

Modifica: ho dimenticato di dire che in realtà sto estendendo una libreria di rete esistente per supportare l'utilizzo asincrono. La libreria riutilizza molto gli oggetti (buffer, socket, oggetti di risposta, ecc.), Quindi, in primo luogo, ho pensato che forse era davvero importante in questo contesto; in secondo luogo, mi piacerebbe perseguire gli stessi obiettivi di progettazione, se possibile.

Alcuni calcoli sul retro della busta che supportano questo desiderio: supponiamo che ci siano 4 futures per richiesta (quello originale, riprovare la logica, raccogliere diversi risultati logici e quindi logica client). Supponiamo che ci siano 50.000 richieste di server in arrivo al secondo e ciascuna si traduce in 2 richieste client. Quindi questo è già 4 * 2 * 50_000 * (object_size) byte al secondo supponendo che i callback vengano riutilizzati. Supponiamo che ogni oggetto abbia 20 byte (intestazione, almeno un listener, riferimento al risultato). Supponi che i callback vengano riutilizzati in modo tale che non vengano creati e raccolti. Questo è 8 Mb al secondo, ma facilmente di più. Quello è 1 GB di spazzatura per un paio di minuti. Forse non troppo, davvero, ma sarebbe comunque interessante capire se potesse essere migliorato.

    
posta starikoff 19.07.2017 - 00:35
fonte

1 risposta

4

I've started with CompletableFuture, but I'm feeling like it might generate too much garbage:

Esattamente da dove viene questo 'sentimento'? Hai provato questo?

each "composition" (thenCompose etc) creates a new object;

La creazione dell'oggetto è solo aggiunta di puntatori e talvolta di inizializzazione. La spesa di entrambi è strongmente dipendente dal design dell'oggetto. Questa è una grande ragione per cui i costruttori che fanno il vero lavoro sono disapprovati.

also, it encourages using lambdas which are also separate objects (right?).

Giusto, ma ancora non tutti gli oggetti sono creati uguali. Del resto, non tutte le JVM sono uguali. Potresti voler vedere le specifiche della lingua Java®, capitolo 15.27.4. Valutazione run-time di Lambda Expressions

In particolare questo piccolo nugget:

  • A new object need not be allocated on every evaluation.

Quindi alcuni di JVM potrebbero ottimizzare la tua pila di oggetti lambda ridondanti. Alcuni potrebbero non.

The final future might consist of several stages (3-5 really: gathers, retries etc), so that feels like a bit too many objects per request.

Davvero? Quindi, se facessi tutto con un solo oggetto da 8 gigabyte che costruiremo e distruggeremo per ogni richiesta, staresti bene con quello? Le risorse sono limitate ma il conteggio degli oggetti non è il modo migliore per gestirli.

Quello che sto cercando di mostrarti è che è davvero impossibile prevedere come funzionerà questo progetto fino a quando non lo testerai.

Ora certo, potresti andare per oggetti allocati staticamente al 100% e CONOSCERE che non si sta per saltare l'heap ma si tratta solo di scambiare un problema con un altro.

    
risposta data 19.07.2017 - 03:07
fonte

Leggi altre domande sui tag