Using Memcache to access a Windows Azure Dedicated Cache

A few weeks ago, Larry and I wrote a couple of post about how to set up co-located caching using Windows Azure Caching (Preview) and access it from Ruby and PHP: Windows Azure Caching (Preview) and Ruby Cloud Services and PHP, Memcache, and Windows Azure Caching. What we didn’t cover in those posts was how to set up a role as a dedicated cache and then access it using the Memcache protocol. (In the co-located scenario, you dedicated a portion of a role’s memory to caching and access from the same role.) So, in this post I’ll cover how to set up a Node.js worker role that serves as a dedicated cache and a PHP web role that accesses the cache using the php_memcache extension. To follow the steps below, you’ll need a Windows Azure account (you can sign up for the free trial) and a Windows Azure Storage account.

Edit (Jan. 3, 2013): The Windows Azure PowerShell cmdlets now make much of the configuration work detailed in this post unnecessary. Read this post for more up-to-date information: Using Memcache to access a Windows Azure Dedicated Cache - Take 2

Before I get into the details, there are a couple of things to note here:

  1. The steps in this tutorial assume that you are working on a Windows machine. The Windows Azure team is working on expanding Cloud Services development to other platforms.
  2. Windows Azure Caching (Preview) is not currently available in Windows Azure Web Sites.
  3. As you will see in the details below, all of the steps to enable this scenario are configuration steps. You can expect tooling support in the future that makes this work much easier.
  4. I have chosen to use a Node.js worker role as the dedicated cache simply because I wanted to. You could choose to use a worker role with any runtime of your choice. The same is true for the web role.

With those things in mind, here’s how to set things up:

1. Make sure you have the Windows Azure SDK 1.7 installed: download.

2. Install the Windows Azure PowerShell cmdlets: install. (For information about using the cmdlets, see How to use Windows Azure PowerShell.)

3. Create a new Windows Azure project with this command: New-AzureServiceProject <project name> . This will create a new directory (with the same name as <project name> ). From within that directory, add a PHP web role and a Node.js worker role with the following commands: Add-AzurePHPWebRole and Add-AzureNodeWorkerRole. By default, the role names will be WebRole1 and WorkerRole1. Optionally, you can enable remote desktop access to these roles by running this command: Enable-AzureServiceProjectRemoteDesktop.

4. Locate the CachingPreview folder in the .NET SDK. (This is typically found here: C:\Program Files\Microsoft SDKs\Windows Azure\.NET SDK\2012-06\ref. ) Copy that folder and paste it in the bin folder of your web role (<project name>/WebRole1/bin). Finally, rename the pasted folder WindowsAzure.Caching.MemcacheShim. (The shim will be installed in a start up task that is defined in the next step.)

5. Open the project’s ServiceDefinition.csdef file (in the <project name> folder) and locate the <WebRole> element. Add the highlight element (below) as a child to the <Startup> element. This defines the task that will install the Memcache shim upon role start up.

   <Task commandLine="setup_web.cmd &gt; log.txt" executionContext="elevated">
       <Variable name="EMULATED">
          < RoleInstanceValue xpath="/RoleEnvironment/Deployment/@emulated" />
       <Variable name="VERSION" value="5.3.13" />
       <Variable name="DATACENTER" value="" />
       <Variable name="RUNTIMEURL" value="" />
       <Variable name="MANIFESTURL" value="" />
   <Task commandLine="WindowsAzure.Caching.MemcacheShim\MemcacheShimInstaller.exe" executionContext="elevated" />
< /Startup>

6. Still in the <WebRole> element of the .csdef file, add the highlighted element (below) to the <Endpoints> element. This sets up an internal port for Memcache protocol communication. We’ll configure it to communicate with the dedicated cache later.

   < InputEndpoint name="Endpoint1" protocol="http" port="80" />
   <InternalEndpoint name="memcache_default" protocol="tcp">
< FixedPort port="11211" />
< /Endpoints>

7. Moving now to the <WorkerRole> element of the .csdef file, add the following element (below) as a child of the <WorkerRole> element. This will import the module that allows the worker role to serve as the dedicated cache.

