2011-11-30 10 views
5

Sto provando a creare un sistema distribuito ad alte prestazioni con Akka e Scala.Supporto framework Akka per la ricerca di messaggi duplicati

Se un messaggio che richiede un calcolo costoso (e senza effetti collaterali) arriva e lo stesso calcolo è già stato richiesto in precedenza, voglio evitare di calcolare nuovamente il risultato. Se il calcolo richiesto in precedenza è già stato completato e il risultato è disponibile, posso memorizzarlo nella cache e riutilizzarlo.

Tuttavia, la finestra temporale in cui può essere richiesto il calcolo duplicato può essere arbitrariamente piccola. per esempio. Potrei ottenere mille o un milione di messaggi che richiedono lo stesso costoso calcolo nello stesso istante per tutti gli scopi pratici.

C'è un prodotto commerciale chiamato Gigaspaces che presumibilmente gestisce questa situazione.

Tuttavia, al momento non sembra esserci alcun supporto di framework per gestire richieste di lavoro duplicate in Akka. Dato che il framework Akka ha già accesso a tutti i messaggi che vengono instradati attraverso il framework, sembra che una soluzione framework possa avere molto senso qui.

Ecco cosa sto proponendo per il framework Akka: 1. Creare un tratto per indicare un tipo di messaggi (ad esempio "ExpensiveComputation" o qualcosa di simile) che devono essere soggetti al seguente approccio di memorizzazione nella cache. 2. Smartly (hashing, ecc.) Identifica i messaggi identici ricevuti dagli attori (uguali o diversi) all'interno di una finestra temporale configurabile dall'utente. Altre opzioni: selezionare una dimensione massima del buffer di memoria da utilizzare per questo scopo, soggetto a sostituzione (ad esempio LRU) ecc. Akka può anche scegliere di memorizzare solo i risultati dei messaggi che erano costosi da elaborare; i messaggi che impiegano pochissimo tempo per essere processati possono essere nuovamente rielaborati se necessario; non c'è bisogno di sprecare spazio prezioso nel buffer memorizzandone i risultati. 3. Quando vengono identificati i messaggi identici (ricevuti all'interno di quella finestra temporale, possibilmente "allo stesso istante"), evitare inutili calcoli duplicati. Il framework lo farebbe automaticamente ed essenzialmente i messaggi duplicati non verrebbero mai ricevuti da un nuovo attore per l'elaborazione; scomparirebbero in silenzio e il risultato della sua elaborazione una volta (indipendentemente dal fatto che il calcolo fosse già stato fatto in passato o in corso in quel momento) sarebbe stato inviato a tutti i destinatari appropriati (immediatamente se già disponibili e al completamento del calcolo se non). Si noti che i messaggi dovrebbero essere considerati identici anche se i campi di "risposta" sono diversi, a condizione che i semantica/calcoli che rappresentano siano identici in ogni altro aspetto. Si noti inoltre che il calcolo dovrebbe essere puramente funzionale, cioè privo di effetti collaterali, per l'ottimizzazione della memorizzazione nella cache suggerita per funzionare e non modificare affatto la semantica del programma.

Se ciò che sto suggerendo non è compatibile con il modo di fare Akka e/o se si vedono alcune ragioni forti per cui questa è una pessima idea, fatemelo sapere.

Grazie, è impressionante, Scala

risposta

10

quello che chiedete è non dipende dal quadro Akka ma piuttosto E 'come architetto tuoi attori e messaggi. Innanzitutto assicurati che i tuoi messaggi siano immutabili e abbiano identità adeguatamente definite tramite i metodi equals/hashCode. Le classi dei casi ti danno entrambi gratuitamente, tuttavia se hai i reutori di actor incorporati nel messaggio a fini di risposta, dovrai sovrascrivere i metodi di identificazione. I parametri della classe case dovrebbero avere anche le stesse proprietà in modo ricorsivo (identità immutabile e corretta).

In secondo luogo è necessario capire come gli attori gestiranno la memorizzazione e l'identificazione dei calcoli correnti/passati.Il più semplice è quello di mappare in modo univoco le richieste agli attori. In questo modo quell'attore e solo quell'attore elaboreranno mai quella richiesta specifica. Questo può essere fatto facilmente dato un set fisso di attori e l'hashCode della richiesta. Punti bonus se l'attore è supervisionato dove il supervisore sta gestendo il bilanciamento del carico/mappatura e sostituzione degli attori falliti (Akka rende questa parte facile).

Infine l'attore stesso può mantenere un comportamento di cache di risposta in base ai criteri che hai descritto. Tutto è thread-safe nel contesto dell'attore, quindi una cache LRU imposta dalla richiesta stessa (ricorda bene le proprietà dell'identità) è facile con qualsiasi tipo di comportamento desideri.

+0

c'è una variazione di ciò che dipende dall'assicurarsi che l'ultimo messaggio sia quello elaborato non i precedenti nella coda. cioè non voglio iniziare il costoso calcolo fino a quando non mi assicuro che tutti i messaggi siano stati ricevuti. Penso che l'approccio sopra potrebbe essere modificato con un FSM per ottenere lo stesso risultato. – dres

5

Come dice Neil, questa non è realmente una funzionalità di framework, è piuttosto banale implementarla e persino astrarla nel proprio tratto.

trait CachingExpensiveThings { self: Actor => 
    val cache = ... 
    def receive: Actor.Receive = { 
    case s: ExpensiveThing => cachedOrCache(s) 
    } 

    def cacheOrCached(s: ExpensiveThing) = cache.get(s) match { 
    case null => val result = compute(s) 
       cache.put(result) 
       self.reply_?)(result) 
    case cached => self.reply_?)(cached) 
    } 
    def compute(s: ExpensiveThing): Any 
} 


class MyExpensiveThingCalculator extends Actor with CachingExpensiveThings { 
    def compute(s: ExpensiveThing) = { 
    case l: LastDigitOfPi => ... 
    case ts: TravellingSalesman => ... 
    } 
} 
+0

Ho anche calcolato l'ultima cifra di Pi, come hai fatto a essere? ; p –

+1

L'ultimo è π –

0

Non so se tutte queste responsabilità debbano essere gestite solo dall'Akka. Come al solito, tutto dipende dalla scala e, in particolare, dal numero di attributi che definisce l'unicità del messaggio.

Nel caso di meccanismo di cache, già citato approccio con richieste di mapping univoco agli attori è modo per andare soprattutto che potrebbe essere supportata dalla persistenza.

In caso di identità , invece di controllare semplice uguaglianza (che può essere strozzatura) Io piuttosto usare algoritmo basato grafico comesignal-collect.

Problemi correlati