Come progettare servizi Web altamente scalabili in Java?


15

Sto creando alcuni servizi Web che avrebbero 2000 utenti simultanei. I servizi sono offerti gratuitamente e si prevede quindi di ottenere una vasta base di utenti. In futuro potrebbe essere necessario ridimensionare fino a 50.000 utenti.

Esistono già alcune altre domande che affrontano il problema come: /programming/2567254/building-highly-scalable-web-services

Tuttavia i miei requisiti differiscono dalla domanda sopra.

Ad esempio - La mia applicazione non ha un'interfaccia utente, quindi immagini, CSS, javascript non sono un problema. È in Java quindi suggerimenti come l'uso di HipHop per tradurre PHP in codice nativo sono inutili.

Quindi ho deciso di porre la mia domanda separatamente.

Questa è la mia configurazione del progetto -

  1. Servizi Web basati su riposo utilizzando Apache CXF
  2. Hibernate 3.0 (con ottimizzazioni pertinenti come caricamento lento e HQL personalizzato per la messa a punto)
  3. Tomcat 6.0
  4. MySql 5.5

Quali sono le migliori pratiche da rispettare per rendere scalabile un'applicazione basata su Java?


Se stai esponendo un servizio REST, usare un proxy inverso come Varnish sarebbe di grande aiuto. Quanto devono essere freschi i dati? Sei sicuro di aver bisogno di un database relazionale? Potresti partizionare i dati? Con lo stack tecnologico che stai descrivendo, mi concentrerei sull'assicurarmi che il minor numero possibile di richieste raggiunga effettivamente il tuo endpoint. Hai mai pensato di farlo in memoria con soluzioni come Hazel cast / Gigaspaces ecc?
ebaxt,

@ebaxt grazie per i tuoi suggerimenti. Gigaspaces sembra essere open source. Ma il cast di Hazel sembra interessante.
Kshitiz Sharma,

1
@ebaxt "Sei sicuro di aver bisogno di un database relazionale?" L'adozione di nosql comporterebbe drastici cambiamenti nell'architettura dell'applicazione. Stiamo cercando di mantenere la complessità al minimo. Il costo però non è un fattore per noi. Quindi resteremo fedeli all'approccio relazionale.
Kshitiz Sharma,

1
Puoi usare Postgres, MySQL o qualsiasi altra cosa. Qual è la tua infrastruttura? Puoi usare disk-array? I server sono ospitati nella stessa posizione? Puoi connettere il tuo cluster con il battito cardiaco ecc.? Puoi metterli nella stessa sottorete?
Edze,

1
Anch'io sono un programmatore. Ma se il tuo database relazionale è il collo di bottiglia, tenderai a finire con queste domande. Ci sono database sul mercato, alcuni funzionano meglio di altri in alcune situazioni. Ma stanno usando diversi livelli di isolamento delle transazioni predefiniti e concorrenza ottimistica contro concorrenza pessimistica ecc.
edze,

Risposte:


8

Ho affrontato il problema in passato, ma sento ancora che ho molto da imparare sul campo. Trovo che questo sia uno dei campi più interessanti che ci sono nello sviluppo del software al giorno d'oggi, ecco alcuni pensieri a riguardo:
MySQL è un database abbastanza equo a meno che tu non stia lavorando con una quantità enorme di dati, e in questo caso potresti considerare NoSQL database, ma dovresti esaminare attentamente quale sia il miglior database NoSQL per il tuo, esigenze. È necessario implementare la memorizzazione nella cache nel proprio sistema - provare a memorizzare nella cache il maggior numero possibile di dati di sola lettura o definire alcune strategie di memorizzazione nella cache - ad esempio, abbiamo avuto uno scenario in cui era valido per un utente vedere "vecchi dati" come purché il recente aggiornamento sia avvenuto nell'ultima ora. prenderei in considerazione JBoss Cache, o forse Infinispan (che è più simile a una struttura di dati distribuiti) o un altro framework di cache popolare per questo.



Inoltre, come hai detto Tomcat, presumo che tu lavori in un modulo di richiesta-risposta. Prova a prendere in considerazione l'utilizzo di una cache esistente nell'ambito di una determinata richiesta, questa può essere anche una semplice HashMap associata all'archiviazione locale dei thread .
La mia idea qui assomiglia abbastanza alla cache di primo livello di Hibernate . Inoltre, devi capire quali 2000 utenti simultanei - ciò significa che 2000 utenti accedono al tuo server contemporaneamente o usano il tuo sistema? Distinguere tra i casi in cui 2000 utenti tentano di aprire un socket sul server e un caso in cui solo 500 sono, e 1500 stanno attualmente esaminando i risultati, di riempire l'input sul lato client. Dovresti considerare l'utilizzo del clustering: dovrai affrontare problemi come

