2011-01-20 10 views
34

Il NIO Java non bloccante è ancora più lento del thread asincrono della connessione thread standard per connessione?thread Java per modello di connessione e NIO

Inoltre, se si dovessero utilizzare thread per connessione, si dovrebbero semplicemente creare nuovi thread o si utilizzerà un pool di thread molto grande?

Sto scrivendo un server MMORPG in Java che dovrebbe essere in grado di scalare facilmente 10000 client con un hardware abbastanza potente, anche se la quantità massima di client è 24000 (che credo sia impossibile raggiungere per il thread per modello di connessione perché di un limite di thread di 15000 in Java). Da un articolo di tre anni, ho sentito che il blocco dell'IO con un thread per modello di connessione era ancora il 25% più veloce di NIO (ovvero, questo documento http://www.mailinator.com/tymaPaulMultithreaded.pdf), ma lo stesso può ancora essere raggiunto in questo giorno? Java è cambiato molto da allora, e ho sentito che i risultati erano discutibili quando si confrontano scenari di vita reale perché la VM utilizzata non era Sun Java. Inoltre, poiché si tratta di un server MMORPG con molti utenti simultanei che interagiscono tra loro, l'utilizzo delle procedure di sincronizzazione e sicurezza dei thread diminuirà le prestazioni fino al punto in cui un selettore NIO a thread singolo che serve 10000 client sarà più veloce? (tutto il lavoro non necessario deve essere elaborato sul thread con il selettore, può essere elaborato su thread di lavoro come MINA/Netty funziona).

Grazie!

+7

I thread 10k non sono una vittoria per qualsiasi server (* commodity *) :-) Inoltre, i client attivi 10k su una singola casella sono molto ... improbabili. –

+1

@ pst: se per merce intendi; Non quantico, ancora da scoprire tipo di tecnologia, sono totalmente d'accordo. Penso che il minimo dei problemi di Kevin sia il numero di thread. Mi scuso per non avere alcun input utile in merito. Ricorda anche il QOTD: Test. –

+0

@pst Oh dolce JRE è elastico! Hai appena reso la mia giornata utile. –

risposta

19

I vantaggi di NIO devono essere assunti con una granella di sale.

In un server HTTP, la maggior parte delle connessioni sono connessioni keep-alive, sono inattive il più delle volte. Sarebbe uno spreco di risorse pre-allocare un thread per ciascuno.

Per MMORPG le cose sono molto diverse. Immagino che le connessioni siano costantemente occupate a ricevere istruzioni dagli utenti e ad inviare l'ultimo stato del sistema agli utenti. Una discussione è necessaria la maggior parte del tempo per una connessione.

Se si utilizza NIO, è necessario ridistribuire costantemente un thread per una connessione. Potrebbe essere una soluzione inferiore, alla semplice soluzione a thread fisso per connessione.

La dimensione predefinita dello stack di thread è piuttosto grande, (1/4 MB?) È il motivo principale per cui possono esistere solo thread limitati. Prova a ridurlo e vedi se il tuo sistema può supportare di più.

Tuttavia, se il tuo gioco è davvero molto "occupato", è la tua CPU che devi preoccuparti di più. NIO o no, è davvero difficile gestire migliaia di giocatori iperattivi su una macchina.

+1

'Per MMORPG le cose sono molto diverse. Immagino che le connessioni siano costantemente occupate a ricevere istruzioni dagli utenti e ad inviare l'ultimo stato del sistema agli utenti. Suppongo che gli utenti vadano a letto, a volte, almeno la maggior parte di essi. Quindi mi aspetterei migliaia di connessioni impegnate e anche altre inattive. Mi chiedo come separarli, magari usando i thread e lasciarli morire sul timeout di Socket? – maaartinus

+0

