How to use a WCF custom channel to implement client-side caching in a Windows Azure-hosted application
Introduction
Some months ago I created a custom WCF protocol channel to transparently inject client-side caching capabilities into an existing WCF-enabled application just changing the its configuration file. Since I posted my first post on this subject I received positive feedbacks on the client-side caching pattern, hence I decided to create a version of my component for Windows Azure. The new implementation is almost identical to the original version and introduces some minor changes. The most relevant addition is the ability to cache response messages in Windows Azure AppFabric Caching Service. The latter enables to easily create a cache in the cloud that can be used by Windows Azure roles to store reference and lookup data. Caching can dramatically increase performance by temporarily storing information from backend stores and sources. My component allows to boost the performance of existing WCF-enabled applications running in the Azure environment by injecting caching capabilities in the WCF channel stack that transparently exploit the functionality offered by Windows Azure AppFabric Caching to avoid redundant calls against remote WCF and ASMX services. The new version of the WCF custom channel provides the possibility to choose among three caching providers:
- A Memory Cache-based provider: this component internally uses an instance of the MemoryCache class contained in the .Net Framework 4.0.
- A Web Cache-based provider: this provider utilizes an instance of the Cache class supplied by ASP.NET to cache response messages.
- An AppFabric Caching provider: this caching provider leverages Windows Azure AppFabric Caching Service. To further improve the performance, it’s highly recommended the client application to use the Local Cache to store response messages in-process.
As already noted in the previous article, client-side caching and server-side caching are two powerful and complimentary techniques to improve the performance of WCF-enabled applications. Client-caching is particularly indicated for those applications, like a web site, that frequently invoke one or multiple back-end systems to retrieve reference and lookup data, that is data that is typically static, like the catalog of an online store and that changes quite infrequently. By using client-side caching you can avoid making redundant calls to retrieve the same data over time, especially when these calls take a significant amount of time to complete.
For more information on Windows Azure AppFabric, you can review the following articles:
- “Introducing the Windows Azure AppFabric Caching Service” article on the MSDN Magazine site.
- “Caching Service (Windows Azure AppFabric)” article on the MSDN site.
- “Windows Azure AppFabric Caching Service Released and notes on quotas” article on the AppFabric CAT site.
- “How to best leverage Windows Azure AppFabric Caching Service in a Web role to avoid most common issues” article on the AppFabric CAT site.
- "Windows Azure AppFabric Learning Series" samples and videos on the CodePlex site.
- “Azure AppFabric Caching Service” article on Neil Mackenzie’s blog.
Problem Statement
The problem statement that the cloud version of my component intends to solve can be formulated as follows:
- How can I implicitly cache response messages within a service or an application running in a Windows Azure role that invokes one or multiple underlying services using WCF and a Request-Response message exchange pattern without modifying the code of the application in question?
To solve this problem, I created a custom protocol channel that you can explicitly or implicitly use inside a CustomBinding when specifying client endpoints within the configuration file or by code using the WCF API.
Design Pattern
The design pattern implemented by my component can be described as follows: the caching channel allows to extend existing WCF-enabled cloud applications and services with client-caching capabilities without the need to explicitly change their code to exploit the functionality supplied by Windows Azure AppFabric Caching. To achieve this goal, I created a class library that contains a set of components to configure, instantiate and use this caching channel at runtime. My custom channel is configured to checks the presence of the response message in the cache and behaves accordingly:
- If the response message is in the cache, the custom channel immediately returns the response message from the cache without invoking the underlying service.
- Conversely, if the response message is not in the cache, the custom channel calls the underlying channel to invoke the back-end service and then caches the response message using the caching provider defined in the configuration file for the actual call.
You can exploit the capabilities of the caching channel in 2 distinct ways:
- You can configure the client application to use a CustomBinding. In this case, you have to specify the ClientCacheBindingElement as the topmost binding element in the binding configuration. This way, at runtime, the caching channel will be the first protocol channel to be called in the channel stack.
- Alternatively, you can use the ClientCacheEndpointBehavior to inject the ClientCacheBindingElement at the top of an existing binding, for example the BasicHttpBinding or the WsHttpBinding.
Scenarios
This section describes 2 scenarios where you can use the caching channel in a cloud application.
First Scenario
The following picture depicts the architecture of the first scenario that uses Windows Azure Connect to establish a protected connection between a web role in the cloud and local WCF service and uses Windows Azure AppFabric Caching provider to cache response messages in the local and distributed cache. The diagram below shows an ASP.NET application running in a Web Role that invokes a local, on-premises WCF service running in a corporate data center. In particular, when the Service.aspx page first loads, it populates a drop-down list with the name of writers from the Authors table of the notorious pubs database which runs on-premise in the corporate domain. The page offers the user the possibility to retrieve the list of books written by the author selected in the drop-down list by invoking the AuthorsWebService WCF service located in the organization’s network that retrieves data for from the Titles tables of the pubs database.
- Directly invoke the WCF service via Windows Azure Connect: the Service.aspx page always calls the AuthorsWebService to retrieve the list of titles for the current author using Windows Azure Connect (see below for more information).
- Use Caching API Explicitly and via Windows Azure Connect: the page explicitly uses the cache aside programming pattern and the Windows Azure AppFabric Caching API to retrieve the list of titles for the current author. When the user presses the Get Titles button, the page first checks whether data is in the cache: if yes it retrieves titles from the cache, otherwise it invokes the underlying WCF service, writes the data in the distributed cache, then returns the titles.
- Use Caching API via caching channel and Windows Azure Connect: this mode uses the caching channel to transparently retrieve data from the local or distributed cache . The use of the caching channel allows to transparently inject client-side caching capabilities into an existing WCF-enabled application without the need to change its code. In this case, it’s sufficient to change the configuration of the client endpoint in the web.config.
- Directly invoke the WCF service via Service Bus: the Service.aspx page always calls the AuthorsWebService to retrieve the list of titles for the current author via Windows Azure AppFabric Service Bus. In particular, the web role invokes a BasicHttpRelayBinding endpoint exposed in the cloud via relay service by the AuthorsWebService.
- Use Caching API via caching channel and Service Bus: this mode uses the Service Bus to invoke the AuthorsWebService and the caching channel to transparently retrieve data from the local or distributed cache . The ClientCacheEndpointBehavior replaces the original BasicHttpRelayBinding specified in the configuration file with a CustomBinding that contains the same binding elements and injects the ClientCacheBindingElement at the top of the binding. This way, at runtime, the caching channel will be the first channel to be invoked in the channel stack.
Let’s analyze what happens when the user selects the third option to use the caching channel with Windows Azure Connect.
Message Flow:
- The user chooses an author from the drop-down list, selects the Use Caching API via WCF channel call mode and finally presses the Get Titles button.
- This event triggers the execution of the GetTitles method that creates a WCF proxy using the UseWCFCachingChannel endpoint. This endpoint is configured in the web.config to use the CustomBinding. The ClientCacheBindingElement is defined as the topmost binding element in the binding configuration. This way, at runtime, the caching channel will be the first protocol channel to be called in the channel stack.
- The proxy transforms the .NET method call into a WCF message and delivers it to the underlying channel stack.
- The caching channel checks whether the response message in the local or distributed cache. If ASP.NET application is hosted by more than one web role instance, the response message may have been previously put in the distributed cache by another role instance. If the caching channel finds the response message for the actual call in the local or distributed cache, it immediately returns this message to the proxy object without invoking the back-end service.
- Conversely, if the response message is not in the cache, the custom channel calls the inner channel to invoke the back-end service. In this case, the request message goes all the way through the channel stack to the transport channel that invokes the AuthorsWebService.
- The AuthorsWebService uses the authorId parameter to retrieve a list of books from the Titles table in the pubs database.
- The service reads the titles for the current author from the pubs database.
- The service returns a response message to the ASP.NET application.
- The transport channel receives the stream of data and uses a message encoder to interpret the bytes and to produce a WCF Message object that can continue up the channel stack. At this point each protocol channel has a chance to work on the message. In particular, the caching channel stores the response message in the distributed cache using the AppFabricCaching provider.
- The caching channel returns the response WCF message to the proxy.
- The proxy transforms the WCF message into a response object.
- The ASP.NET application creates and returns a new page to the browser.
Second Scenario
The following picture depicts the architecture of the second scenario where the web role uses the Windows Azure AppFabric Service Bus to invoke the AuthorsWebService and Windows Azure AppFabric Caching provider to cache response messages in the local and distributed cache.
Let’s analyze what happens when the user selects the fifth option to use the caching channel with the Service Bus.
Message Flow:
- The user chooses an author from the drop-down list, selects the Use Caching API via WCF channel call mode and finally presses the Get Titles button.
- This event triggers the execution of the GetTitles method that creates a WCF proxy using the UseWCFCachingChannel endpoint. This endpoint is configured in the web.config to use the CustomBinding. The ClientCacheBindingElement is defined as the topmost binding element in the binding configuration. This way, at runtime, the caching channel will be the first protocol channel to be called in the channel stack.
- The proxy transforms the .NET method call into a WCF message and delivers it to the underlying channel stack.
- The caching channel checks whether the response message in the local or distributed cache. If ASP.NET application is hosted by more than one web role instance, the response message may have been previously put in the distributed cache by another role instance. If the caching channel finds the response message for the actual call in the local or distributed cache, it immediately returns this message to the proxy object without invoking the back-end service.
- Conversely, if the response message is not in the cache, the custom channel calls the inner channel to invoke the back-end service via the Service Bus. In this case, the request message goes all the way through the channel stack to the transport channel that invokes the relay service.
- The Service Bus relays the request message to the AuthorsWebService.
- The AuthorsWebService uses the authorId parameter to retrieve a list of books from the Titles table in the pubs database.
- The service reads the titles for the current author from the pubs database.
- The service returns a response message to the relay service.
- The relay service passes the response message to the ASP.NET application.
- The transport channel receives the stream of data and uses a message encoder to interpret the bytes and to produce a WCF Message object that can continue up the channel stack. At this point each protocol channel has a chance to work on the message. In particular, the caching channel stores the response message in the distributed cache using the AppFabricCaching provider.
- The caching channel returns the response WCF message to the proxy.
- The proxy transforms the WCF message into a response object.
- The ASP.NET application creates and returns a new page to the browser.
Windows Azure Connect
In order to establish an IPsec protected IPv6 connection between the Web Role running in the Windows Azure data center and the local WCF service running in the organization’s network, the solution exploits Windows Azure Connect that is main component of the networking functionality that will be offered under the Windows Azure Virtual Network name. Windows Azure Connect enables customers of the Windows Azure platform to easily build and deploy a new class of hybrid, distributed applications that span the cloud and on-premises environments. From a functionality standpoint, Windows Azure Connect provides a network-level bridge between applications and services running in the cloud and on-premises data centers. Windows Azure Connect makes it easier for an organization to migrate their existing applications to the cloud by enabling direct IP-based network connectivity with their existing on-premises infrastructure. For example, a company can build and deploy a hybrid solution where a Windows Azure application connects to an on-premises SQL Server database, a local file server or an LOB applications running the corporate network.
For more information on Windows Azure Connect, you can review the following resources:
- “Overview of Windows Azure Connect” article on the MSDN site.
- "Tutorial: Setting up Windows Azure Connect" article on the MSDN site.
- “Overview of Windows Azure Connect When Roles Are Joined to a Domain” article on the MSDN site.
- "Domain Joining Windows Azure roles" article on the Windows Azure Connect team blog.
Windows Azure AppFabric Service Bus
The Windows Server AppFabric Service Bus is an Internet-scale Service Bus that offers scalable and highly available connection points for application communication. This technology allows to create a new range of hybrid and distributed applications that span the cloud and corporate environments. The AppFabric Service Bus is designed to provide connectivity, queuing, and routing capabilities not only for the cloud applications but also for on-premises applications. The Service Bus and in particular Relay Services support the WCF programming model and provide a rich set of bindings to cover a complete spectrum of design patterns:
- One-way communications
- Publish/Subscribe messaging
- Peer-to-peer communications
- Multicast messaging
- Direct connections between clients and services
The Relay Service is a service residing in the cloud, whose job is to assist in the connectivity, relaying the client calls to the service. Both the client and service can indifferently reside on-premises or in the cloud.
For more information on the Service Bus, you can review the following resources:
- "AppFabric Service Bus" section on the MSDN site.
- “Getting Started with AppFabric Service Bus" section on the MSDN site.
- "Developing Applications that Use the AppFabric Service Bus" section on the MSDN site.
- “Service Bus Samples” on the CodePlex site.
- "Implementing Reliable Inter-Role Communication Using Windows Azure AppFabric Service Bus, Observer Pattern & Parallel LINQ" article on the AppFabric CAT site.
We are now ready to delve into the code.
Solution
The solution code has been implemented in C# using Visual Studio 2010 and the .NET Framework 4.0. The following picture shows the projects that comprise the WCFClientCachingChannel solution.
A brief description of the individual projects is indicated below:
- AppFabricCache: this caching provider implements the Get and Put methods to retrieve and store data items from\to Windows Azure AppFabric Caching.
- MemoryCache: this caching provider provides the Get and Put methods to retrieve and store items to a static in-process MemoryCache object.
- WebCache: this caching provider provides the Get and Put methods to retrieve and store items to a static in-process Web Cache object.
- ExtensionLibrary: this assembly contains the WCF extensions to configure, create and run the caching channel at runtime.
- Helpers: this library contains the helper components used by the WCF extensions objects to handle exceptions and trace messages.
- Pubs WS: this project contains the code for the AuthorsWebService.
- Pubs: this test project contains the definition and configuration for the Pubs web role.
- PubsWebRole: this project contains the code of the ASP.NET application running in Windows Azure.
As I mentioned in the introduction of this article, the 3 caching providers have been modified to be used in a cloud application. In particular, the AppFabricCache project has been modified to use the Windows Azure AppFabric Caching API in place of their on-premises counterpart. Windows Azure AppFabric uses the same cache client programming model as the on-premise solution of Windows Server AppFabric. However, the 2 API are not identical and there are relevant differences when developing a Windows Azure AppFabric Caching solution compared to developing an application that leverages Windows Server AppFabric Caching . For more information on this topic, you can review the following articles:
- “Differences Between Caching On-Premises and in the Cloud” article on the MSDN site.
- “API Reference (Windows Azure AppFabric Caching)” article on the MSDN site.
NOTE The Client API of Windows Server AppFabric Caching and Windows Azure AppFabric Caching have the same fully-qualified name. So, what happens when you install the Windows Azure AppFabric SDK on a development machine where Windows Server AppFabric Caching is installed? The setup process of Windows Server AppFabric installs the Cache Client API assemblies in the GAC whereas the Windows Azure AppFabric SDK copies the assemblies in the installation folder, but it doesn’t register them in the GAC. Therefore, if you create an Azure application on a development machine hosting both the on-premises and cloud version of the Cache Client API, even if you reference the Azure version in your web or worker role project, when you debug the application within the Windows Azure Compute Emulator, your role will load the on-premises version, that is, the wrong version of the Cache Client API. Fortunately, the 2 on-premises and cloud versions of the API have the same fully-qualified name but different version number, hence you can include the following snippet in the web.config configuration file of your role to refer the right version of the API.
|
Configuration
The following table shows the web.config configuration file of the PubsWebRole project.
|
Please find below a brief description of the main elements and sections of the configuration file:
-
Lines [4-10] define the config sections. For Windows Azure AppFabric Caching features to work, the configSections element must be the first element in the application configuration file. It must contain child elements that tell the runtime how to use the dataCacheClients element.
-
Lines [28-56] contain the dataCacheClients element that is used to configure the cache client. Child elements dataCacheClient define cache client configuration; in particular, the localCache element specifies the local cache settings.
-
Lines [148-175] contain the client section that defines a list of endpoints the test project uses to connect to the test service. In particular, I created 4 different endpoints to demonstrate how to configure the caching channel:
-
The first endpoint called **DirectCallNoCache** does not use the caching channel and always invokes the the underlying service directly using the **Windows Azure Connect** and the [BasicHttpBinding](https://msdn.microsoft.com/en-us/library/system.servicemodel.basichttpbinding.aspx).
-
The second endpoint called **UseWCFCachingChannel** uses the [CustomBinding](https://msdn.microsoft.com/en-us/library/system.servicemodel.channels.custombinding.aspx) as a recipe to create the channel stack at runtime. The custom binding is composed of 3 binding elements: the **clientCaching**, **textMessageEncoding** and **httpTransport**. As you can see at lines **\[181-203\]** , the **clientCaching** binding element allows to accurately configure the runtime behavior of the caching channel at a general level and on a per operation basis. Below I will explain in detail how to configure the **clientCaching** binding element.
-
The first endpoint called **UseSBWithoutCaching** does not use the caching channel and always invokes the the underlying service directly using the **Service Bus** and the [BasicHttpRelayBinding](https://msdn.microsoft.com/en-us/library/microsoft.servicebus.basichttprelaybinding.aspx).
-
The fourth endpoint called **UseSBWithCaching** adopts the **Service Bus** and the [BasicHttpRelayBinding](https://msdn.microsoft.com/en-us/library/microsoft.servicebus.basichttprelaybinding.aspx) to communicate with the underlying service. However, the endpoint is configured to use the **cachingBehavior** that at runtime replaces the original binding with a [CustomBinding](https://msdn.microsoft.com/en-us/library/system.servicemodel.channels.custombinding.aspx) made up of the same binding elements and adds the **clientCaching** binding element as the first element to the binding element collection. This technique is an alternative way to use and configure the caching channel.
- Lines [265-324] contain the extensions element which defines the cachingBehavior extension element and the clientCaching binding element extension element. Besides, this section configures the WCF extensions introduced and required by the Service Bus (see the NOTE box below for more information on this).
As you can easily notice, both the cachingBehavior and clientCaching components share the same configuration that is defined as follows:
cachingBehavior and clientCaching elements:
enabled property: gets or sets a value indicating whether the WCF caching channel is enabled. When the value is false, the caching channel always invokes the target service. This property can be overridden at the operation level. This allows to enable or disable caching on a per operation basis.
headerproperty: gets or sets a value indicating whether a custom header is added to the response to indicate the source of the WCF message (cache or service). This property can be overridden at the operation level.
timeoutproperty: gets or sets the default amount of time the object should reside in the cache before expiration. This property can be overridden at the operation level.
cacheType property: gets or sets the cache type used to store items. The component actually supports two caching providers: AppFabricCache and WebCache. This property can be overridden at the operation level.
maxBufferSizeproperty: gets or sets the maximum size in bytes for the buffers used by the caching channel. This property can be overridden at the operation level.
indexes property: gets or sets a string containing a comma-separated list of indexes of parameters to be used to compute the cache key. This property is used only when the keyCreationMethod= Indexed.
keyCreationMethodproperty: gets or sets the method used to calculate the key for cache items. The component provides 5 key creation methods:
- Action: this method uses the value of the Actionheader of the request as key for the response. For obvious reasons, this method can be used only for operations without input parameters.
- MessageBody: this method uses the body of the request as key for the response. This method doesn’t work when the request message contains contains DateTimeelements that could vary from call to call.
- Simple: this method creates the string [A](P1)(P2)…(Pn) for an operation with n parameters P1-Pn and Action = A.
- Indexed: this method works as the Simple method, but it allows to specify which parameters to use when creating the key. For example, the Indexed method creates the string [A](P1)(P3)(5) for an operation with n parameters P1-Pn (n >= 5) and Action = A and when the value of the Indexesproperty is equal to “1, 3, 5”. This method can be used to skip DateTime parameters from the compute of the key.
- MD5: this method uses the MD5 algorithm to compute a hash from the body of the request message.
operation element:
- action property : gets or sets the WS-Addressing action of the request message.
- enabled property: gets or sets a value indicating whether the WCF caching channel is enabled for the current operation identified by the Actionproperty.
- headerproperty: gets or sets a value indicating whether a custom header is added to the response to indicate the source of the WCF message (cache or service) at the operation level.
- timeoutproperty: gets or sets the default amount of time the object should reside in the cache before expiration at the operation level.
- cacheType property: gets or sets the cache type used to store responses for the current operation. The component actually supports two caching providers: AppFabricCache and WebCache. This property can be overridden at the operation level.
- maxBufferSizeproperty: gets or sets the maximum size in bytes for the buffers used by the caching channel for the current operation.
- indexes property: gets or sets a string containing a comma-separated list of indexes of parameters to be used to compute the cache key for the current operation. This property is used only when the keyCreationMethod= Indexed.
- keyCreationMethod property: gets or sets the method used to calculate the key for cache items.
NOTE When you install the Windows Azure AppFabric SDK on your development machine, the setup process registers the Service Bus relay bindings, binding elements and behaviors as WCF extensions in the machine.config. However, these extensions are not installed by default in a Windows Azure VM when you deploy an Windows Azure application to the cloud. The documentation on MSDN suggests to perform the following steps when you develop a Windows Azure-Hosted application that uses the Service Bus to invoke a remote service:
However, the above steps are not sufficient. In order to use the relay bindings in a Web Role or a Worker Role running in the cloud, you need to make sure to properly register them as WCF extensions in the application configuration file or use a Startup Task to register them in the machine.config. In this case, make sure to add the following XML snippet to the configuration/system.serviceModel section of the configuration file.
|
Source Code
This section contains the code of the main classes of the solution. You can find the rest of the components in the source code that accompanies this article. The following table contains the source code for the Service.aspx page running in the web role.
|
The following table contains the source code for the Cache class in the AppFabricCache project. This class implements the Windows Azure AppFabric Caching caching provider used by the caching channel.
|
Performance Results
The following performance results have been collected under the following condition:
- The AuthorsWebService and the pubs database were running on my laptop in Milan, Italy.
- The Web Role and Windows Azure AppFabric Caching were running in the South Central US Azure Data Center. I intentionally deployed the Web Role in US and not in Western Europe to emphasize the distance between the cloud application and the remote service.
- The local cache was enabled in the cache client configuration.
Call Mode | Average Call Time (milliseconds) |
Directly invoke the WCF service via Windows Azure Connect | 355 |
Use Caching API Explicitly and via Windows Azure Connect | 2 |
Use Caching API via caching channel and Windows Azure Connect | 5 |
Directly invoke the WCF service via Service Bus | 415 |
Use Caching API via caching channel and Service Bus | 5 |
NOTE According to my tests, invoking a WCF service running on-premises in the organization's network from a Web Role using Windows Azure Connect and the BasicHttpBinding is slightly faster than invoking the same service via the Service Bus and the BasicHttpRelayBinding. Nevertheless, more accurate and exhaustive performance tests should be conducted to confirm this result. |
Conclusions
The caching channel shown in this article can be used to transparently inject client-side caching capabilities into an existing Windows Azure application that uses WCF to invoke one or multiple back-end services. The use of client-side caching techniques allows to dramatically increase performance and reduce the costs due to the use of Windows Azure Connect and Service Bus. The source code that accompanies the article can be downloaded here. As always, any feedback is more than welcome!
Additional Resources/References
For more information on the topic discussed in this blog post, please refer to the following:
- ”Windows Azure Service Bus CTP User Guide” whitepaper on the Service Bus Samples site.
- “Windows Azure Service Bus CTP FAQ” article in the MSDN library.
- ”Windows Azure AppFabric Caching Service Released and notes on quotas” post on the Windows Azure AppFabric Team blog.