Partager via


Using Service Bus Queues with WCF

In 2008 I posted a series of blog articles about how to use MSMQ, WCF and IIS together. I chose to use this architecture as it combined the scalability and resiliency benefits of durable asynchronous messaging, with the simplicity and power of the WCF programming model and IIS hosting model. Over the last year I’ve spent much of my time working with Windows Azure. While Windows Azure has long provided a durable queuing mechanism, there was no integration with WCF or IIS, meaning developers were responsible for writing code to poll the queue, read and dispatch the messages.

Thankfully this has changed with the September 2011 release of Windows Azure AppFabric Service Bus . This release has significantly expanded on the capabilities of the previous release with support for Queues, Topics and Subscriptions plus the ability to integrate with WCF and IIS using the NetMessagingBinding.

In this post I’ll provide a simple example of how to use a Service Bus Queue to enable asynchronous messaging between a single client and a service. The full sample can be downloaded here. In a later post I’ll extend this sample to use Topics and Subscriptions to support a publisher-subscriber pattern.

Creating the Queue

To use the Service Bus, you first need to have a Windows Azure subscription. If you don’t yet have one, you can sign up for a free trial. Once you have a subscription, log into the Windows Azure Portal, navigate to Service Bus, and create a new Service Namespace. You can then create one or more queues directly from the portal, however in my sample I built a small library that lets you define your queues (and topics and subscriptions) in a configuration file so they can be created when needed by the application:

   <serviceBusSetup>
    <credentials namespace="{your namespace here}" issuer="owner" key="{your key here}" />
    <queues>
      <add name="samplequeue" />
    </queues>
  </serviceBusSetup>

Note that for any interactions with Service Bus, you’ll need to know your issuer name (“owner” by default) and secret key (a bunch of Base64 gumph), as well as your namespace, all which can be retrieved from the portal. For my sample, this info needs to go in a couple of places in each configuration file.

Defining the Contract

As with any WCF service, you need to start with the contract. Queuing technologies are inherently one-way, so you need to use the IsOneWay property on the OperationContract attribute. I chose to use a generic base interface that accepts any payload type, which can be refined for specific concrete payloads. However if you don’t want to do this, a simple single interface would work just fine.

     [ServiceContract]
    public interface IEventNotification<TLog>
    {
        [OperationContract(IsOneWay = true)]
        void OnEventOccurred(TLog value);
    }  
     [ServiceContract]
    public interface IAccountEventNotification : IEventNotification<AccountEventLog>
    {
    }

    [DataContract]
    public class AccountEventLog
    {
        [DataMember]
        public int AccountId { get; set; }

        [DataMember]
        public string EventType { get; set; }

        [DataMember]
        public DateTime Date { get; set; }
    }

Building and Hosting the Service

The service is implemented exactly the same way as any other WCF service. You could build your own host, but I choose to host the service in IIS via a normal .svc file and associated code-behind class file. For my sample, whenever I receive a message I write a trace message and also store the payload in a list in a static variable. (I also built a web page to view this list using my horrendous web development skills, but let’s not look at this in any detail Smile).

     public class Service1 : IAccountEventNotification 
    {
        public void OnEventOccurred(AccountEventLog log)
        {
            Trace.WriteLine(String.Format(
                 "Service One received event '{0}' for account {1}", log.EventType, log.AccountId));
            Subscriber.ReceivedEvents.Add(log);
        }
    }

The magic of wiring this service up to the Service Bus all happens in configuration. First, make sure you’ve downloaded and referenced the latest version of the Microsoft.ServiceBus.dll – NuGet is the easiest way to get this (just search for “WindowsAzure.ServiceBus”).

Now it’s just a matter of telling WCF about the service, specifying the NetMessagingBinding and correct URL, and configuring your authentication details. Since I haven’t got the SDK installed, the definitions for the bindings are specified directly in my web.config file instead of in machine.config.

   <system.serviceModel>
    <!-- These <extensions> will not be needed once our sdk is installed-->
    <extensions>
      <bindingElementExtensions>
        <add name="netMessagingTransport" type="Microsoft.ServiceBus.Messaging.Configuration.NetMessagingTransportExtensionElement, Microsoft.ServiceBus, Version=1.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
      </bindingElementExtensions>
      <bindingExtensions>
        <add name="netMessagingBinding" type="Microsoft.ServiceBus.Messaging.Configuration.NetMessagingBindingCollectionElement, Microsoft.ServiceBus, Version=1.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
      </bindingExtensions>
      <behaviorExtensions>
        <add name="transportClientEndpointBehavior" type="Microsoft.ServiceBus.Configuration.TransportClientEndpointBehaviorElement, Microsoft.ServiceBus, Version=1.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
      </behaviorExtensions>
    </extensions>
    <behaviors>
      <endpointBehaviors>
        <behavior name="securityBehavior">
          <transportClientEndpointBehavior>
            <tokenProvider>
              <sharedSecret issuerName="owner" issuerSecret="{your key here}" />
            </tokenProvider>
          </transportClientEndpointBehavior>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <bindings>
      <netMessagingBinding>
        <binding name="messagingBinding" closeTimeout="00:03:00" openTimeout="00:03:00" 
               receiveTimeout="00:03:00" sendTimeout="00:03:00" sessionIdleTimeout="00:01:00" 
               prefetchCount="-1">
          <transportSettings batchFlushInterval="00:00:01" />
        </binding>
      </netMessagingBinding>
    </bindings>
    <services>
      <service name="ServiceBusPubSub.ServiceOne.Service1">
        <endpoint name="Service1"  
                  address="sb://{your namespace here}.servicebus.windows.net/samplequeue" 
                  binding="netMessagingBinding" 
                  bindingConfiguration="messagingBinding" 
                  contract="ServiceBusPubSub.Contracts.IAccountEventNotification" 
                  behaviorConfiguration="securityBehavior" />
      </service>
    </services>
  </system.serviceModel>

