2012-12-10 18 views
7

Desidero creare un servizio WCF che utilizza un bind MSMQ poiché ho un volume elevato di notifiche che il servizio deve elaborare. È importante che i client non siano trattenuti dal servizio e che le notifiche vengano elaborate nell'ordine in cui vengono generate, quindi l'implementazione della coda.Come imporre la sequenza delle code dei messaggi con più istanze del servizio WCF

Un'altra considerazione è la resilienza. So che è possibile cluster MSMQ per rendere la coda più robusta, ma voglio essere in grado di eseguire un'istanza del mio servizio su server diversi, quindi se un server si blocca le notifiche non si accumulano in coda ma un altro server continua a elaborare .

Ho sperimentato il collegamento MSMQ e ho scoperto che è possibile avere più istanze di un servizio in ascolto sulla stessa coda e lasciate a se stesse si finisce per eseguire una sorta di round-robin con il carico distribuito tra i servizi disponibili . Questo è grandioso, ma finisco per perdere la sequenza della coda, poiché istanze diverse impiegano una quantità diversa di tempo per elaborare la richiesta.

Ho usato una semplice app per console per sperimentare, che è il dump del codice epico di seguito. Quando è gestito ottengo un output simile a questo:

host1 open 
host2 open 
S1: 01 
S1: 03 
S1: 05 
S2: 02 
S1: 06 
S1: 08 
S1: 09 
S2: 04 
S1: 10 
host1 closed 
S2: 07 
host2 closed 

Quello che voglio che accada è:

host1 open 
host2 open 
S1: 01 
<pause while S2 completes> 
S2: 02 
S1: 03 
<pause while S2 completes> 
S2: 04 
S1: 05 
S1: 06 
etc. 

avrei pensato che, come S2 non ha completato, potrebbe ancora riuscire e restituire il messaggio stava elaborando in coda. Pertanto, S1 non dovrebbe essere in grado di estrarre un altro messaggio dalla coda. La mia coda è transazionale e ho provato a impostare TransactionScopeRequired = true sul servizio, ma senza successo.

È possibile? Sto andando nel modo sbagliato? Esiste un altro modo per creare un servizio di failover senza alcun tipo di meccanismo di sincronizzazione centrale?

class WcfMsmqProgram 
{ 
    private const string QueueName = "testq1"; 

    static void Main() 
    { 
     // Create a transactional queue 
     string qPath = ".\\private$\\" + QueueName; 
     if (!MessageQueue.Exists(qPath)) 
      MessageQueue.Create(qPath, true); 
     else 
      new MessageQueue(qPath).Purge(); 

     // S1 processes as fast as it can 
     IService s1 = new ServiceImpl("S1"); 
     // S2 is slow 
     IService s2 = new ServiceImpl("S2", 2000); 

     // MSMQ binding 
     NetMsmqBinding binding = new NetMsmqBinding(NetMsmqSecurityMode.None); 

     // Host S1 
     ServiceHost host1 = new ServiceHost(s1, new Uri("net.msmq://localhost/private")); 
     ConfigureService(host1, binding); 
     host1.Open(); 
     Console.WriteLine("host1 open"); 

     // Host S2 
     ServiceHost host2 = new ServiceHost(s2, new Uri("net.msmq://localhost/private")); 
     ConfigureService(host2, binding); 
     host2.Open(); 
     Console.WriteLine("host2 open"); 

     // Create a client 
     ChannelFactory<IService> factory = new ChannelFactory<IService>(binding, new EndpointAddress("net.msmq://localhost/private/" + QueueName)); 
     IService client = factory.CreateChannel(); 

     // Periodically call the service with a new number 
     int counter = 1; 
     using (Timer t = new Timer(o => client.EchoNumber(counter++), null, 0, 500)) 
     { 
      // Enter to stop 
      Console.ReadLine(); 
     } 

     host1.Close(); 
     Console.WriteLine("host1 closed"); 
     host2.Close(); 
     Console.WriteLine("host2 closed"); 

     // Wait for exit 
     Console.ReadLine(); 
    } 

    static void ConfigureService(ServiceHost host, NetMsmqBinding binding) 
    { 
     var endpoint = host.AddServiceEndpoint(typeof(IService), binding, QueueName); 
    } 

    [ServiceContract] 
    interface IService 
    { 
     [OperationContract(IsOneWay = true)] 
     void EchoNumber(int number); 
    } 

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] 
    class ServiceImpl : IService 
    { 
     public ServiceImpl(string name, int sleep = 0) 
     { 
      this.name = name; 
      this.sleep = sleep; 
     } 

     private string name; 
     private int sleep; 

     public void EchoNumber(int number) 
     { 
      Thread.Sleep(this.sleep); 
      Console.WriteLine("{0}: {1:00}", this.name, number); 
     } 
    } 
} 
+0

