Occasionally Connected Clients – Queuing Messages
This blog includes:
- Why we are doing this?
- Getting started
- Setup on Web Portal
- ServiceName, IssuerName, IssuerSecret
- Install c:\Program Files\Windows Azure platform AppFabric SDK
- Visual Studio 2010
- Source Code
- Setup on Web Portal
- Big picture
- Sample code run
- The Code
Source Code
https://brunoblogfiles.com/zips/WeatherBillboard.zip
Why Are We Doing This?
One of the difficult problems to solve for developers is dealing with client computers that are not always connected. Although connectivity and reliability continues to improve, there are many scenarios where we want a “buffer in the cloud.”
These buffers waits in the cloud for the client to connect. At that point, when a client connects, message can finally be read by the client. These message can expire and don’t necessarily need to stick around forever. Afterall, some message lose their relevance with the passage of time, like weather, for example.
There is a caveat, however – Fault Tolerance
Because message buffer contents are stored in active memory, there are no strong fault tolerance or reliability guarantees. If the server hosting a message buffer crashes, you may lose messages. In the event of a server crash, you will not necessarily lose the buffer itself: knowledge of the buffer, including policy settings, is distributed across multiple servers and may be recovered. However, any messages in your buffer at the time of the crash will be lost. Therefore, if you are designing an application that needs a high degree of message reliability, it is recommended that you provide for message redundancy and recovery through other means.
Differences from WCF
It is important to realize that a Service Bus message buffer application is not designed like a WCF application: there is no host, or explicitly defined channel, or strong service/client paradigm. Instead, an application simply calls CreateMessageBuffer on an endpoint, and then either sends messages to it, or pulls messages from it. In addition, as mentioned above, the message buffer can be managed and accessed by non-Windows applications through REST only.
Getting Started
In order to work with the code and a live example, there are a few things you need to do:
- Sign up and get the info you need (ServiceName, IssuerName, IssuerSecret)
- Install the “Windows Azure platform AppFabric SDK”
- Currently here: https://www.microsoft.com/downloads/details.aspx?FamilyID=39856a03-1490-4283-908f-c8bf0bfad8a5&displaylang=en
- Else, look it up.
- I used Visual Studio 2010 Beta 2. It will be easier if you download it. Right now it is free.
- Source code can be downloaded here:
Big Picture
To make things more concrete, envision this scenario. Consider the weather billboards below. The weather billboards are occasionally disconnected and therefore need a way to have message waiting in a buffer in the AppFabric Service Bus.
In the diagram below the Weather Billboard User is interested in securely reading waiting messages in the cloud. The user might be on a wireless network, going through a router and firewall.
Figure 1 : Example of Listeners – the Weather Billboard
Let’s take a look at a sample implementation for the Weather Billboards. Another way to think about it is who is the “publisher” and who is the “consumer?” So the point of this blog is to walk you through the mechanics of doing this.
I want to demonstrate how to use the Windows Azure platform AppFabric Service Bus and its message buffer feature.
The Weather Billboard application creates the message buffer and then retrieves messages from it that are sent by the Weather Station. Creating a message buffer requires the credentials for the endpoint, the message buffer URI, and the message buffer policy. The Weather Billboard creates these prerequisite objects.
Sample code run
Step 1 – Run the WeatherStation
-
- Run the WeatherStation.exe application
- Click on the top button to connect
- Type in some weather conditions to send out
- Click “Send Weather Conditions”
You can send more than one. They get queued up.
Step 2 – Close Weather Station
- Close the WeatherStation.exe application
- At this point both the producer and consumer are both disconnected
Step 3 – Start Weather Billboard
- Click on the top button to connect
- Click “Read Weather Conditions”
- You can read more than one. They have been queued up
The code – Source Available Above
Note the two projects here that have both a Service Bus and User Interface code component. CredentialInfo.cs holds all the Shared Secret keys that you got from the web portal.
Code PrintOut – WeatherBillboard::CredentialInfo.cs
You must now go to the web portal. This is the screen you are looking for:
Go to the web portal for:
serviceNamespace
IssuerName
IssuerSecret
Note: The key line is line 10, where you need to choose the right service name. Lines 11 and 12 also need to be filled out using the information in the immediate screen above.
Code Snippet
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace WeatherBillboard
- {
- class CredentialInfo
- {
- private string serviceNamespace = "WeatherCentralService";
- private string issuerName = "owner";
- private string issuerSecret = "";
- public string ServiceNamespace
- {
- get { return serviceNamespace; }
- set { serviceNamespace = value; }
- }
- public string IssuerName
- {
- get { return issuerName; }
- set { issuerName = value; }
- }
- public string IssuerSecret
- {
- get { return issuerSecret; }
- set { issuerSecret = value; }
- }
- }
- }
Code PrintOut – WeatherBillboard::MessageBuffer.cs
Code Snippet
- using System;
- using System.Text;
- using Microsoft.ServiceBus;
- using System.ServiceModel.Channels;
- namespace WeatherBillboard
- {
- public class MyMessageBuffer : IDisposable
- {
- private MessageBufferClient client;
- private MessageBufferPolicy policy;
- private TransportClientEndpointBehavior credential;
- private Uri uri;
- private CredentialInfo credentials = new CredentialInfo();
- private bool _disposed;
- public bool SetupSecurity()
- {
- this.policy = new MessageBufferPolicy();
- this.policy.Authorization = AuthorizationPolicy.Required;
- this.policy.MaxMessageCount = 10;
- this.policy.ExpiresAfter = TimeSpan.FromMinutes(5); // messages in the message buffer expire after 5 mins
- this.policy.TransportProtection = TransportProtectionPolicy.AllPaths;
- // create the credentials object for the endpoint
- this.credential = new TransportClientEndpointBehavior();
- this.credential.CredentialType = TransportClientCredentialType.SharedSecret;
- this.credential.Credentials.SharedSecret.IssuerName = credentials.IssuerName;
- this.credential.Credentials.SharedSecret.IssuerSecret = credentials.IssuerSecret;
- return true;
- }
- public void CreateServiceUri()
- {
- // create the URI for the message buffer
- this.uri = ServiceBusEnvironment.CreateServiceUri("https", credentials.ServiceNamespace, "MessageBuffer");
- }
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
- protected virtual void Dispose(bool disposing)
- {
- // If you need thread safety, use a lock around these
- // operations, as well as in your methods that use the resource.
- if (!_disposed)
- {
- if (disposing)
- {
- // Nothing here right now. All clean !
- }
- // Indicate that the instance has been disposed.
- //_resource = null;
- _disposed = true;
- }
- }
- public string GetNextMessage()
- {
- Message message;
- string content;
- // Retrieve a message (destructive read)
- message = client.Retrieve();
- content = message.GetBody<string>();
- message.Close();
- return content;
- }
- public void CreateMessageBuffer()
- {
- this.client = MessageBufferClient.CreateMessageBuffer(this.credential, this.uri, this.policy);
- }
- }
- }
Code PrintOut – WeatherBillboard::MainWindow.xaml
Code Snippet
- <Window x:Class="WeatherBillboard.MainWindow"
- xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
- Title="MainWindow" Height="350" Width="525">
- <Grid>
- <Button Content="Connect to Service Bus" Height="56" HorizontalAlignment="Left" Margin="41,27,0,0" Name="button1" VerticalAlignment="Top" Width="176" Click="button1_Click" />
- <Button Content="Read Weather Conditions" Height="56" HorizontalAlignment="Left" Margin="41,97,0,0" Name="button2" VerticalAlignment="Top" Width="176" Click="button2_Click" />
- <TextBlock Height="126" HorizontalAlignment="Left" Margin="42,168,0,0" Name="textBlock1" VerticalAlignment="Top" Width="440" />
- </Grid>
- </Window>
Code PrintOut – WeatherBillboard::MainWindow.xaml.cs
Code Snippet
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Data;
- using System.Windows.Documents;
- using System.Windows.Input;
- using System.Windows.Media;
- using System.Windows.Media.Imaging;
- using System.Windows.Navigation;
- using System.Windows.Shapes;
- using System.Drawing;
- using System.IO;
- using System.Net;
- namespace WeatherBillboard
- {
- public partial class MainWindow : Window
- {
- Window mainWindow = new Window();
- MyMessageBuffer message_buffer = new MyMessageBuffer();
- public MainWindow()
- {
- InitializeComponent();
- }
- private void button1_Click(object sender, RoutedEventArgs e)
- {
- message_buffer.SetupSecurity();
- message_buffer.CreateServiceUri();
- message_buffer.CreateMessageBuffer();
- this.Title = "Weather Billboard is connected...";
- }
- private void button2_Click(object sender, RoutedEventArgs e)
- {
- string message = message_buffer.GetNextMessage();
- this.textBlock1.FontSize = 32;
- this.textBlock1.Inlines.Clear();
- this.textBlock1.Inlines.Add("The Weather Station sent us ");
- this.textBlock1.Inlines.Add(new Bold(new Italic(new Run(message))));
- this.textBlock1.TextWrapping = TextWrapping.Wrap;
- }
- }
- }
Code PrintOut – WeatherStation::CredentialInfo.cs
Go to the web portal for:
- serviceNamespace
- IssuerName
- IssuerSecret
It is the same as WeatherBillboard::CredentialInfo.cs
Code Snippet
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace WeatherStation
- {
- class CredentialInfo
- {
- private string serviceNamespace = "WeatherCentralService";
- private string issuerName = "owner";
- private string issuerSecret = "";
- public string ServiceNamespace
- {
- get { return serviceNamespace; }
- set { serviceNamespace = value; }
- }
- public string IssuerName
- {
- get { return issuerName; }
- set { issuerName = value; }
- }
- public string IssuerSecret
- {
- get { return issuerSecret; }
- set { issuerSecret = value; }
- }
- }
- }
Code PrintOut – WeatherStation::MessageBuffer.cs
Code Snippet
- using System;
- using System.Text;
- using Microsoft.ServiceBus;
- using System.ServiceModel.Channels;
- namespace WeatherStation
- {
- public class MyMessageBuffer : IDisposable
- {
- private MessageBufferClient client;
- private MessageBufferPolicy policy;
- private TransportClientEndpointBehavior credential;
- private Uri uri;
- private CredentialInfo credentials = new CredentialInfo();
- private bool _disposed;
- public bool SetupSecurity()
- {
- this.policy = new MessageBufferPolicy();
- this.policy.Authorization = AuthorizationPolicy.Required;
- this.policy.MaxMessageCount = 10;
- this.policy.ExpiresAfter = TimeSpan.FromMinutes(5); // messages in the message buffer expire after 5 mins
- this.policy.TransportProtection = TransportProtectionPolicy.AllPaths;
- // create the credentials object for the endpoint
- this.credential = new TransportClientEndpointBehavior();
- this.credential.CredentialType = TransportClientCredentialType.SharedSecret;
- this.credential.Credentials.SharedSecret.IssuerName = credentials.IssuerName;
- this.credential.Credentials.SharedSecret.IssuerSecret = credentials.IssuerSecret;
- return true;
- }
- public void CreateServiceUri()
- {
- // create the URI for the message buffer
- this.uri = ServiceBusEnvironment.CreateServiceUri("https", credentials.ServiceNamespace, "MessageBuffer");
- }
- public void Dispose()
- {
- Dispose(true);
- // Use SupressFinalize in case a subclass
- // of this type implements a finalizer.
- GC.SuppressFinalize(this);
- }
- protected virtual void Dispose(bool disposing)
- {
- // If you need thread safety, use a lock around these
- // operations, as well as in your methods that use the resource.
- if (!_disposed)
- {
- if (disposing)
- {
- // Nothing coded for cleanup yet
- }
- // Indicate that the instance has been disposed.
- //_resource = null;
- _disposed = true;
- }
- }
- public string GetNextMessage()
- {
- Message message;
- string content;
- // Retrieve a message (destructive read)
- message = client.Retrieve();
- content = message.GetBody<string>();
- message.Close();
- return content;
- }
- public bool SendMessage(string message)
- {
- MessageVersion messageVersion = MessageVersion.Soap12WSAddressing10;
- string messageAction = "urn:Message";
- client.Send(Message.CreateMessage(messageVersion, messageAction, message));
- return true;
- }
- public void CreateMessageBuffer()
- {
- this.client = MessageBufferClient.CreateMessageBuffer(this.credential, this.uri, this.policy);
- }
- }
- }
Code PrintOut – WeatherStation::MainWindow.xaml
Code Snippet
- <Window x:Class="WeatherStation.MainWindow"
- xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
- Title="Weather Station" Height="350" Width="525">
- <Grid>
- <Button Content="Connect to Service Bus" Height="56" HorizontalAlignment="Left" Margin="41,27,0,0" Name="button1" VerticalAlignment="Top" Width="176" Click="button1_Click" />
- <Button Content="Send Weather Conditions" Height="56" HorizontalAlignment="Left" Margin="41,97,0,0" Name="button2" VerticalAlignment="Top" Width="176" Click="button2_Click" />
- <TextBox Height="72" HorizontalAlignment="Left" Margin="44,196,0,0" Name="textBox1" VerticalAlignment="Top" Width="277" TextWrapping="WrapWithOverflow" VerticalScrollBarVisibility="Auto" />
- </Grid>
- </Window>
Code PrintOut – WeatherStation::MainWindow.xaml.cs
Code Snippet
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Data;
- using System.Windows.Documents;
- using System.Windows.Input;
- using System.Windows.Media;
- using System.Windows.Media.Imaging;
- using System.Windows.Navigation;
- using System.Windows.Shapes;
- using System.ServiceModel.Channels;
- namespace WeatherStation
- {
- /// <summary>
- /// Interaction logic for MainWindow.xaml
- /// </summary>
- public partial class MainWindow : Window
- {
- MyMessageBuffer message_buffer = new MyMessageBuffer();
- public MainWindow()
- {
- InitializeComponent();
- }
- private void button1_Click(object sender, RoutedEventArgs e)
- {
- message_buffer.SetupSecurity();
- message_buffer.CreateServiceUri();
- message_buffer.CreateMessageBuffer();
- this.Title = "Weather Station just connected...";
- }
- private void button2_Click(object sender, RoutedEventArgs e)
- {
- message_buffer.SendMessage(this.textBox1.Text);
- this.Title = "Weather Station just sent " + this.textBox1.Text;
- }
- }
- }
References
This part is important. Make sure Microsoft.ServiceBus points to
C:\Program Files\Windows Azure platform AppFabric SDK\V1.0\Assemblies\Microsoft.ServiceBus.dll
Some information about reading from message buffers
When you read a message you can do it two ways:
Method 1 : A destructive read
- Automatically deletes the message from the buffer
Method 2 : Peek/Lock
- When you choose to peek/lock a message, a copy of the message is sent to the client. Careful about the lock, though.
- The message is held temporarily by the message buffer, but “locked” so that no one else can retrieve it.
*Note*
- During this time, you or other clients can still pull other messages from the buffer.
Automatic Lock Release if Timeout
- If the client does not explicitly instruct the message buffer to delete the locked message before the lock timeout expires, the message buffer releases the lock on the message
More on this later. I will add some more code commentary. But you have enough here to implement your own. In my next blog entry, I will get the Weather Station to read data from the National Weather Service.