AppFabric Service Bus Relayed Messaging

Picking up where we left off in the Service Bus Introduction, this post will walk through a Relayed Messaging sample in order to highlight how the AppFabric Service Bus could be utilized to build Hybrid applications. In addition to the basic sample, I will also demonstrate how to provision a BizTalk Server 2010 Receive Location on the Service Bus.

Organizations typically expose web services to the internet using web servers hosted on their private infrastructure along with a wide array of load balancers, firewalls and proxy configurations to help minimize the attack surface area and improve availability. In special cases, a secure VPN could be provisioned to provide a tunnel between enterprises, but the overhead of maintaining the resource makes this option less desirable. Regardless of the techniques used, the web server will passively await an inbound request and hope that the infrastructure is configured to allow valid requests to pass through. The communication pattern for the Azure AppFabric Service Bus takes a different approach by initiating communication using an outbound request to the Service Bus hosted in one of Microsoft’s data centers when the web service host process is initialized. This outbound call to the Service Bus registers an endpoint within a pre-configured namespace and creates a bi-directional TCP socket between the Service Bus and the on-premise service. Once the Service Bus endpoint is registered, anyone with access to the Service Bus namespace can register as a client to that endpoint. When a client sends a message to the endpoint in the cloud, the message is “relayed” to the service listening on-premise. If the service is defined as a one-way service the conversation is over. If the service is configured as a two-way, the response is sent back to the cloud and relayed back to the initial caller.

The key behind communicating with the Service Bus is the family of relay bindings that Microsoft has provided as an extension to the current set of WCF bindings. The following table provides a list of some of the current/standard WCF bindings and the equivalent Service Bus relay binding:

Standard WCF Binding Equivalent Relay Binding
BasicHttpBinding BasicHttpRelayBinding
WebHttpBinding WebHttpRelayBinding
WS2007HttpBinding WS2007HttpRelayBinding
NetTcpBinding NetTcpRelayBinding
N/A NetOnewayRelayBinding
N/A NetEventRelayBinding

In order to see how to communicate with the Service Bus using these relay bindings, we will walk through a simple demo using console applications: one acting as the service and the other acting as the client.

In order to run the demos, you will need the following:

Windows Azure AppFabric SDK 1.5 September 2011 (Download)

Windows Azure Account – If you do not have an account with Microsoft, you can obtain a free 90 day trial here.

Service Bus Namespace – If you do not have a Service Bus namespace, follow these instructions from MSDN.

You will need to edit all of the app.config files by inserting the corresponding Service Bus namespace name, Issuer Name and Issued Key fields that were created automatically by provisioning a Service Bus namespace. Once you are logged into the Azure Management Portal, select Service Bus from the left navigation. Once you are on the Service Bus page, locate the “Default Key” property in the right pane. Select “View” to view the hidden data: Default Issuer and Default Key. The default name is always “owner”. In a production scenario, this credential should never be used. Neil Mackenzie wrote an excellent blog post detailing the best practices in this area.

Demonstration

We will create an EchoService that takes a string as a parameter and returns the string to the caller. When the ServiceHost is started, it will register itself with the Service Bus. We will then create a Client based on the EchoService contract, configure Service Bus credentials to allow a connection and then send messages to the service via the AppFabric Service Bus.

Service Contract

	[ServiceContract(Name = "IEchoContract", Namespace = "http://servicebus.microsoft.com/demo/relay/")] 
	public interface IEchoContract
	{
	    [OperationContract]
	    string Echo(string text);
	}
	 
	public interface IEchoChannel : IEchoContract, IClientChannel { }

Service Implementation

	[ServiceBehavior(Name = "EchoService", Namespace = "http://servicebus.microsoft.com/demo/relay/")] 
	class EchoService : IEchoContract
	{
	    public string Echo(string text)
	    {
	        Console.WriteLine("Echoing: {0}", text);
	        return text;            
	    }
	}