Dovresti ricordare che file, transazioni e altre risorse sono costosi in termini di tenerli aperti. Assicurati di chiudere i file e le transazioni il più presto possibile, altrimenti finirai con i bug che verranno riprodotti su configurazioni su larga scala



bilanciamento del carico , sessione appiccicosa (il che significa che il bilanciamento del carico reindirizzerà una richiesta allo stesso server per la stessa sessione) e altro ancora.

Se devi disporre di un codice di sincronizzazione, scegli attentamente la strategia di sincronizzazione. Ho visto alcuni sistemi in cui veniva utilizzato un semplice blocco, ma ReaderWriterLockavrebbe potuto migliorare le cose, dato che la maggior parte dell'accesso era di sola lettura.

Prendi in considerazione la memorizzazione nella cache lato client e la convalida, se possibile, prova a salvare le chiamate al server e a inviare solo differenze di dati, nel caso in cui la maggior parte della tua risposta per una richiesta con lo stesso parametro non cambi.
Ad esempio, al progetto open source oVirt chiediamo di ottenere statistiche su una determinata macchina virtuale. alcuni dei dati della VM cambiano raramente, quindi ne inviamo solo MD5, se i dati cambiano anche il valore MD5 viene modificato, eseguiamo una richiesta per ottenere i dati completi e non solo MD5.

Ho già parlato di ibernazione in precedenza - ti consiglierei di prendere attentamente in considerazione di usarlo - se devi eseguire molte scritture e meno letture, Hibernate potrebbe non essere l'ideale per te e dovresti considerare di lavorare con Spring-JDBC come wrapper JDBC.

Indicizza il tuo database con saggezza e usa uno schema db corretto. Prendi in considerazione l'utilizzo di uno strato di procedure memorizzate in quanto sono precompilate e ottimizzate.

Vorrei affermare che in passato, ho gestito un sistema (nodo singolo) su mysql (principalmente accesso in sola lettura) con jboss 4.2.1 e sono riuscito a raggiungere 2000 concorrenti utenti
(non accedendo subito in termini di apertura di 2000 socket contro il nostro server), ma usando / navigando nel nostro sistema, usando JBoss Cache e precaricando nella cache alcuni dei dati più accessibili, o dati che abbiamo realizzato saranno "caldi e popolari" ma la nostra soluzione era buona per la nostra architettura e i nostri flussi ,
quindi , come ho detto in questi casi,
ci sono più suggerimenti e trucchi, ma dipende davvero dalla tua architettura e dai flussi che devi avere nel tuo sistema. In bocca al lupo!


Sono d'accordo tranne che per i processi memorizzati, non utilizzare i processi memorizzati. E puoi usare una hashmap simultanea e valori atomici, per rendere thread
safe

3

Buona domanda. Probabilmente difficile dire quale sia l'approccio migliore, ma proverò dalla mia esperienza.

Il modo migliore per ridimensionare l'applicazione Web basata su Java è scriverla nel modo più stateless possibile (se possibile). Ciò consente di ridimensionare orizzontalmente l'applicazione, in cui è possibile aggiungere server Tomcat se vi sono più utenti simultanei.

Tuttavia, come hai notato, potrebbero esserci problemi con le connessioni al database. Ma la domanda che ho è: come stai ottenendo i dati? Viene generato dall'utente o ottieni dati da terze parti? Questo è molto importante perché, se stai fornendo un servizio al tuo utente con i dati aggregati dall'applicazione di terze parti (ad esempio FB, Twitter ecc.), Allora ciò che puoi seguire è scrivere nel database principale e replicare i dati nei database secondari che sono assegnati a ciascuna istanza di Tomcat. Quindi ogni server Tomcat può ottenere dal proprio database slave.

 Are there faster alternatives to Mysql?

Puoi scegliere il cluster MySQL che ha un archivio dati in memoria. Ma attenzione al fatto che l'applicazione potrebbe richiedere alcune modifiche. Non sql joinssono ben supportati nel cluster MySQL sebbene nell'ultima versione ci siano miglioramenti per lo stesso. Se il costo non è un fattore, puoi provare Oracle.

La soluzione di memorizzazione nella cache sicuramente migliorerà le prestazioni. Ma poi, tutto dipende dall'architettura dell'intera applicazione. È necessario essere consapevoli di quando inviare i dati nella cache, quando renderli sporchi (rimuovere dalla cache).

Per quanto riguarda la distribuzione del carico in ambiente multi server, ti suggerirei di utilizzare il bilanciamento del carico piuttosto che di utilizzare Apache per il bilanciamento del carico.


