エクスポート (0) 印刷
すべて展開

How to: Use CloudFx to Implement a Request-Reply Message Exchange Pattern using Service Bus Topics

This topic describes the simplest method of using the Cloud Application Framework and Extensions (CloudFx) library with Windows Azure Service Bus to send and receive messages using the very common request-reply enterprise application integration (EAI) messaging pattern. (The request-reply pattern is described at http://www.eaipatterns.com/RequestReply.html.) The sample application uses pair of Service Bus topics to create subscriptions that listen for request messages and send reply messages to the sender of that request.

It is important to note that request-reply implementations can be synchronous (in that the client’s request process waits for the reply message) or asynchronous, but creating a solid asynchronous request-reply implementation can take some time to get right. The sample application in this topic makes use of CloudFx to decouple the request process from the reply process, making the message exchange fully asynchronous and straightforward to use at the application level.

For a short example that demonstrates sending and receiving a basic message in CloudFx using a Service Bus topic, see How to: Use CloudFx to Send and Receive Messages with a Service Bus Topic and Subscription.

Important重要
This topic assumes that you have Visual Studio 2010 SP1 or later and have already installed the NuGet package manager Visual Studio extension. If you have not done so, download and install the NuGet extension. This topic also uses the Windows Azure SDK for .NET version 1.7. To download and install that SDK, see this download page.

In addition, graphics at the end of the topic that display the Service Bus topic and subscription structure are screenshots of the Service Bus Explorer tool, which you are free to download and use.

Configure the Project for the .NET 4.0 Target Framework

  1. Open Visual Studio running as an administrator, which is required to use the Service Bus .NET libraries.

  2. Create a new console application. (Note that although this sample uses a local console application to create the example quickly, CloudFx supports usage locally and in Windows Azure.)

  3. Right-click the new project in the solution and choose Properties. In the properties tab, confirm that the Target Framework is set to .NET 4.0 and not any other Target Framework, as shown in the following graphic.

    note
    Note that the following graphic does not match the sample application in this topic. However, the tasks are identical, no matter what the name of the solution or project.

    Visual Studio のターゲット変更ダイアログ。
    note
    If you do need to change your target framework, depending on your configuration Visual Studio may need to close the project and then reload it.

  4. In the Solution Explorer, download the NuGet packages and add them into your project by right-clicking the project and choosing Manage NuGet Packages… as shown in the following graphic.

    VS での NuGet パッケージの要求。
  5. In the NuGet search text box, type “CloudFx” and press ENTER. When the CloudFx package appears, click Install, accept the prompts that follow, and then close the dialog when it has finished.

    現在の CloudFX Nuget ビュー。

Add the Code

  1. Add the following using statements to the top of the program file.

    #region Using references
    using System;
    using System.Runtime.Serialization;
    using Microsoft.Experience.CloudFx.Framework;
    using Microsoft.Experience.CloudFx.Framework.Configuration;
    using Microsoft.Experience.CloudFx.Framework.Messaging;
    
    #endregion 
    
    
  2. Enter the following empty try/catch/finally code block in the Main method. Most sample code is placed inside the try code block.

    try
    {
    
    }
    catch (Exception ex)
    {
      Console.ForegroundColor = ConsoleColor.Red;
      Console.Write(ExceptionTextFormatter.Format(ex));
      Console.ReadLine();
    }
    finally
    {
      Console.ResetColor();
    }
    
    
  3. Locate the <appSettings> element in your project’s application configuration file (app.config). This code example requires your issuer secret and service namespace.

    note
    The following code requires your Service Bus Default Issuer (the default value is owner), Service Bus Default Key, and your service namespace. For information about how to create a service namespace, see Create a Service Namespace. For information about how to obtain your Default Key for a particular namespace, see Obtain the Default Credentials for the Namespace.

    In the <appSettings> element, locate the first <add> element and add your Service Bus namespace, your Service Bus Default Key, and the Default Key in the value attribute.

    Then add two more <add> elements, one like <add key="RequestTopicName" value="CloudServiceRequest" /> and one like <add key="ResponseTopicName" value="CloudServiceResponse" />, so that the complete <appSettings> element looks like the following, except with your values filled into the first connection string <add> element.

    <appSettings>
    <!-- Service Bus specific app settings for messaging connections -->
      <add key="Microsoft.ServiceBus.ConnectionString" 
    value="Endpoint=sb://<your_SB_Namespace>.servicebus.windows.net;SharedSecretIssuer=owner;SharedSecretValue=<Default_Key>"/>
      <add key="RequestTopicName" value="CloudServiceRequest" />
      <add key="ResponseTopicName" value="CloudServiceResponse" />
    </appSettings>
    
    
  4. As the first added code to the try block inside the Main method, add the following code to use the app.config values from the preceding step to configure two Service Bus topic endpoints, one to send requests and one to receive responses.

    // Retrieve Service Bus connection string configuration.  CloudApplicationConfiguration 
    // recognizes the execution environment and decides whether to use web/app.config 
    // or ServiceConfiguration.cscfg to retrieve the application configuration.
    var serviceBusConnString = 
      CloudApplicationConfiguration.Current.AppSettings["Microsoft.ServiceBus.ConnectionString"];
    
    // Create a definition of a Service Bus topic endpoint using the 
    // connection string and topic name from the config file. 
    // This topic is used to send requests between 2 parties 
    // using the request-response message exchange pattern. 
    var requestTopic = ServiceBusEndpointInfo.Parse(serviceBusConnString);
    requestTopic.EndpointType = ServiceBusEndpointType.Topic;
    requestTopic.TopicPath = 
      CloudApplicationConfiguration.Current.AppSettings["RequestTopicName"];
    
    // Create a definition of a Service Bus topic endpoint using the connection
    // string and topic name from the config file. The purpose of this topic 
    // is to allow one connected party to reply back to the other party with responses. 
    var responseTopic = ServiceBusEndpointInfo.Parse(serviceBusConnString);
    responseTopic.EndpointType = ServiceBusEndpointType.Topic;
    responseTopic.TopicPath = 
      CloudApplicationConfiguration.Current.AppSettings["ResponseTopicName"];
    
    
  5. Just after the configuration work in the preceding step, add the following code, which shortens the duration of the message receive loop to reduce waiting time.

    // Customize the publish/subscribe channel settings to shorten
    // the receive loops. (The default wait timeout is 30 seconds.) 
    var requestResponseChannelSettings =
      new ServiceBusRequestResponseChannelSettings 
      {
        MessageWaitTimeout = TimeSpan.FromSeconds(1)
      };
    
    
  6. Inside the try code block underneath the preceding code, add the following code to create two messaging channels of type ServiceBusRequestResponseChannel. These represent the two applications that talk to each other using the request-response pattern.

    // Create 2 messaging channels, one for each connected party. 
    // Uses a pair of one-way publish/subscribe messaging endpoints  
    // over which the request/response pattern will be facilitated. 
    Console.WriteLine("Creating request and reply channels...");
    using (var backEndSystem = new ServiceBusRequestResponseChannel(
        "BackendSystem", requestTopic, responseTopic, requestResponseChannelSettings
        )
      )
    using (var frontEndClient = new ServiceBusRequestResponseChannel(
      "FrontEndClient", requestTopic, responseTopic, requestResponseChannelSettings
      )
    )
    {
    }
    
    
  7. Inside the using code block added in the preceding step, add the following code. This code creates a System.Reactive.ISubject for request-reply messages sent to the back-end system application, and passes the observing anonymous delegate that is invoked when a message is received, returning a response object back to the requestor using the OnNext method. (For more information about subjects, which are both observers and observables, see Using Subjects.)

    // Create a handler for "GetStatusRequest" messages. It will invoke a 
    // private method which processes the requests and returns responses. 
    Console.WriteLine("Creating an observer that processes requests and returns responses...");
    var getStatusRequestHandler = 
      backEndSystem.Subscribe<GetStatusRequest, GetStatusResponse>(“GetStatus”);
    getStatusRequestHandler.Subscribe(
      req => getStatusRequestHandler.OnNext( HandleGetStatusRequest(req) )
    );
    
    
    Important重要
    Now, of course, your code does not compile because the GetStatusRequest and GetStatusResponse types and the HandleGetStatusRequest method included in the preceding step does not have an implementation. In Visual Studio, right-click each unknown type or method and choose Generate… and select the appropriate code block: Class… for the two classes, and Method Stub… for the HandleGetStatusRequest method. The types (in new project .cs files) and a stub method are generated for you and your code compiles when you press CTRL+SHFT+B. Or you can write the code yourself.

    Important重要
    Make sure you go into the GetStatusRequest.cs and GetStatusResponse.cs files and make the types public, or they are not usable from the main program.cs file.

  8. In the using block underneath the code added by the preceding step, add the following code. This code calls the Publish method 10 times, injecting the types it expects to send and then receive, passes the GetStatusRequest parameter, and writes what it has done to the console.

    // Simulate a few requests to the backend system. 
    Console.WriteLine("Sending 10 messages...");
    for (var i = 0; i < 10; i++)
    {
      // Send a message to the backend system and receive its response. 
      var response =
        frontEndClient.Publish<GetStatusRequest, GetStatusResponse>(
          new GetStatusRequest { DeviceID = i }
        );
    
      Console.Write("Received response for Device ID {0}. Device status: ", i);
      if (response.Status == DeviceAvailabilityStatus.Offline)
        Console.ForegroundColor = ConsoleColor.Yellow;
      else if (response.Status == DeviceAvailabilityStatus.Online)
        Console.ForegroundColor = ConsoleColor.Green;
      else
        Console.ForegroundColor = ConsoleColor.Red;
      Console.Write(response.Status + "\r\n");
      Console.ResetColor();
    }
    
    Console.WriteLine("\nIt is now a good time to use the Service Bus Explorer: ");
    Console.WriteLine("(http://code.msdn.microsoft.com/windowsazure/Service-Bus-Explorer-f2abca5a)");
    Console.WriteLine(" to examine the topics and subscriptions created in the Service Bus.");
    Console.WriteLine(
      "Once you finish reviewing the entities, press any key to close the application..."
    );
    Console.ReadLine();
    Console.WriteLine("Cleaning up channel resources...");
    
    
  9. Now, add the following code somewhere inside the namespace, but outside the class (since it is a value type itself). After adding this enum, the application is ready to add the final bit of code to send messages.

    public enum DeviceAvailabilityStatus
    {
      Unspecified,
      Online,
      Offline
    }
    
    
  10. Press CTRL+SHFT+B to build the project, and note that the solution lacks some properties it needs to compile and execute. Right-click and choose Generate… to create the GetStatusRequest.DeviceID and GetStatusResponse.Status properties in their respective files. Note that because of the addition of the enum in the preceding step, the generated Status property returns the appropriate enum type.

    Important重要
    Once you have created the new properties, you can compile your code. However, to enable serialization support at runtime you must now decorate each type with the System.Runtime.Serialization.DataContractAttribute and the properties with the System.Runtime.Serialization.DataMemberAttribute, and add the System.Runtime.Serialization namespace to the namespaces section at the top of those respective files.

  11. Replace the HandleGetStatusRequest method with the following code, which sets the DeviceAvailabilityStatus value for the reply message.

        private static GetStatusResponse HandleGetStatusRequest(GetStatusRequest request)
    {
      return new GetStatusResponse { 
    Status = 
        (request.DeviceID % 2 == 0 ? DeviceAvailabilityStatus.Offline : DeviceAvailabilityStatus.Online) 
      };
    }
    
    

Running the Sample Application

  1. Press F5. You should see a console the output of which is similar to the following graphic.

    コンソール出力。
  2. Before pressing ENTER and ending the application, it is helpful to view the topic an subscription structure created to support the request-reply pattern. The easiest way to do this is to download the Service Bus Explorer tool and use it to view the structure before ending the application (at which time the application deletes the Service Bus subscriptions it created. The topics are not deleted or modified.) If you do so, the overall structure should look like the following graphic:

    すべてのトピックおよびサブスクリプション ビュー。
  3. Finally, select the Resp-FrontEndClient subscription and examine the filter used. It should look like the following graphic:

    応答サブスクリプション フィルター。

This is the entire program.cs code file used to create this sample.

using System;
using System.Runtime.Serialization;
using Microsoft.Experience.CloudFx.Framework;
using Microsoft.Experience.CloudFx.Framework.Configuration;
using Microsoft.Experience.CloudFx.Framework.Messaging;

namespace RequestReplyPatternSample
{
  class Program
  {
    static void Main(string[] args)
    {
      try
      {
        // Retrieve Service Bus connection string configuration.  CloudApplicationConfiguration 
        // recognizes the execution environment and decides whether to use web/app.config 
        // or ServiceConfiguration.cscfg to retrieve the application configuration.
        var serviceBusConnString =
          CloudApplicationConfiguration.Current.AppSettings["Microsoft.ServiceBus.ConnectionString"];

        // Create a definition of a Service Bus topic endpoint using the 
        // connection string and topic name from the config file. 
        // This topic is used to send requests between 2 parties 
        // using the request-response message exchange pattern. 
        var requestTopic = ServiceBusEndpointInfo.Parse(serviceBusConnString);
        requestTopic.EndpointType = ServiceBusEndpointType.Topic;
        requestTopic.TopicPath =
          CloudApplicationConfiguration.Current.AppSettings["RequestTopicName"];

        // Create a definition of a Service Bus topic endpoint using the connection
        // string and topic name from the config file. The purpose of this topic 
        // is to allow one connected party to reply back to the other party with responses. 
        var responseTopic = ServiceBusEndpointInfo.Parse(serviceBusConnString);
        responseTopic.EndpointType = ServiceBusEndpointType.Topic;
        responseTopic.TopicPath =
          CloudApplicationConfiguration.Current.AppSettings["ResponseTopicName"];

        // Customize the publish/subscribe channel settings to shorten
        // the receive loops. (The default wait timeout is 30 seconds.) 
        var requestResponseChannelSettings =
          new ServiceBusRequestResponseChannelSettings 
          {
            MessageWaitTimeout = TimeSpan.FromSeconds(1)
          };

        // Create 2 messaging channels, one for each connected party. 
        // Uses a pair of one-way publish/subscribe messaging endpoints  
        // over which the request/response pattern will be facilitated. 
        Console.WriteLine("Creating request and reply channels...");
        using (var backEndSystem = new ServiceBusRequestResponseChannel(
            "BackendSystem", requestTopic, responseTopic, requestResponseChannelSettings
            )
          )
        using (var frontEndClient = new ServiceBusRequestResponseChannel(
          "FrontEndClient", requestTopic, responseTopic, requestResponseChannelSettings
          )
        )
        {
          // Create a handler for "GetStatusRequest" messages. It will invoke a 
          // private method which processes the requests and returns responses. 
          Console.WriteLine("Creating an request observer that processes requests and returns responses...");
          var getStatusRequestHandler =
            backEndSystem.Subscribe<GetStatusRequest, GetStatusResponse>("GetStatus");
          getStatusRequestHandler.Subscribe(
            req => getStatusRequestHandler.OnNext( HandleGetStatusRequest(req) )
          );

          // Simulate a few requests to the backend system. 
          Console.WriteLine("Sending 10 messages...");
          for (var i = 0; i < 10; i++)
          {
            // Send a message to the backend system and receive its response. 
            var response =
              frontEndClient.Publish<GetStatusRequest, GetStatusResponse>(
                new GetStatusRequest { DeviceID = i }
              );

            Console.Write("Received response for Device ID {0}. Device status: ", i);
            if (response.Status == DeviceAvailabilityStatus.Offline)
              Console.ForegroundColor = ConsoleColor.Yellow;
            else if (response.Status == DeviceAvailabilityStatus.Online)
              Console.ForegroundColor = ConsoleColor.Green;
            else
              Console.ForegroundColor = ConsoleColor.Red;
            Console.Write(response.Status + "\r\n");
            Console.ResetColor();
          }

          Console.WriteLine("\nIt is now a good time to use the Service Bus Explorer: ");
          Console.WriteLine("(http://code.msdn.microsoft.com/windowsazure/Service-Bus-Explorer-f2abca5a)");
          Console.WriteLine(" to examine the topics and subscriptions created in the Service Bus.");
          Console.WriteLine(
            "Once you finish reviewing the entities, press any key to close the application..."
          );
          Console.ReadLine();
          Console.WriteLine("Cleaning up channel resources...");

       }

      }
      catch (Exception ex)
      {
        Console.ForegroundColor = ConsoleColor.Red;
        Console.Write(ExceptionTextFormatter.Format(ex));
        Console.ReadLine();
      }
      finally
      {
        Console.ResetColor();
      }

    }

    private static GetStatusResponse HandleGetStatusRequest(GetStatusRequest request)
    {
      return new GetStatusResponse
      {
        Status =
            (request.DeviceID % 2 == 0 ? DeviceAvailabilityStatus.Offline : DeviceAvailabilityStatus.Online)
      };
    }
  }

  public enum DeviceAvailabilityStatus
  {
    Unspecified,
    Online,
    Offline
  }

}


The following file is the generated GetStatusRequest.cs file and its modifications used to create this sample application.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;

namespace RequestReplyPatternSample
{
  [DataContract]
 public class GetStatusRequest
  {
    [DataMember]
    public int DeviceID { get; set; }
  }
}


The following file is the generated GetStatusResponse.cs file and its modifications used to create this sample application.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;

namespace RequestReplyPatternSample
{
  [DataContract]
 public class GetStatusResponse
  {
    [DataMember]
    public DeviceAvailabilityStatus Status { get; set; }
  }
}



ビルド日:

2013-10-23

コミュニティの追加

表示:
© 2014 Microsoft