Service Host

	static void Main(string[] args)
	{
	    try
	    {
	        Console.WriteLine("Configuring service details.");
	 
	        string serviceNamespace = System.Configuration.ConfigurationManager.AppSettings["ServiceNamespace"];
	        string uriScheme = System.Configuration.ConfigurationManager.AppSettings["UriScheme"];
	        string servicePath = System.Configuration.ConfigurationManager.AppSettings["ServicePath"];
	 
	        Uri address = ServiceBusEnvironment.CreateServiceUri(uriScheme, serviceNamespace, servicePath);
	 
	        ServiceHost host = new ServiceHost(typeof(EchoService), address);
	 
	        Console.WriteLine("Connecting to the Service Bus.");
	 
	        host.Open();
	 
	        Console.WriteLine("Service address: " + address);
	        Console.WriteLine("Press [Enter] to exit");
	        Console.ReadLine();
	 
	        host.Close();
	    }
	    catch (Exception ex)
	    {
	        Console.WriteLine("Exception: \n{0}", ex.ToString());
	        Console.WriteLine("Press [Enter] to exit");
	        Console.ReadLine();
	    }
	}

The Service Contract, Service Implementation and Service Host are defined like any other WCF Service. The only line that is specific to the Service Bus is line 11 that uses the ServiceBusEnvironment.CreateServiceUri method to generate a Service Bus targeted Uri. The only thing left to review is the app.config which is where all of the magic happens.

Service Configuration

	<configuration>
	  <appSettings>
	    <add key="ServiceNamespace" value="{SERVICE_BUS_NAMESPACE_HERE}"/>
	    <add key="UriScheme" value="sb" /><!-- sb or https-->
	    <add key="ServicePath" value="RelayedMessagingDemo"/>
	  </appSettings>
	  <system.serviceModel>
	    <services>
	      <service name="RelayedMessaging.Service.EchoService">
	        <endpoint behaviorConfiguration="SharedSecretBehavior"
	                  binding="netTcpRelayBinding"
	                  contract="RelayedMessaging.Contract.IEchoContract"
	                  name="EchoService"/>
	                  <!--netTcpRelayBinding or basicHttpRelayBinding -->
	      </service>
	    </services>
	    <behaviors>
	      <endpointBehaviors>
	        <behavior name="SharedSecretBehavior">
	          <transportClientEndpointBehavior credentialType="SharedSecret">
	            <clientCredentials>
	              <sharedSecret issuerName="{DEFAULT_ISSUER_HERE}"
	                            issuerSecret="{DEFAULT_KEY_HERE}"/>
	            </clientCredentials>
	          </transportClientEndpointBehavior>
	          <serviceRegistrySettings
	                       discoveryMode="Public"
	                       displayName="RelayedMessagingDemo_ConsoleService"/>
	        </behavior>
	      </endpointBehaviors>
	    </behaviors>
	  </system.serviceModel>
	</configuration>

Line 3 is where we plug in the Service Bus namespace. This is used to build the Service Bus Uri.

Line 4 holds our Uri scheme. We are currently using sb with the netTcpRelayBinding, but we could choose to use the https scheme and connect to the Service Bus with the basicHttpRelayBinding.

Line 11 is where we can see the relay binding applied to the service endpoint.

Lines 19 – 25 are the most critical in the configuration. The transportClientEndpointBehavior configuration element contains the issuer and key that the Service Host will use to connect to the Service Bus and the Access Control Service (ACS) in order to authenticate and authorize the provisioning of the service endpoint with the Service Namespace.

