Il presente articolo è stato tradotto automaticamente.

Previsione: nuvoloso

Completamento del percorso esplorativo sulle code di AppFabric

Joseph Fultz

Scaricare il codice di esempio

Joseph FultzNel numero di ottobre, toccato alcune delle nuove funzionalità di Windows Azure AppFabric Service Bus (msdn.microsoft.com/magazine/hh456395).Questo mese, continuerò con lo scenario che ho iniziato seguendo il viaggio di ritorno indietro.Finora nello scenario, un negozio ha richiesto un controllo inventario da negozi nelle vicinanze pubblicando le richieste di controllo inventario a un argomento.I negozi sottoscritti l'argomento ha ricevuto richieste basate su un filtro su abbonamento che limitata messaggi a quelle all'interno della loro regione e non inviati da loro.

Per il viaggio di ritorno, ti si basano su un paio di funzionalità di Windows Azure AppFabric Service Bus code.In primo luogo, ci sono due proprietà importanti a cui io sarò assegnare valori su BrokeredMessage che viene inviato al tema.Il primo a cui io sarò assegnare il valore è la proprietà ReplyTo, per dire il destinatario dove inviare il messaggio.Questa sarà una coda specifica creata dal mittente al momento che e invia il messaggio.Voglio usare una coda invece di un argomento, in questo caso, perché la risposta sta per un unico destinatario, a differenza del modello di richiesta, che equiparato a una trasmissione per aiuto a chiunque ascolto.

La nuova parte del flusso del messaggio è mostrata in Figura 1, come quella che scorre a destra della fila di negozi.

Inquiry and Response Round-Trip
Figura 1 richiesta e risposta andata e ritorno

Aggiornare il codice di richiesta in uscita

Nel primo passaggio, ho ricevuto il messaggio l'argomento e dimostrato raccogliendo da sottoscrizioni appropriate, ma due elementi importanti sono stati lasciati annullati per supportare una risposta facile e piacevole.Il primo elemento è impostando la proprietà CorrelationId della BrokeredMessage.La proprietà è parte dell'API, così non devo includere come parte della mia classe di dati o schema.Perché è una stringa, potrebbe essere qualcosa di significativo, ma dovrebbe essere qualcosa che sarà identificare in modo univoco il messaggio tale che qualsiasi risposta che ha un CorrelationId corrispondente non può essere confuso per abbinare alcuni altri richiesta nel sistema.Per i miei scopi di campione, io uso un GUID, ma che è una scommessa abbastanza sicura in pratica, troppo.Ecco il codice:

BrokeredMessage msg = BrokeredMessage.CreateMessage(data);
// Add props to message for filtering
msg.Properties["Region"] = data.Region;
msg.Properties["RequesterID"] = data.RequesterID;
// Set properties for message
msg.TimeToLive = TimeSpan.FromSeconds(30);
msg.ReplyTo = returnQueueName;
msg.CorrelationId = Guid.NewGuid().ToString();

Il BrokeredMessage ha anche una proprietà denominata SessionId, che non sto usando qui. SessionId è un'altra proprietà per raggruppamento logico che è bello avere al livello della busta del messaggio, perché facilita il raggruppamento dei messaggi che sono tutti collegati. Pone la questione di come si differenzia nell'intento di CorrelationId. Ci sono due scenari in cui la proprietà SessionId potrebbe essere particolarmente utile. Il primo è in un sistema dove più demoni stanno facendo varie richieste che sono tutti connessi. Il parametro CorrelationId avrebbe utilizzato per indirizzare la risposta al processore richiedente. La proprietà SessionId avrebbe utilizzato per raggruppare tutti i messaggi inviati e ricevuti attraverso i nodi di elaborazione. Tale raggruppamento è utile per determinare lo stato di elaborazione in un sistema di analisi e debug. Per le stesse ragioni, questo è un costrutto utile in un sistema che rende molte richieste come parte di un processo globale (per esempio, il processo di acquisto, controllo inventario, verifica di pagamento, inviare a compimento e così via), ma il flusso esatta e la temporizzazione non è garantita.

Dopo aver impostato il parametro CorrelationId del messaggio in uscita, il prossimo cambiamento che ho bisogno di fare è impostare la proprietà ReplyTo. Ho potuto creare un altro argomento e hanno tutti i negozi di monitorare per risposta o utilizzare una coda singola, ma che creerebbe traffico inutile e sotto le volte del carico di essere più probabilità di causare un collo di bottiglia. Così, ha senso per semplicemente creare una coda di risposta al momento della richiesta e lasciare che il destinatario di sapere dove che è. Poiché si tratta di una stringa, essa potrebbe essere qualsiasi cosa, anche se vorrei suggerire un nome completo per evitare confusione o collisione in future evoluzioni del software. Si potrebbe iniziare con solo il nome della coda, ma in manutenzione e in espansione, questo potrebbe portare a confusione, come il sistema inizia a sostenere più servizio autobus spazi dei nomi e le code secondarie. Inoltre, un indirizzo completo sarà meglio per i destinatari non utilizzando Microsoft.NET Framework.