avrete difficoltà a fare questo con MSMQ di WCF vincolante (vedi http://stackoverflow.com/questions/729612/ordered-delivery-with-netmsmqbinding). Tuttavia, è possibile farlo utilizzando MSMQ transazionale e alcuni codici di riconciliazione (senza WCF). –

risposta

10

batwad,

si sta cercando di creare manualmente un servizio bus. Perché non provi ad usarne uno esistente?

NServiceBus, MassTransit, ServiceStack

Almeno 2 di quelli lavoro con MSMQ.

Inoltre, se è assolutamente necessario un ordine, potrebbe essere in realtà per un altro motivo: si desidera poter inviare un messaggio e non si desidera che i messaggi dipendenti vengano elaborati prima del primo messaggio. Stai cercando il modello Saga. NServiceBus e MassTransit entrambi vi permetterà di gestire facilmente Sagas, saranno entrambi permettono di attivare semplicemente il messaggio iniziale e quindi attivare i messaggi rimanenti in base alle condizioni. Ti permetterà di implementare il plumping della tua applicazione distribuita in un attimo.

È anche possibile scalare fino a migliaia di client, server di accodamento e processori di messaggi senza dover scrivere una singola riga di codice né avere alcun problema.

Abbiamo provato a implementare il nostro bus di servizio su msmq qui, abbiamo rinunciato perché un altro problema è continuato a salire. Siamo andati con NServiceBus ma anche MassTransit è un prodotto eccellente (è open source al 100%, NServiceBus no). ServiceStack è fantastico per creare API e utilizzare Code di messaggi: sono sicuro che potresti usarlo per creare servizi che fungono da front-end in coda in pochi minuti.

Oh, ho già detto che nel caso di NSB e MT richiedono solo meno di 10 righe di codice per implementare pienamente code, mittenti e gestori?

----- ----- AGGIUNTO

Udi Dahan (uno dei principali contributori di NServiceBus) parla di questo in: "In-Order Messaging a Myth" by Udi Dahan "Message Ordering: Is it Cost Effective?" with Udi Dahan

Chris Patterson (uno dei principali contributori di Mass Transit) domande "Using Sagas to ensure proper sequential message order" question

StackOverflow/risposte: "Preserve message order when consuming MSMQ messages in a WCF application"

DOMANDA ----- -----

devo dire che sto perplesso sul motivo per cui è necessario garantire l'ordine messaggio - vuoi essere nella stessa posizione, se si stesse utilizzando un HTTP/SOAP protocollo? La mia ipotesi è no, allora perché è un problema in MSMQ?

Buona fortuna, spero che questo aiuta,

+0

Sto sollevando notifiche che dipendono l'una dall'altra. Ad esempio, un treno non può lasciare una stazione prima che sia arrivato lì. Voglio assicurarmi che la notifica di OnDepart sia gestita dopo OnArrive, altrimenti la gestione di OnDepart potrebbe non funzionare (ad es.: non ha funzionato per quanto tempo il treno ha speso alla stazione se non avesse ancora gestito il corrispondente OnArrive) – batwad

+0

Non dovresti mai aumentare OnArrive prima di OnDepart, il che significa anche che non dovresti inviarlo. Guarda il Pattern Saga, è esattamente ciò che intendi: devi gestire lo stato al di fuori di MSMQ. –

+0

Ok, penso di averlo finalmente capito ... Stai inviando un evento OnDepart, tuttavia sei preoccupato che quando l'evento OnArrive viene attivato in seguito l'evento OnDepart potrebbe non essere ancora stato elaborato ... Sì, vedo il problema - ma sfortunatamente non posso davvero darti una soluzione d'oro diversa da quella: usa una sorta di identificatore univoco, se non puoi garantire che MSMQ manda un messaggio prima di un altro (la rete di origine del treno si blocca quindi OnDepart è bloccato lì, e alla fine arriva il treno) quindi devi code intorno ad esso. Se ricevi un evento OnArrive e gli eventi OnDepart –

1

Garantire la consegna in ordine di messaggi è uno dei problemi di fatto di alta qualità con la messaggistica ad alto volume.

In un mondo ideale, le destinazioni dei messaggi devono essere in grado di gestire la messaggistica fuori ordine. Questo può essere ottenuto assicurando che la fonte del messaggio includa qualche tipo di informazione di sequenziamento. Anche in questo caso idealmente ciò assume la forma di una sorta di timbro di lotto x-of-n (messaggio 1 di 10, 2 di 10, ecc.). La destinazione del messaggio è quindi necessaria per assemblare i dati in ordine una volta che è stato consegnato.

Tuttavia, nel mondo reale non c'è spesso spazio per modificare i sistemi a valle per gestire i messaggi che arrivano fuori ordine. In questo caso si hanno due scelte:

  1. interamente devoluto a thread singolo - in realtà di solito si può trovare una sorta di 'id raggruppamento' il che significa che si può andare a thread singolo in un certo senso for-each-gruppo, il che significa ha ancora concomitanza tra diversi gruppi di messaggi.
  2. Implementare un wrapper re-sequencer attorno a ciascuno dei sistemi consumer che si desidera ricevere messaggi in ordine.

Nessuna soluzione è molto bella, ma questo è l'unico modo in cui penso che si possa avere simultaneità e consegna dei messaggi in ordine.

+0

Interessante, ma se un ServerA prende il messaggio 1-di-10 fuori dalla coda e ServerB prende il messaggio 2-di-10 fuori dalla coda, nessuno dei due potrà mai ottenere un batch completo da rieseguire. – batwad

+0

Quindi in questa configurazione è necessaria una coda per gruppo. Non è molto pratico. –

Problemi correlati