<Import moduleName="Caching" />
< /Imports>

8. Also as a child of the <WorkerRole> element, add the element below. This sets up a resource for collecting logs and dumps.

< LocalStorage name="Microsoft.WindowsAzure.Plugins.Caching.FileStore" sizeInMB="1000" cleanOnRoleRecycle="false" />
< /LocalResources>

9. Now open the ServiceConfiguration.cscfg file (in the <project name> folder). Add the highlighted elements (below) to the <ServiceConfiguration> element for WorkerRole1. Note that you will also need to fill in your storage account name and key.

<Role name="WorkerRole1">
  < ConfigurationSettings>
    <Setting name="Microsoft.WindowsAzure.Plugins.Caching.NamedCaches" value="" />
<Setting name="Microsoft.WindowsAzure.Plugins.Caching.Loglevel" value="" />
<Setting name="Microsoft.WindowsAzure.Plugins.Caching.CacheSizePercentage" value="" />
<Setting name="Microsoft.WindowsAzure.Plugins.Caching.ConfigStoreConnectionString" value="DefaultEndpointsProtocol=https;AccountName=Your storage account name;AccountKey=Your storage account key" />
  <Instances count="1" />
< /Role>

In the next few steps, we’ll enable the memcache shim (on the PHP web role) to communicate with the dedicated cache on the worker role).

10. From the WebRole1 folder, delete the file. The shim will look for the Web.config file.

11. Open the Web.config file and delete the following element. If you plan to run your project in the Windows Azure Emulators, leave this element for now, but delete it before you publish your project to Windows Azure.

   <add key="EMULATED" value="true" />
< /appSettings>

12. Still in the Web.config file, add the following element as the first child of the <configuration> element:

<section name="dataCacheClients"
type="Microsoft.ApplicationServer.Caching.DataCacheClientsSection, Microsoft.ApplicationServer.Caching.Core"
allowDefinition="Everywhere" />
< /configSections>

13. Again in the Web.config file, add the following element as a child of the <configuration> element (just not the first child). Make sure that the value of the identifier element is the name of the worker role (WorkerRole1 is the default name).

<tracing sinkType="DiagnosticSink" traceLevel="Error" />
< dataCacheClient name="DefaultShimConfig" useLegacyProtocol="false">
< autoDiscover isEnabled="true" identifier="WorkerRole1" />
< /dataCacheClients>

14. Finally, you need to add the PHP memcache extension to the web role (it isn’t included by default). Do this by adding a php folder to the bin directory of your web role. In the php folder add a php.ini file with one line (this will be added to the role’s PHP configuration: extension=php_memcache.dll. Also, add an ext directory to the php folder and put the php_memcache.dll there (make sure it is the php 5.3, nts, VC9 version of the dll, which you can find here).

That’s all there is to the configuration. Smile Like I mentioned earlier, look for tooling soon that will make this work easier.

At this point, your application is ready for deployment, but we should add some PHP code that tests that the cache is working. To do this, I suggest adding a PHP file (cachetest.php) to the root of your web role with the following code:

 $memcache = new Memcache;
 $memcache->connect('localhost_WebRole1', 11211) or die ("Could not connect");
 $version = $memcache->getVersion();
 echo "Server's version: ".$version."<br/>\n";
 $tmp_object = new stdClass();
 $tmp_object->str_attr = 'test';
 $tmp_object->int_attr = 123;
 $memcache->set('key', $tmp_object, false, 10) or die ("Failed to save data at the server");
 echo "Store data in the cache (data will expire in 10 seconds)<br/>\n";
 $get_result = $memcache->get('key');
 echo "Data from the cache:<br/>\n";


Note the connection code (the server name is ‘localhost_<name of web role>’):

$memcache->connect('localhost_WebRole1', 11211) or die ("Could not connect");

After you deploy your project (Publish-AzureServiceProject), you should be able to browse to cachetest.php and see the output of the code above.

As always, we’d love feedback on this if you try it out.