Le ultime due cose che devo fare prima di inviare il messaggio è quello di creare la coda di risposta e avviare un timer per controllare le risposte. Ho iniziato con il metodo GetQueue. Al momento di questa scrittura, la documentazione (bit.ly/pnByFw) GetQueue stati per il "ritorno valore" che "tipo" è uguale a Microsoft.ServiceBus.Messaging.Queue; un handle di coda per la coda; oppure null se la coda non esiste nello spazio dei nomi di servizio. Tuttavia, questo non è il caso. In realtà, verrà generata un'eccezione:

// Check if-exists and create response queue
try
{
  returnQ = this.ServiceBusNSClient.GetQueue(returnQueueName);
}
catch (System.Exception ex)
{
  Debug.WriteLine(ex.Message);
}
if (returnQ == null)
{
  returnQ = this.ServiceBusNSClient.CreateQueue(returnQueueName);
}
checkQTimer.Enabled = true;

Così, ho eseguito il wrapping del metodo GetQueue in un blocco try-catch e spostato. Il mio codice di esempio, come è tipico, niente più di scrivere l'errore. Una volta creata la coda, posso assegnare a una variabile così posso fare riferimento per il controllo della coda e quindi attivare il timer che ho creato quando l'applicazione iniziato.

L'aggiornamento del destinatario e rispondere

Con le modifiche adeguate sul lato del mittente, che devo fare alcune aggiunte, così posso rispondere alle richieste. Ho intenzione di utilizzare un gran parte hardcoded risposta, ma ho intenzione di elencare i messaggi in una griglia, così posso selezionare quello a cui ti rispondo. Ho istituito un meccanismo di eventi per la notifica dell'interfaccia utente di nuovi messaggi. Una volta che un messaggio viene ricevuto, ho impostato negozio ID del destinatario corrente come il risponditore e poi notificare l'interfaccia utente del messaggio per la visualizzazione:

recvSuccess = msgReceiver.TryReceive(TimeSpan.FromSeconds(3), out NewMsg);
if (recvSuccess)
{
  CreateInquiry.InventoryQueryData qryData = 
    NewMsg.GetBody<CreateInquiry.InventoryQueryData>();
  NewMsg.Properties.Add("ResponderId", this.StoreID);
  EventContainer.RaiseMessageReceived(NewMsg);
}

Nell'interfaccia utente, ho sottoscritto l'evento che si riceve il messaggio e ho poi passarlo a un metodo per aggiornare gli elementi di interfaccia utente che eseguiranno il necessario controllo metodo InvokeRequired:

void EventContainer_MessageReceivedEvent(object sender, MessagingEventArgs e)
{
  BrokeredMessage msg = (BrokeredMessage)e.MessageData;
  var RequestObject =
    msg.GetBody<CreateInquiry.InventoryQueryData>();
  RequestObject.ResponderID = msg.Properties["ResponderId"].ToString();
  this.ReceivedMessages.Add(RequestObject);
  UpdateUIElements(msg.MessageId, RequestObject);
}

Con il codice completo per recuperare i messaggi per argomento e per aggiornare l'interfaccia utente, io posso ora visualizzare i messaggi in arrivo (vedere Figura 2).

The Inventory Topic Monitor
Nella figura 2 il Monitor di argomento inventario

Questa interfaccia utente (io non ero assunto per mie competenze di progettazione esperienza utente, ovviamente) mi permetterà di selezionare uno dei messaggi e rispondere con una quantità impostata nella casella di testo inferiore. Il codice di risposta sarà un compito piuttosto semplice e breve, come devo solo aggiungere un piccolo frammento di codice per impostare la quantità dell'oggetto del messaggio e poi inviarlo alla coda specificata nella proprietà ReplyTo del messaggio.

Per questo esempio, sto semplicemente aggiungendo i messaggi ricevuti dal soggetto a un dizionario < string, BrokeredMessage > oggetto dove sto usando il BrokeredMessage.MessageId come la chiave per il dizionario. Io uso semplicemente l'Id messaggio dalla griglia per recuperarla dal dizionario e quindi creare un nuovo BrokeredMessage assegnando lo stesso CorrelationId e assegnando valore per le proprietà ResponderId e quantità. Proprio come quando si riceve dall'argomento, utilizzerò la MessagingFactory per creare un QueueClient e da quello oggetto un MessageSender:

// Send message
  SharedSecretCredential credential =
    TransportClientCredentialBase.CreateSharedSecretCredential(
    Constants.issuerName, Constants.issuerKey);
  Uri sbUri = ServiceBusEnvironment.CreateServiceUri(
           "sb", Constants.sbNamespace, String.Empty);
MessagingFactory Factory =  MessagingFactory.Create(sbUri, credential);
QueueClient QClient = Factory.CreateQueueClient(msg.ReplyTo);
MessageSender Sender = QClient.CreateSender();
Sender.Send(ResponseMsg);

Che Invia risposta indietro, così dobbiamo spostare il nostro obiettivo di nuovo app originari per elaborare la risposta.

Ricevere la risposta

Per questo esempio, voglio solo tirare le risposte fuori della coda. Perché il codice per inviare la richiesta è stato modificato per avviare monitoraggio coda ReplyTo, dovrei davvero aggiungere il codice effettivo nel controllare la coda. Io iniziare creando un MessageReceiver e istituito un semplice ciclo per ottenere tutti i messaggi disponibili nella coda di questo tempo intorno While:

void checkQTimer_Tick(object sender, EventArgs e)
{
  MessageReceiver receiver =
    QClient.CreateReceiver(ReceiveMode.ReceiveAndDelete);
  BrokeredMessage NewMessage = null;
  while (receiver.TryReceive(TimeSpan.FromSeconds(1), out NewMessage))
  {
    InquiryResponse resp = new InquiryResponse();
    resp.CorrelationID = NewMessage.CorrelationId;
    resp.Message = NewMessage;
    InquiryResponses.Add(resp);
  }
 }

Come prima, io uso il metodo TryReceive. Mentre funziona per questo esempio, lo considererei facendo un po ' diversamente per un'interfaccia utente reale, perché il metodo blocca il thread, così presta meglio essere eseguito su un thread diverso. Voglio recuperare l'intero BrokeredMessage dall'elenco di CorrelationId, così ho creato un oggetto e utilizzare il parametro CorrelationId per filtrare l'oggetto più tardi. Io voglio il messaggio come un BrokeredMessage, perché voglio dati che fa parte della dotazione BrokeredMessage che incapsula ora mio oggetto InquiryData (vedere Figura 3).

Inquiry Request and Responses
Figura 3 inchiesta richiesta e risposte

Modificando il codice SelectedIndexChanged del controllo ListBox, semplicemente CorrelationId utilizzata come elemento di afferrare e utilizzarlo per ottenere le risposte di interesse fuori dell'elenco che sto costruendo come risposte show fino nella coda:

string correlationId =
  lbRequests.SelectedItem.ToString();
List<InquiryResponse> CorrelatedList =
  InquiryResponses.FindAll(
  delegate(InquiryResponse resp)
{
  return resp.CorrelationID == correlationId;
});

L'ultimo bit di lavoro consiste nell'aggiungere le risposte per il controllo DataGrid per visualizzare i negozi che hanno risposto alla mia richiesta di inventario. Aggiungendo alla bellezza del mio UI sterile, si noterà che sto usando i GUID, ma spero che è ovvio per il lettore che questo sarà sostituito da descrizioni user-friendly, lasciando i GUID brutto e gli ID per le pagine di particolari estesi.

Review

Dopo crescere come un monello esercito, ho un sacco di detti e altre cose del genere bruciati nella mia testa. Un esempio è il "metodo militare" di educazione, che è che ti dirò che cosa stiamo andando a fare, lo faremo, e poi ti dirò quello che abbiamo fatto. Beh, io sono presso il "che cosa abbiamo fatto" parte. Ho iniziato questa scrittura come una sola voce, ma ha trovato che affrontare l'andata e ritorno completo di essere troppo per una singola colonna. Così dividere in due voci: uno di uscire la richiesta e il secondo per ottenere la risposta indietro. Il mio obiettivo era quello di presentare le caratteristiche di base del Windows Azure AppFabric ServiceBus e dare una sensazione generale per il suo utilizzo. Quando pensando a tecnologia, mi piace sempre di avvolgerla in qualche tipo di contesto, e in questo caso ho voluto il contesto per avere una query di prodotto inter-store, perché è uno scenario fattibile e ha senso utilizzare un mix di argomenti e di code.

Come di questa scrittura, la può CTP di Windows Azure App­è stato rilasciato il tessuto ServiceBus e alcune informazioni su di esso possono essere trovati alla bit.ly/it5Wo2. Inoltre, è possibile ottenere coinvolto sul forum a bit.ly/oJyCYx.

Joseph Fultz è un software architect in Hewlett-Packard Co, lavorando come parte della HP.com globale di esso. In precedenza è stato software architect di Microsoft, lavorando con la sua impresa di alto livello e clienti ISV definizione di architettura e soluzioni di progettazione.

Grazie all'esperto tecnica seguente per la revisione di questo articolo: Jim Keane