2011-09-29 3 views
8

Ho dichiarato un bean Spring, che esegue il polling del mio server di posta ogni tanto e così secondi. Se c'è la posta, la recupera e cerca di estrarre tutti i file allegati. Questi file vengono quindi inviati a un Uploader che li salva in modo sicuro. Anche l'uploader viene dichiarato come bean Spring. Un terzo bean associa il mittente dell'email al nome file del file e lo memorizza in un DB.Con l'ambito bean predefinito come singleton, non sarà pericoloso quando si verificano chiamate simultanee?

Si è scoperto che quando alcune persone hanno provato a inviare e-mail nello stesso momento, è accaduto un sacco di cose confuse. I record nel DB hanno nomi di file errati. Alcuni non hanno ricevuto alcun nome di file, ecc.

Ho attribuito il problema al fatto che i bean hanno l'ambito di singleton per impostazione predefinita. Ciò significa che un mucchio di thread probabilmente stanno facendo confusione con uno e la stessa istanza allo stesso tempo. La domanda è come risolverlo.

Se sincronizzo tutti i metodi sensibili, tutti i thread si accumuleranno e attenderanno l'un l'altro, il che è un po 'come l'idea del multithreading.

D'altra parte, scoping i fagioli alla "richiesta" sta per creare nuove istanze di ciascuno di essi, che non è veramente buono o, se si parla di consumo di memoria e programmazione dei thread

Sono confuso. Cosa dovrei fare?

+0

È necessario sincronizzare i metodi, ma ancora più importante è necessario assicurarsi che i metodi chiamati non stiano modificando i dati di istanza su cui si basano altri thread. Sto solo supponendo che tu non abbia fornito il tuo codice, ma probabilmente è molto più della semplice sincronizzazione dei metodi. Sembra che tu abbia un problema sistemico di sovrascrittura dei dati prima che tu abbia finito con quello. –

risposta

8

Sono d'accordo con entrambe le risposte @Bozho e @stivio.

Le opzioni preferite sono passare non memorizzare lo stato in un bean con ambito singleton e passare un oggetto di contesto ai metodi oppure utilizzare un prototipo/richiesta di scope con scope che viene creato per ogni ciclo di elaborazione. La sincronizzazione può essere in genere evitata, scegliendo uno di questi approcci e ottenendo prestazioni molto più elevate, evitando allo stesso tempo deadlock. Assicurati di non modificare nessuno stato condiviso, come membri statici.

Ci sono pro e contro per ogni approccio:

  1. fagioli Singlton sono agire come una classe di servizio simile, che alcuni potrebbero dire che non sono una buona progettazione orientata agli oggetti.
  2. Passare il contesto ai metodi in una lunga catena di metodi può rendere il codice disordinato, se non si presta attenzione.
  3. I bean di prototipo possono conservare molta memoria più a lungo di quanto previsto e possono causare esaurimento della memoria. Devi stare attento con il ciclo di vita di questi fagioli.
  4. I prototipi di fagioli possono rendere più ordinato il tuo design. Assicurati però di non riutilizzare i bean con più thread.

Tendo ad andare con l'approccio al servizio nei casi più semplici. Puoi anche permettere a questi bean singleton di creare un oggetto di elaborazione che possa mantenere il suo stato per il calcolo. Questa è una soluzione che può essere utile per gli scenari più complessi.

Edit: Ci sono casi in cui si dispone di un fagiolo Singleton seconda prototipo ambito di fagioli, e si desidera una nuova istanza del bean prototipo per ogni chiamata di metodo. Spring offre diverse soluzioni:

Il primo utilizza Method Injection, come descritto nella documentazione di riferimento Spring. Non mi piace molto questo approccio, perché costringe la tua classe ad essere astratta.

Il secondo è quello di utilizzare un ServiceLocatorFactoryBean o la propria classe factory (che deve essere iniettata con le dipendenze e richiamare un costruttore). Questo approccio funziona molto bene nella maggior parte dei casi e non ti abbina a Spring.