Service Bus Client

	static void Main(string[] args)
	{
	    try
	    {
	        Console.WriteLine("Configuring service details.");
	 
	        Stopwatch stopWatch = new Stopwatch();
	 
	        string serviceNamespace = System.Configuration.ConfigurationManager.AppSettings["ServiceNamespace"];
	        string uriScheme = System.Configuration.ConfigurationManager.AppSettings["UriScheme"];
	        string servicePath = System.Configuration.ConfigurationManager.AppSettings["ServicePath"];
	 
	        Uri serviceUri = ServiceBusEnvironment.CreateServiceUri(uriScheme, serviceNamespace, servicePath);
	 
	        ChannelFactory<IEchoChannel> channelFactory =
	            new ChannelFactory<IEchoChannel>("RelayEndpoint", new EndpointAddress(serviceUri));
	 
	        Console.WriteLine("Connecting to the Service Bus.");
	 
	        IEchoChannel channel = channelFactory.CreateChannel();
	        channel.Open();
	 
	        Console.WriteLine("Connected to: " + serviceUri);
	        Console.WriteLine("Enter text to echo (or [Enter] to exit):\n");
	        string input = Console.ReadLine();
	        while (input != String.Empty)
	        {
	            try
	            {
	                stopWatch.Start();
	                string echoResult = channel.Echo(input);
	                stopWatch.Stop();
	                Console.WriteLine("\nResponse: {0}",
	                                                echoResult);
	                Console.WriteLine("Message exec time {0} ms\n",
	                              stopWatch.ElapsedMilliseconds.ToString());
	                stopWatch.Reset();
	            }
	            catch (Exception e)
	            {
	                Console.WriteLine("Error: " + e.Message);
	            }
	            input = Console.ReadLine();
	        }
	 
	        channel.Close();
	        channelFactory.Close();
	    }
	    catch (Exception ex)
	    {
	        Console.WriteLine("Exception: \n{0}", ex.ToString());
	        Console.WriteLine("Press [Enter] to exit");
	        Console.ReadLine();
	    }
	}

The client code is very straight-forward. We start by pulling a few values from the app.config, configuring the Service Bus Uri and using the standard WCF Channel Factory to create a communication channel. Once the channel is open, we send messages to the bus in a loop until the Enter key is pressed, measure the response time with a stopwatch and echo out the results to the console. Much like the Service.exe, the Service Bus magic happens in the configuration.

Client Configuration

	<configuration>
	  <appSettings>
	    <add key="ServiceNamespace" value="{SERVICE_BUS_NAMESPACE_HERE}"/>
	    <add key="UriScheme" value="sb" /><!-- sb or https-->
	    <add key="ServicePath" value="RelayedMessagingDemo"/>
	  </appSettings>
	  <system.serviceModel>
	    <client>
	      <endpoint behaviorConfiguration="SharedSecretBehavior"
	                binding="netTcpRelayBinding"
	                contract="RelayedMessaging.Contract.IEchoContract"
	                name="RelayEndpoint"/>
	      <!--netTcpRelayBinding or basicHttpRelayBinding -->
	    </client>
	    <behaviors>
	      <endpointBehaviors>
	        <behavior name="SharedSecretBehavior">
	          <transportClientEndpointBehavior credentialType="SharedSecret">
	            <clientCredentials>
	              <sharedSecret issuerName="{DEFAULT_ISSUER_HERE}"
	                            issuerSecret="{DEFAULT_KEY_HERE}"/>
	            </clientCredentials>
	          </transportClientEndpointBehavior>
	        </behavior>
	      </endpointBehaviors>
	    </behaviors>
	  </system.serviceModel>
	</configuration>

Line 3 is where we plug in the Service Bus namespace. This is used to build the Service Bus Uri.

Line 4 holds our Uri scheme. We are currently using sb with the netTcpRelayBinding because the scheme chosen in the client must match the scheme used in the service.

Line 10 is where we instruct the client to use the relay binding to connect to the service bus endpoint.

Lines 17 – 24 are the most critical for the client configuration as they configure the transportClientEndpointBehavior and sharedSecret elements.

With all of those pieces in place, we can run the Service.exe and see the endpoint registered on the Service Bus. To see the registered endpoint, navigate to the Service Gateway located at https://{YOUR_NAMESPACE_HERE}.servicebus.windows.net/. This is a simple Atom Feed that lists all of the endpoints registered on the Service Bus namespace. You can also use the Service Bus Explorer tool created by Paolo Salvatori. This tool is extremely useful especially when you work with Queues and Topics which I will cover in a subsequent post.

Once the Service is running and the endpoint is registered with the Service Bus, run the Client.exe, type a message and hit enter. You will see the message echoed in the Service.exe and a response back in the Client demonstrating the round trip. Since the communication happens behind the scenes, I’d like to highlight the message flow that we just witnessed:

  1. The message is sent from the Client to the Service Bus.
  2. The Service Bus relays the message to the Service listening on-premise.
  3. The Service responds back to the Service Bus.
  4. The Service Bus relays the response back to the Client.

