Previous posts covered how to make a custom WCF transport channel that used the file system as the transport mechanism. This post covers how to use Azure tables as the transport.
The full code is available here: https://github.com/dmetzgar/azure-table-transport
The genesis of this transport channel came about while I was trying to find a suitable way to communicate with individual Azure instances that did not require an open port and did not go through the load balancer. There are a number of approaches that could work but the technique of communicating through Azure tables was something another group in Microsoft was doing and had success with.
From the service side, here is the design:
- The service runs on an Azure role instance
- Deployment id, role name, and instance name are acquired from RoleEnvironment and concatenated to create a partition key for the Azure table
- The service polls the Azure table with the partition key looking for messages
- When a message is received, it is executed and the response is sent to another Azure table
The client works in a similar way where it writes a request message to an Azure table and polls another table for a response. The difference is that it has to be pointed to a particular Azure role instance.
Turning this into a WCF transport channel seems a piece of cake when modifying the existing custom transport channel code that uses the file system.
The code below is using Azure SDK 1.8. The first thing I created was a TableEntity to store the SOAP message. This works for both the request and response.
The URI for the service endpoint should serve as the name of the Azure table. Since I have two tables, I append Request and Reply to them. The ChannelListener seems a good place to create the tables if they do not already exist.
The lynchpin of the whole system is the code to poll for messages. As with the file transport channel, both the request and reply channel classes inherit from a common base class. This is where I've added the WaitForMessage method.
The partitionKey is stored as a member variable. You can see where it's applied as a filter condition when querying the table. The sleep value is passed in to the method. You could make this configurable if you want.
Writing a message to a table is the same for both request and reply.
The code here uses the encoder (which is set to the text encoder) to write the message to a buffer created by the buffer manager. Then it's encoded as a UTF8 string. If you were to open the table in a tool like Azure Storage Explorer you would see the text SOAP message. This can help for debugging purposes.
Reading the message is split into two methods. This is because there are two code paths for getting the message. In the reply channel, WaitForRequest only returns a Boolean indicating whether or not a request was received within the timeout. The other path is TryReceiveRequest in the reply channel and the Request method in the request channel that waits for the reply message. This path will use the message read from the WaitForMessage method instead of reading again from the table.
Notice also that the table entity is deleted in the finally. This is to keep us from reading the same messages over and over. You could modify this to update a flag indicating the message was read so you could keep an archive of the messages.
The rest of the code is pretty similar to the file transport channel. One improvement is that there are several properties added to the BindingConfigurationElement that are used in the app.config. That code is fairly straightforward as well.
To test this you should have the Azure SDK 1.8 installed. Start up the Storage Emulator first. Then set multiple startup projects on the solution and start the service above the client. It should work the exact same way as the file transport channel did in previous posts.