Esistono casi in cui si desidera che i bean prototipo dispongano di dipendenze di runtime. Un mio buon amico ha scritto un buon post su questo qui: http://techo-ecco.com/blog/spring-prototype-scoped-beans-and-dependency-injection/.

+0

Sono d'accordo con te e upvoted, eccetto per il punto 1. I bean singleton non sono raccomandati perché rendono più difficili i test unitari e perché introducono dipendenze magiche, ma stiamo parlando di Java Singleton, quelli con costruttore privato e un metodo statico per restituire un'istanza. I singleton di primavera non hanno questi problemi. – stivlo

+0

@stivio quando ho scritto i bean singleton intendevo i bean spring in scope singleton, e non le classi java che implementavano il pattern di progettazione singleton. Sono d'accordo sul fatto che il più tardo è probabilmente il modello più problematico nel libro GoF, ma i bean singleton non sono difficili da testare affatto - puoi istanziarli, introdurre interfacce e prendere in giro le dipendenze. e testarli come qualsiasi altra classe. –

+1

Esattamente, questo è quello che ho detto: "I singleton primaverili non hanno questi problemi". - Questo è il motivo per cui non sono d'accordo con il punto 1, sarei d'accordo se stavi parlando dei singleton di GoF. – stivlo

12

I fagioli con ambito Singleton non devono contenere alcun stato, che risolve il problema in genere. Se passi solo i dati come parametri del metodo e non li assegni ai campi, sarai al sicuro.

2

Altrimenti basta dichiarare i bean come richiesta, non preoccuparti del consumo di memoria, la garbage collection lo cancellerà, a patto che ci sia abbastanza memoria non sarà un problema di prestazioni.

+0

Il mio unico problema è che non riesco a rendere "richiesta" l'ambito di MailManager, perché sto eseguendo il polling per la posta utilizzando l'attivatore di servizi di Spring Integration, cioè non esiste una richiesta web. Posso ancora contrassegnare in qualche modo MailManager come "per-thread"? – user802232

+0

usa "prototipo". – stivlo

+0

È anche possibile utilizzare SimpleThreadScope di Spring core (http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/context/support/SimpleThreadScope.html) che lega un bean a un thread -local –

1

I single devono essere di stato e thread-safe.

Se un singleton è senza stato, è un caso degenerato di essere stateful e thread-safe è banalmente vero. Ma allora qual è il punto di essere single? Creane uno nuovo ogni volta che qualcuno lo richiede.

Se un'istanza è di stato e non thread-safe, quindi non deve essere singleton; ogni thread dovrebbe avere esclusivamente un'istanza diversa.

+1

Penso che i singletons di stato siano piuttosto pericolosi (sono effettivamente variabili globali). Perché credi che i singleton debbano essere stati dichiarati? – sleske

2

Parlare in modo astratto: se si utilizza Spring Integration, è necessario creare il codice in termini di messaggi stessi. Ad esempio, tutti gli stati importanti dovrebbero essere propagati con i messaggi. Ciò rende banale il ridimensionamento aggiungendo altre istanze di integrazione di primavera per gestire il carico. L'unico stato (davvero) in Spring Integration è per componenti come l'aggregatore, che attende e raccoglie messaggi con una correzione. In questo caso, è possibile delegare a un backing store come MongoDB per gestire l'archiviazione di questi messaggi e questo è ovviamente thread-safe.

Più in generale, questo è un esempio di architettura guidata da eventi a fasi: i componenti devono essere stateless (N (1) non importa quanti messaggi) gestiscono i messaggi e quindi inoltrarli su un canale per il consumo da un altro componente che non conosce sul componente precedente da cui proviene il messaggio.

Se si verificano problemi di thread-sicurezza utilizzando l'integrazione di Primavera, si potrebbe fare qualcosa di un po 'diverso rispetto al previsto e potrebbe valere la pena di rivisitare il tuo approccio ...

Problemi correlati