On most days, I can communicate from Richmond, VA with either of the 2 U.S. Microsoft Data Centers in Chicago or San Antonio with full round-trips taking anywhere from 70 ms to 1000 ms. Considering all of the pathways that the messages have to traverse, it’s pretty remarkable.

Here is the most important point of this entire blog post: how many lines of code did I deploy to the cloud in this demo? Go ahead and count. I’ll wait.

[Queue Final Jeopardy Music]

Time’s up. The answer is zero lines of code were deployed. The only thing that we did here was use the Relay Bindings to communicate with the Bus, register an endpoint in the cloud and provision a communication channel with the Windows Azure AppFabric Platform-as-a-Service.

Bonus Material – Connecting BizTalk to the Service Bus

At this point in my presentations, I usually hear a few grumbles from attendees about the fact that I just used console apps and they are not in the business of writing the next instant messaging application. Fair enough. Let’s kick it up a notch and replace the Service.exe with something a bit more robust, BizTalk Server 2010. For those who may not know, BizTalk Server 2010 is very proficient at using WCF Adapters to communicate with a wide range of Line of Business systems. I have included a BizTalk project with the rest of the code already examined in this post. Much like the Service and Client configuration files above, the most important part of the project is the WCF Adapter configuration file which I exported for closer inspection.

	<configuration>
	  <system.serviceModel>
	    <services>
	      <service name="BizTalk">
	        <endpoint address="sb://{SERVICE_BUS_NAMESPACE_HERE}.servicebus.windows.net/RelayedMessagingDemo/"
	                  behaviorConfiguration="SharedSecretBehavior"
	                  binding="netTcpRelayBinding"  
	                  contract="BizTalk"
	                  name="BizTalkEchoService"/>
	      </service>
	    </services>
	    <behaviors>
	      <endpointBehaviors>
	        <behavior name="SharedSecretBehavior">
	          <transportClientEndpointBehavior credentialType="SharedSecret">
	            <clientCredentials>
	              <sharedSecret issuerName="{DEFAULT_ISSUER_HERE}"
	                            issuerSecret="{DEFAULT_KEY_HERE}" />
	            </clientCredentials>
	          </transportClientEndpointBehavior>
	          <serviceRegistrySettings
	                         discoveryMode="Public"
	                         displayName="RelayedMessagingDemo_BizTalkService" />
	        </behavior>
	      </endpointBehaviors>
	    </behaviors>
	  </system.serviceModel>
	</configuration>

Take a look back at the Service configuration above. Do you notice anything familiar? The configuration elements are virtually identitical. When this configuration is applied to a WCF Custom Request-Response Recieve Location, the endpoint (BizTalk Receive Location) is registered on the Service Bus when the BizTalk Host Instance hosting this Receive Location is started. Once the host instance is running and the endpoint is registered, you can execute the same Client.exe console application used above and pass messages through the cloud to the on-premise BizTalk Server.

In order to build robust, enterprise-level applications, the AppFabric Team has put together a set of best practices and a reference architecture that tackles some major issues such as handling transient faults and more complex authentication. I have listed some additional resources below and recommend them to anyone who is serious about building production applications that will utilize the AppFabric Service Bus.

The moral of the story is that the magic of the Relayed Messaging pattern is in the WCF Bindings. What happens when the client and the service are both unpredictable and may not be connected to the Service Bus at the same time? The answer to that question lies in the Brokered Messaging pattern which I will cover in the next post.

Download the full demo code here.

Additional Resources

Service Bus Bindings
http://msdn.microsoft.com/en-us/library/windowsazure/hh410102.aspx

Transient Fault Handing Framework
http://code.msdn.microsoft.com/Transient-Fault-Handling-b209151f

Hybrid Reference Implementation Using BizTalk Server, Windows Azure, Service Bus and SQL Azure
http://msdn.microsoft.com/en-us/windowsazure/hh547113(v=VS.103).aspx

This entry was posted in System Integration and tagged , , , , , , , , . Bookmark the permalink.