One major area where hosting RIA in Azure is significantly different from hosting in a regular web server is the use of the foundational piece of Azure platform itself - .NET Services Service Bus. Service Bus is an “intermediary in the cloud” allowing you to implement all kinds of patterns which were not possible before, magically connecting cloud applications, on-premise clients, web clients, devices and various combinations of all of the above, and doing it through variety of the protocols supported by WCF 3.5 SP1.
Unfortunately, this variety is unavailable to Silverlight whose Core CLR doesn’t support everything WCF 3.5 supports, for example custom TCP-based bindings (unavailable even in Silverlight 3). Now, since Silverlight application is served from a web server these mechanisms could be moved to the Azure’s web role where full .NET 3.5 may be available, right? Yes, but with one significant exception which pretty much renders most interesting communication architectures unusable – Azure is load-balanced and denies any control over the load balancer, which means you have no idea what web role instance requests from your Silverlight client will hit next and, more importantly,- you lose control over inbound communication with Silverlight clients. All in all – even simple multicasting (1:N communication) that is so effectively done in massive scale through Service Bus is unavailable for Silverlight clients hosted in Azure, let alone more advanced architectures.
Let’s see how we can work around this problem in the multicast example. Here’s the architecture we're going to implement:
Figure 1. Service Bus Azure Proxy for Silverlight (SBAPS)
Step 1. Configure Azure worker role for use with Service Bus.
Azure VMs don’t yet contain all binaries needed to work with other parts of Windows Azure Platform (WAP) such as Service Bus, therefore you might run into situation when the Azure solution works fine in your local development fabric since all the binaries are GAC’ed after installing .NET Services SDK but after deploying you will see roles not starting without any explanation – this is a sign that some of the references binaries are not only there but are not configured properly (remember, .NET Services SDK also puts its configuration into your local machine.config file). To encapsulate the deployment and the configuration of the Service Bus library in your worker role (web role is not suitable for hosting services), you need to set the referenced Microsoft.ServiceBus.dll assembly’s “Copy Local” property to “true” in Visual Studio and move all portions of your machine.config file related to the Service Bus to the App.config file in the worker role project.
Note: Due to a bug in Azure SDK July CTP, you need to set global environment variable AddAppConfigToBuildOutputs to “true” before starting Visual Studio in order for all the config file to be published along with the solution.
Also, note that we’ve added netEventRelayBinding definition that we need this configuration to support in the first place. This is the only binding we’re going to use to communicate with Service Bus in this sample.
We also need to add client and service configurations to the same serviceModel section:
<client>
<endpoint name="RelayEndpoint"
contract="psoWorkerRole.IMulticastContract"
binding="netEventRelayBinding"
bindingConfiguration="default"
address="http://AddressToBeReplacedInCode/" />
</client>
<services>
<service name="psoWorkerRole.MulticastService">
address="" />
</service>
</services>
Note: Contract and service will be defined in the subsequent steps. Endpoint addresses will be inserted at runtime as we’re going to be establishing the channel with these endpoints explicitly in the code.
In order to communicate between worker and web role, you’ll need to utilize reliable communication mechanism such as Azure Queue Storage. Add following section to both worker role’s App.config and web role’s Web.config:
<appSettings>
<add key="QueueStorageEndpoint" value="http://queue.core.windows.net/" />
<add key="AccountName" value="<INSERT ACCOUNT NAME>" />
<add key="AccountSharedKey" value="<INSERT SHARED KEY>" />
</appSettings>
Step 3. Implement Service Bus client in worker role.
Add following code to the main worker role class derived from RoleEntryPoint:
// Create queue from config - exactly the same as in web role
StorageAccountInfo accountInfo = StorageAccountInfo.GetDefaultQueueStorageAccountFromConfiguration();
QueueStorage queueStorage = QueueStorage.Create(accountInfo);
MessageQueue outboundQueue = queueStorage.GetQueue("out");
outboundQueue.CreateQueue();
programInstance = new PSOHost(ConnectivityMode.AutoDetect);
programInstance.Run();
while (true)
{
// Get message from the queue
outboundMessage = outboundQueue.GetMessage();
if (outboundMessage != null)
programInstance.BroadcastText(outboundMessage.ContentAsString());
outboundQueue.DeleteMessage(outboundMessage);
}
else
Thread.Sleep(5000); // Adjust this sleep interval
We are creating only outbound queue here which will be used by web roles to send messages to other recipients through Service Bus. Then we start polling this queue for messages and immediately broadcast them to the Service Bus:
// Broadcast the text through ServiceBus
public void BroadcastText(string text)
channel.Say(null, text);
Here, Say is one of the operations on the multicast contract we associate with our Service Bus channel:
namespace psoWorkerRole
using System;
using System.ServiceModel;
[ServiceContract(Name = "IMulticastContract", Namespace = "http://pso")]
public interface IMulticastContract
[OperationContract(IsOneWay=true)]
void Enter(string nickName);
[OperationContract(IsOneWay = true)]
void Say(string nickName, string text);
void Exit(string nickName);
public interface IMulticastChannel : IMulticastContract, IClientChannel { }
And finally, Say operation, which is called as soon as the Service Bus delivers the message – in this case to the same worker role instance, – invokes DumpMessage, which propagates the message to the inbound queues so they could be picked up by the web roles:
void DumpMessage(string text)
RoleManager.WriteToLog("Information", text);
Console.WriteLine(text);
foreach (MessageQueue inboundQueue in inboundQueues)
inboundQueue.PutMessage(new Message(text));
For more information on how to implement multicasting pattern with Service Bus, please refer to MulticastSample sample in .NET Service SDK.
Step 4. Implement duplex communication in web ole and Silverligh client.
Silverlight 3 provides an out of the box mechanism for bi-directional communication with the server. In fact, the only server-side assembly in Silverlight SDK is System.ServiceModel.PollingDuplex.dll. We’re going to utilize it to talk to Silverlight clients. Reference this assembly in web role.
To close the loop and communicate with worker role we need to add operations with Azure queues:
Timer timer;
StorageAccountInfo accountInfo;
QueueStorage queueStorage;
MessageQueue outboundQueue;
MessageQueue inboundQueue;
public Communicator() : base()
// create queue from config
accountInfo = StorageAccountInfo.GetDefaultQueueStorageAccountFromConfiguration();
queueStorage = QueueStorage.Create(accountInfo);
outboundQueue = queueStorage.GetQueue("out");
inboundQueue = queueStorage.GetQueue("in" + HttpContext.Current.Application["roleId"]);
inboundQueue.CreateQueue();
//Set up a message update every few seconds
this.timer = new Timer(new TimerCallback(PollInboundMessages),
null, 0, 10000);
Note: We’re pulling the name of the inbound queue for each web role from the ASP.NET session. We create the inbound queue with unique name each time web role starts as we have to distinguish the queues somehow so the messages delivered from the worker role to them don’t get picked up by some other web role running in the application. Worker role doesn’t need to know the names – it just delivers the messages to all queues which name starts with "in".
void PollInboundMessages(object o)
Microsoft.Samples.ServiceHosting.StorageClient.Message inboundMessage = inboundQueue.GetMessage();
if (inboundMessage != null)
TextChatMessageFromServer duplexMessage = new TextChatMessageFromServer();
duplexMessage.acceptsPublicMessages = true;
duplexMessage.text = inboundMessage.ContentAsString();
RoleManager.WriteToLog("Information", "Received from inbound queue: " + duplexMessage.text);
// Propagate message to connected clients
PushToAllClients(duplexMessage);
inboundQueue.DeleteMessage(inboundMessage);
Here, PushToAllClients method delivers the message to the Silverlight clients connected to the web role through PollingDuplex protocol. The implementation of the duplex communicator pattern suitable for Azure can be taken from Silverlight Chat sample (http://code.msdn.microsoft.com/wcfazure/Wiki/View.aspx?title=SilverlightChat&referringTitle=Home).