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);
}
}
}
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). –