One final (but critical) thing to note: Most IIS-hosted WCF services are automatically “woken up” whenever a message arrives. However this does not happen when working with the Service Bus—in fact it only starts listening to the queue after it’s already awake. During development (and with the attached sample) you can wake up the service by manually browsing to the .svc file. However for production use you’ll obviously need a more resilient solution. For applications hosted on Windows Server, the best solution is to use Windows Server AppFabric to host and warm up the service as documented in this article. If you’re hosting your service in Windows Azure, you’ll need to use a more creative solution to warm up the service, or you could host in a worker role instead of IIS. I’ll try to post more on possible solutions sometime in the near future.

Building the Client

Once again, building the client is just the same as for any other WCF application. I chose to use a ChannelFactory so I could reuse the contract assembly from the service, but any WCF proxy approach should work fine. An abridged version of the code is shown below.

     var factory = new ChannelFactory<IAccountEventNotification>("Subscriber");
    var clientChannel = factory.CreateChannel();
    ((IChannel)clientChannel).Open();
    clientChannel.OnEventOccurred(accountEventLog);
    ((IChannel)clientChannel).Close();
    factory.Close();

Again, the interesting part is the configuration, although it matches the service pretty closely:

   <system.serviceModel>
    <extensions>
      <bindingElementExtensions>
        <add name="netMessagingTransport" type="Microsoft.ServiceBus.Messaging.Configuration.NetMessagingTransportExtensionElement, Microsoft.ServiceBus, Version=1.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
      </bindingElementExtensions>
      <bindingExtensions>
        <add name="netMessagingBinding" type="Microsoft.ServiceBus.Messaging.Configuration.NetMessagingBindingCollectionElement, Microsoft.ServiceBus, Version=1.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
      </bindingExtensions>
      <behaviorExtensions>
        <add name="transportClientEndpointBehavior" type="Microsoft.ServiceBus.Configuration.TransportClientEndpointBehaviorElement, Microsoft.ServiceBus, Version=1.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
      </behaviorExtensions>
    </extensions>
    <behaviors>
      <endpointBehaviors>
        <behavior name="securityBehavior">
          <transportClientEndpointBehavior>
            <tokenProvider>
              <sharedSecret issuerName="owner" issuerSecret="{your key here}"/>
            </tokenProvider>
          </transportClientEndpointBehavior>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <bindings>
      <netMessagingBinding>
        <binding name="messagingBinding" sendTimeout="00:03:00" receiveTimeout="00:03:00" 
               openTimeout="00:03:00"  closeTimeout="00:03:00" sessionIdleTimeout="00:01:00" prefetchCount="-1">
          <transportSettings batchFlushInterval="00:00:01" />
        </binding>
      </netMessagingBinding>
    </bindings>
    <client>
      <endpoint name="Subscriber" 
                   address="sb://{your namespace here}.servicebus.windows.net/samplequeue" 
                   binding="netMessagingBinding" 
                   bindingConfiguration="messagingBinding" 
                   contract="ServiceBusPubSub.Contracts.IAccountEventNotification" 
                   behaviorConfiguration="securityBehavior" />
    </client>
  </system.serviceModel>

Summary

That should be it! With just some minor changes to your WCF configuration, some code (or manual processes) to create a queue, and a bit of work around activation, you can get a client and service happily communicating via a Windows Azure AppFabric Service Bus Queue.

In the next post, we’ll take this one step further by integrating Topics and Subscriptions to allow multiple subscribers to process the same (or even different) messages from the publisher.

ServiceBusQueues.zip

Comments

  • Anonymous
    October 08, 2011
    Great post will check out the code.

  • Anonymous
    October 19, 2011
    Great post Tom; this is what exactly I was looking for. I have multiple application hosted in cloud (clients) talking to a on-premise WCF service via service bus. The reason I thought of using service bus queues was to improve the performance as these clients make multiple calls to my service. Do we have any size limitation on the service bus queue?