5

Sono un po 'confuso.Play Framework 2.X e chiamata al database di blocco

Dal documentation:

Giocare pool di thread di default - Questo è il pool di thread predefinito in cui viene eseguito tutto il codice dell'applicazione in Play Framework, ad esclusione del codice iteratees. È un dispatcher Akka e può essere configurato da configurando Akka, descritto di seguito. Per impostazione predefinita, ha un thread per processore .

vuol portare beneficio per avvolgere una chiamata al database di blocco in un Future, la chiamata al Future essere in sé avvolto da un async regolatore (riportandolo), al fine di lasciare che il pool di thread predefinita movimentazione altri utenti richieste?

Sposta semplicemente il codice di blocco all'interno di un altro thread (da un ExecutionContext dedicato), ma lascia lo Action sbloccato.

Mi sono imbattuto in this post ma non sono soddisfatto della risposta data.
Infatti, se lascio il blocco delle chiamate del database all'interno del pool di thread predefinito, non impedirebbe potenzialmente la gestione di richieste di altri utenti che non dipendono dal database nel frattempo?

Nota: il mio database (Neo4j) non ha un driver asincrono.

risposta

8

Ci sono alcuni modi per gestire le chiamate di blocco. Non posso dire quale sia il migliore, poiché dipenderebbe sicuramente da casi d'uso specifici e richiederà un sacco di benchmarking.

Per impostazione predefinita, Play gestisce le richieste utilizzando un pool di thread con un thread per core CPU. Quindi, se stai eseguendo la tua app Play su una CPU quad-core, ad esempio, sarà in grado di gestire solo 4 richieste simultanee se stanno utilizzando il blocco delle chiamate al database. Quindi sì, tutte le altre richieste in arrivo dovranno aspettare che uno dei thread sia stato liberato.

La soluzione più semplice è quello di aumentare il numero di thread Play utilizza per elaborare le richieste del pool di thread di default (in application.conf):

play { 
    akka { 
    akka.loggers = ["akka.event.slf4j.Slf4jLogger"] 
    loglevel = WARNING 
    actor { 
     default-dispatcher = { 
      fork-join-executor { 
      parallelism-min = 300 
      parallelism-max = 300 
      } 
     } 
    } 
    } 
} 

L'opzione successiva è quella che si parla nel vostro Question- - scaricando le chiamate al database di blocco su un altro ExecutionContext. È possibile configurare un pool di thread separato all'interno application.conf in questo modo:

database-io { 
    fork-join-executor { 
     parallelism-factor = 10.0 
    } 
} 

Questo creerà un 10 thread per core cpu in piscina chiamato database-io, e si può accedere all'interno Giocare in questo modo:

val dbExecutor: ExecutionContext = Akka.system.dispatchers.lookup("database-io") 

val something = Future(someBlockingCallToDb())(dbExecutor) 

Ciò consentirà al pool di thread predefinito di gestire più richieste mentre è in attesa del completamento di Future. Una terza opzione sarebbe quella di utilizzare un Actor per gestire le chiamate al database, ma è più complicato e va oltre lo scopo di questa domanda.

La linea di fondo è, si, utilizzare un pool di thread più grande o un diverso ExecutionContext per le chiamate di blocco, come si mai desidera bloccare nel pool di thread di default se si può farne a meno.

Questo è tutto indicato nello Play Documentation for Thread Pools. (ultima versione)

+0

Bella risposta :) In effetti, anche gli attori dedicati dietro a un router potevano fare il trucco. Ma non capisco perché l'altro post evochi: "Non hai nulla su cui non bloccare, quindi non c'è alcun motivo per rendere il controller asincrono". Se utilizzo la "soluzione ExecutionContext", devo avere un controller asincrono, siete d'accordo? – Mik378

+0

'Action's sono asincroni di default, comunque. 'Action.async' è solo una comodità per trattare con' Future's. C'erano troppi aspetti negativi nella dichiarazione citata per essere veramente chiari. Rimanerei con il 'ExecutionContext' separato. –

+0

Sì, lo so. La riproduzione è asincrona al 100%. 'Async' è solo un modo per gestire il futuro più facilmente. Ma se usassi il classico 'Action', dovrei fare la chiamata al database con un futuro, quindi' Await.result' (per ottenere il risultato), quindi specificare il risultato (come 'Ok (myResult)'), poiché 'Action' non restituisce un' Future [Result] 'ma semplicemente usa un futuro internamente. E 'Await' farebbe un blocco. – Mik378

Problemi correlati