The service bus supports a WCF-friendly programming model by
offering a set of dedicated bindings and behaviors. By and large, except
for a few slight twists to the programming model, working with the relay
service is no different from any other WCF service.The service bus supports the core WCF features of reliable
messaging, Message security and Transport security. Presently, it does not
support propagation of transactions from the client to the service across
the relay service.
The service bus features are available in the Microsoft.ServiceBus.dll assembly, mostly under
the Microsoft.ServiceBus
namespace.
Note: The service bus requires a separate download of the Windows Azure
SDK, and will work on machines running .NET 3.5 and .NET 4.0.
1. Relay Service Address
After opening an account with the Windows Azure AppFabric
Service Bus, you need to use the Windows Azure AppFabric Service Bus
administration website to create a service namespace (see Figure 1) and to select a connection
payment plan.
Note: It may be obvious, yet it is best to state that the service bus
use is not free and, at the time of this writing, users (both clients
and services) pay based on the maximum number of active concurrent
connections per unit of time they maintain against the service
bus.
A service namespace is the equivalent of a machine or domain name
in regular network addressing, and any available (not already taken)
URI-compliant string will do.
Like any other WCF service, every relayed service must have a
unique address. The format of the address is always a base address
followed by any number of optional URIs:
[base address]/[optional URI]/.../[optional URI]
The format of the base address is always the transport scheme
followed by the service namespace and then the service bus
address:
[scheme]://[service namespace].[service bus address]
For now, the service bus address is always:
servicebus.windows.net
The format of the scheme is either sb, http,
or https. Example 1 shows a few possible
addresses.
Example 1. Possible service bus addresses
sb://MyNamespace.servicebus.windows.net/ sb://MyNamespace.servicebus.windows.net/MyService sb://MyNamespace.servicebus.windows.net/MyService/MyEndpoint sb://MyNamespace.servicebus.windows.net/AnythingYouLike
http://MyNamespace.servicebus.windows.net/ http://MyNamespace.servicebus.windows.net/MyService
https://MyNamespace.servicebus.windows.net/MyService/MyEndpoint https://MyNamespace.servicebus.windows.net/AnythingYouLike
|
Once your service connects to the relay service and starts
listening on its address, no other service can listen on any other URI
scoped under your service URI. Put differently, two distinct services
must have two addresses so that no address is the prefix of the other.
For example, here are two invalid addresses when used
concurrently:
sb://MyNamespace.servicebus.windows.net/
sb://MyNamespace.servicebus.windows.net/MyService
The address of the service bus is available via the ServiceBusEnvironment static class:
public static class ServiceBusEnvironment
{
public static Uri CreateServiceUri(string scheme,string serviceNamespace,
string servicePath);
public static string DefaultIdentityHostName
{get;}
//More members
}
string serviceBusAddress = ServiceBusEnvironment.DefaultIdentityHostName;
Debug.Assert(serviceBusAddress == "servicebus.windows.net");
1.1. Extracting the service namespace
When working with the service bus and a config file, you
should avoid hard-coding the service namespace either on the client or
the service. Instead, you can extract the service namespace from the
config file using my ServiceBusHelper static helper class, shown
in Example 2.
Example 2. Extracting the service namespace from the config file
public static class ServiceBusHelper { public static string ExtractNamespace(Uri address) { return address.Host.Split('.')[0]; }
public static string ExtractNamespace(string endpointName) { Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); ServiceModelSectionGroup sectionGroup = ServiceModelSectionGroup.GetSectionGroup(config);
foreach(ChannelEndpointElement endpointElement in sectionGroup.Client.Endpoints) { if(endpointElement.Name == endpointName) { return ExtractNamespace(endpointElement.Address); } } return null; } public static string ExtractNamespace(Type serviceType) { Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); ServiceModelSectionGroup sectionGroup = ServiceModelSectionGroup.GetSectionGroup(config);
foreach(ServiceElement serviceElement in sectionGroup.Services.Services) { if(serviceElement.Name == serviceType.ToString()) { return ExtractNamespace(serviceElement.Endpoints[0].Address); } } return null; } }
|
The client can provide the endpoint name to the ExtractNamespace()
method, and the method will iterate over the client endpoints, parsing
the service namespace out of the matching endpoint address. The host
can call the ExtractNamespace()
method while providing it the service type, in which case the method
will parse the service namespace out of the first endpoint the host
uses. You will see usage of these helper methods later in the
chapter.
2. The Service Bus Registry
The service bus offers an ATOM feed of listening services
on the service namespace base address or on any one of its sub-URIs. You
can view the feed by navigating to the service namespace (or the nested
URI) using a browser, as shown in Figure 2.
By default, your service will not be listed in the service bus
registry. You control the service registry publishing using the
ServiceRegistrySettings
endpoint behavior class, defined as:
public enum DiscoveryType
{
Public,
Private
}
public class ServiceRegistrySettings : IEndpointBehavior
{
public ServiceRegistrySettings();
public ServiceRegistrySettings(DiscoveryType discoveryType);
public DiscoveryType DiscoveryMode
{get;set;}
public string DisplayName
{get;set;}
}
The host needs to add that behavior programmatically to every
endpoint you wish to publish to the registry (there is no matching
configurable behavior). For example, use the following to publish all
endpoints to the registry:
IEndpointBehavior registryBehavior =
new ServiceRegistrySettings(DiscoveryType.Public);
ServiceHost host = new ServiceHost(typeof(MyService));
foreach(ServiceEndpoint endpoint in host.Description.Endpoints)
{
endpoint.Behaviors.Add(registryBehavior);
}
host.Open();
You can streamline this behavior with a dedicated host type, such
as my DiscoverableServiceHost:
public partial class DiscoverableServiceHost : ServiceHost,...
{
public DiscoverableServiceHost(Type serviceType,params Uri[] baseAddresses)
: base(serviceType,baseAddresses)
{}
//More constructors
protected override void OnOpening()
{
IEndpointBehavior registryBehavior = new
ServiceRegistrySettings(DiscoveryType.Public);
foreach(ServiceEndpoint endpoint in Description.Endpoints)
{
endpoint.Behaviors.Add(registryBehavior);
}
base.OnOpening();
}
}
3. The Service Bus ExplorerTo help visualize the service bus, I wrote the Service Bus
Explorer shown in Figure 3.
The tool accepts the service namespace to explore and after
logging into the feed will display the running services for you. All the
explorer does is parse the ATOM feed and place the items in the tree on
the left. You can explore multiple service namespaces and see your
service bus administration pages in the right pane. The tool also shows
the available buffers (discussed later in the chapter); this information
is very handy in administering them.