"Ti suggerirei di usare il bilanciamento del carico piuttosto che usare Apache per il bilanciamento del carico" Quale approccio / software suggeriresti se non Apache?
Kshitiz Sharma,

Praticamente raccomandavo l'hardware di bilanciamento del carico, che l'amministratore di rete dovrebbe essere in grado di configurare. Questo corso ha un costo aggiuntivo per il progetto. Questo bilanciamento del carico avrà un proprio IP (chiamato anche IP virtuale) e in pratica assegnerai questo IP al tuo dominio. Quando arriva la richiesta, questo verrà indirizzato a tutti i server connessi in modalità round robin (anche altri algoritmi disponibili). Puoi usare apache a questo scopo se l'hardware non è un'opzione, ma preferirei l'hardware in quanto non è necessario ottimizzare Apache solo per questo scopo.

Stiamo usando un server dedicato con httpd per fare la stessa cosa. L'hardware non è un problema.
Kshitiz Sharma,

Puoi usare httpd e mod_cluster, se ricordo bene. Considererei attentamente prima di passare alla soluzione "overkill" dell'hardware LB, prima di controllare httpd e mod_cluster

@zaske - Probabilmente hai ragione sul fatto che il bilanciamento del carico hardware potrebbe essere eccessivo. Ma nel caso in cui sia necessario ridimensionare, è facile farlo aggiungendo più server.

2

Attualmente sto installando un sistema simile (a livello professionale) e questo è il design che ho scelto:

  • Due bilanciatori del carico Nginx (entrambi attivi, entrambi di failover per l'altro, bilanciati con il round robin DNS)
  • Due database MySQL in modalità master master replica
  • Due istanze Tomcat come cluster Tomcat
  • Due istanze Memcached per la memorizzazione nella cache e la condivisione dello stato della sessione per il cluster Tomcat

Ciò consentirà una soluzione ridondante, ad alta disponibilità e scalabile.

I loadbalancer (su hardware decente) bilanciano facilmente una linea satura da 1 gbit ciascuno. Questo è anche un ottimo posto per l'offloading SSL.

È possibile salvare le informazioni sulla sessione in memcached. Nel caso in cui un'istanza di Tomcat fallisca, un'altra istanza di Tomcat può recuperare informazioni rilevanti sulla sessione e i client non noteranno nulla. Non dimenticare di combinare anche questo con sessioni appiccicose. (Per limitare il traffico di rete)

Il clustering Tomcat ha anche un'opzione per condividere le informazioni sulla sessione tra il cluster in tempo reale, senza usare memcached. Anche se penso che le prestazioni siano sagge, l'uso di Memcached sarà migliore.

Se hai bisogno di più potenza in una di queste applicazioni:

  • Nginx: aggiungi più loadbalancer, anche se non credo che questo sarà il collo di bottiglia molto presto.
  • Tomcat: è possibile aumentare facilmente le dimensioni del cluster Tomcat o aggiungere più cluster
  • Mysql: aggiungi alcuni slave di sola lettura o aumenta le dimensioni del cluster (a seconda della tua applicazione, ma poiché hai scritto un'applicazione basata su REST, questo non dovrebbe essere un problema)
  • Memcached: aggiungi più nodi, credo che le scale Memcached siano abbastanza buone.

Non so come sia la tua applicazione e quali siano i grandi maiali delle risorse, ma se vedi un carico elevato del database (durante i tuoi test di carico!), L'aggiunta di una cache tra l'applicazione e il database potrebbe sicuramente migliorare molto le prestazioni. Ma non dimenticare che non tutto è ricambiabile, se le tue domande sono sempre diverse, la memorizzazione nella cache non aiuterà (molto)

Il mio consiglio sarebbe di scaricare VMware Workbench (o software di virtualizzazione simile) e provare a creare una configurazione semplice. Nessun bilanciamento del carico o clustering, solo le basi e il lavoro da lì. Uno ad uno aggiungi altre funzionalità (bilanciamento, memorizzazione nella cache, clustering, ecc.) E assicurati di fare qualche ricerca su ogni argomento, così saprai che hai fatto la scelta giusta.

Se continui a eseguire gli stessi test delle prestazioni durante questo processo, puoi vedere da solo se l'uso di X è migliore dell'uso di Y nella configurazione o quale impatto avrà la cache, ecc.

Alla fine, una configurazione come questa dipende davvero dai requisiti della tua applicazione e dei suoi client, tutto può essere fatto in vari modi, ognuno con i suoi punti di forza e di debolezza.

Altre domande?

In bocca al lupo!

Wesley



Utilizzi un framework per il livello di memorizzazione nella cache o solo un mucchio di hash manuali su query SQL?
Djechlin,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.