Ho fatto un client a thread singolo, server a thread singolo nel vecchio io e uno in nio qui (il nio ha battuto il vecchio io o l'ho legato ... difficile da dire ... collo e collo.) Https://github.com/deanhiller/webpeeces/tree/master/core/core-asyncserver/src/test/java/org/webpieces/nio/api/throughput Nio sembra un'opzione migliore per poi avere un threadpool di dimensioni N per il rapporto di 10k. –

9

Se si vuole spendere qualsiasi somma di denaro su hardware abbastanza potente perché limitarsi a un server. google non usa un server, non usano nemmeno un datacenter di server.

Un malinteso comune è che NIO consente l'IO non bloccante per il suo unico modello che valga il benchmarking. Se si esegue il benchmark del blocco NIO, è possibile ottenerlo il 30% più velocemente rispetto al vecchio IO. Ad esempio, se si utilizza lo stesso modello di threading e si confrontano solo i modelli IO.

Per un gioco sofisticato, è molto più probabile che si esaurisca la CPU prima di raggiungere le connessioni 10K. Ancora una volta è più semplice avere una soluzione che si riduca orizzontalmente. Quindi non devi preoccuparti di quante connessioni puoi ottenere.

Quanti utenti possono interagire ragionevolmente? 24? nel qual caso hai 1000 gruppi indipendenti che interagiscono. Non avrai questo numero di core in un server.

Quanti soldi hai intenzione di spendere per gli utenti sui server? È possibile acquistare un server di 12 core con 64 GB di memoria per meno di £ 5000. Se metti 2500 utenti su questo server hai speso £ 2 per utente.

MODIFICA: Ho un riferimento http://vanillajava.blogspot.com/2010/07/java-nio-is-faster-than-java-io-for.html che è mio. ;) Ho avuto questo recensito da qualcuno che è un GURU di Java Networking ed è ampiamente d'accordo con ciò che ha trovato.

+0

Wow, non sapevo che NIO supporti anche il blocco dell'IO! Sembra come se non prestasse molta attenzione a se stesso e, di conseguenza, è difficile trovare benchmark che confrontino il blocco dei vecchi I/O e il blocco di NIO, il che è un peccato. Lo prenderò in considerazione però. –

+1

Dubito del tuo numero di prestazione, hai un riferimento? È stato segnalato che il NIO non bloccante è più lento del 30% rispetto all'IO tradizionale. Tuttavia il test non è stato realistico perché non fa nulla con i dati. Non appena ogni byte nel flusso viene letto almeno una volta, il sovraccarico di NIO/IO diventa insignificante. – irreputable

+0

Quando si fa qualcosa di realistico con i dati, la rete e l'elaborazione diventano più importanti e il vantaggio di NIO o IO è in gran parte perso. Io uso NIO come sembra a me stesso circa 6 ci di latenza per le letture. Questo non è particolarmente importante per la maggior parte degli sviluppatori. –

12

Ci sono in realtà 3 soluzioni:

  1. Più thread
  2. Un thread e NIO
  3. Entrambe le soluzioni 1 e 2 allo stesso tempo

La cosa migliore da fare per le prestazioni devono avere un numero limitato e limitato di thread e eventi di rete multiplex su questi thread con NIO quando i nuovi messaggi arrivano sulla rete.


Utilizzando NIO con un filo è una cattiva idea per un paio di motivi:

  • Se si dispone di più CPU o per anime, vi sarà al minimo le risorse dal momento che è possibile utilizzare un solo core alla volta se hai solo un thread.
  • Se si deve bloccare per qualche motivo (forse per fare un accesso al disco), la CPU è inattiva quando si potrebbe gestire un'altra connessione mentre si è in attesa del disco.

Un filo per la connessione è una cattiva idea perché non scala. Diciamo che sono:

  • 10 000 connessioni
  • 2 CPU con 2 core ciascuno
  • soli 100 discussioni saranno blocchi in un dato momento

Poi si può capire che è necessario solo 104 discussioni. Più e stai sprecando risorse per la gestione di thread aggiuntivi che non ti servono. C'è un sacco di contabilità sotto il cofano necessario per gestire 10.000 thread. Questo ti rallenterà.


Ecco perché si combinano le due soluzioni. Inoltre, assicurati che la tua macchina virtuale utilizzi le chiamate di sistema più veloci. Ogni SO ha le sue uniche chiamate di sistema per l'IO di rete ad alte prestazioni. Assicurati che la tua macchina virtuale utilizzi le ultime e le più grandi. Credo che questo sia epoll() in Linux.

Inoltre, se si sceglie di usare discussioni per connessione, vorresti solo creare nuove discussioni o useresti una piscina molto grande filo?

Dipende da quanto tempo si desidera ottimizzare l'ottimizzazione. La soluzione più rapida è creare risorse come thread e stringhe quando necessario. Quindi lascia che la raccolta dei rifiuti li richieda quando hai finito con loro. È possibile ottenere un aumento delle prestazioni avendo un pool di risorse.Invece di creare un nuovo oggetto, chiedi al pool uno e lo restituisci alla piscina quando hai finito. Ciò aggiunge la complessità del controllo della concorrenza. Questo può essere ulteriormente ottimizzato con algoritmi di concorrenza avanzati come non-blocking algorithms. Le nuove versioni dell'API Java ne offrono alcune per te. Puoi passare il resto della tua vita a fare queste ottimizzazioni su un solo programma. Qual è la soluzione migliore per la tua specifica applicazione è probabilmente una domanda che merita il proprio post.

+0

Come hai deciso di 104 thread? Vedo 10000/100 + 4. Ma perché non ci sono solo 100 thread, a cosa servono gli altri 4? 4 thread di lettura e 100 lavoratori? Ci sarebbero più thread che chiamano selector.select()? Attualmente sto esplorando l'idea di avere più thread con selettori individuali. L'ho fatto in C, ma la sintassi NIO è leggermente diversa da me. registrare le chiavi per i selettori in modo round robin è quello che sto pensando. Ogni thread chiama select e quindi genera un thread per fare il lavoro, o usando alcuni thread pooling black box magic. – JustinDanielson

+0

@JustinDanielson, Se ci sono 100 thread bloccati, è possibile avere 4 thread aggiuntivi sulla CPU poiché ci sono 4 core. Qualche cosa di meno e ci saranno dei nuclei liberi quando c'è del lavoro da fare. – Jay

1

Come molti di voi ragazzi stanno dicendo che il server è destinato ad essere rinchiuso in utilizzo della CPU prima di 10k utenti simultanei sono raggiunti, suppongo che sia meglio per me di utilizzare un blocco filettato (N) approccio IO considerando il fatto che per questo particolare MMORPG, ottenere diversi pacchetti al secondo per ogni giocatore non è raro e potrebbe impantanare un selettore se uno dovesse essere utilizzato.

Peter sollevato un punto interessante che il blocco NIO è più veloce rispetto le vecchie librerie, mentre irreputable detto che per un server MMORPG occupato, sarebbe meglio usare le discussioni a causa del numero di istruzioni vengono ricevuti per giocatore. Non conterei su troppi giocatori inattivi in ​​questo gioco, quindi non dovrebbe essere un problema per me avere un sacco di thread non funzionanti. Mi sono reso conto che la sincronizzazione è ancora necessaria anche quando si utilizza un framework basato su NIO perché utilizzano più thread di lavoro in esecuzione contemporaneamente per elaborare i pacchetti ricevuti dai client. Il cambio di contesto potrebbe rivelarsi costoso, ma darò una soluzione a questa soluzione. È relativamente facile refactoring il mio codice in modo che potrei utilizzare un framework NIO se trovo che c'è un collo di bottiglia.

Credo che la mia domanda abbia avuto risposta. Aspetterò un po 'di più per ricevere ancora più informazioni da più persone. Grazie per tutte le tue risposte!

EDIT: Ho finalmente scelto la mia linea d'azione. Io in realtà ero indeciso e deciso di usare JBoss Netty e consentire all'utente di passare da uno oio o nio utilizzando le classi

org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; 
org.jboss.netty.channel.socket.oio.OioServerSocketChannelFactory; 

Molto bello che Netty supporta sia!

3

Se si dispone di connessioni occupate, il che significa che inviano costantemente dati e li si invia indietro, è possibile utilizzare non-Blocking IO in combinazione con Akka.

Akka è un toolkit e runtime open-source che semplifica la costruzione di applicazioni concorrenti e distribuite su JVM. Akka supporta diversi modelli di programmazione per la concorrenza, ma enfatizza la concorrenza basata sugli attori, con l'ispirazione tratta da Erlang. Esistono binding linguistici sia per Java che per Scala.

La logica di Akka è non bloccante, quindi è perfetta per la programmazione asincrona. Utilizzando Akka Actors è possibile rimuovere Thread overhead.

Ma se i vostri flussi di socket bloccano più spesso, io suggerisco di usare Blocking IO in combinazione con Quasar

Quasar è una libreria open-source per la semplice, leggero concorrenza JVM, che implementa i veri fili leggeri (fibre AKA) su la JVM.Le fibre Quasar si comportano esattamente come i normali thread Java, eccetto che non hanno praticamente nessun sovraccarico di memoria e di task-switching, in modo da poter generare facilmente centinaia di migliaia di fibre - o anche milioni - in una singola JVM. Quasar fornisce anche canali per comunicazioni inter-fibra modellate su quelle offerte dalla lingua Go, complete di selettori di canale. Contiene anche un'implementazione completa del modello dell'attore, strettamente modellato su Erlang.

La logica del quasar è bloccante, quindi è possibile generare, diciamo 24000 fibre in attesa su connessioni diverse. Uno dei punti positivi di Quasar è che le fibre possono interagire facilmente con le discussioni semplici. Anche Quasar ha integrazioni con librerie popolari, come Apache HTTP client o JDBC o Jersey e così via, quindi puoi usare i benefici dell'utilizzo di Fibre in molti aspetti del tuo progetto.
Si può vedere un buon confronto tra questi due quadri here.