How To Share a Redis Cache Across Multi-Region Azure API Management Instances
Published Feb 05 2022 03:10 PM 2,506 Views
Microsoft

Introduction

Among the many great features Azure API Management (APIM) provides, response and value caching are some of the most advantageous approaches to alleviate load on your backends and provide reduced round-trip times (latency) for your API consumers. Depending on the nature of your implementation, gains can be quite positively impactful.

 

APIM provides two types of caching - internal and external. The former is caching maintained in each APIM instance; the latter is implemented through Azure Cache for Redis. Both offerings have their respective advantages and disadvantages, and the best way to gain an understanding of which may be most suitable for you is to read up on both and understand how each APIM tier you employ may utilize cache

 

Our Use Case

While this blog post can be just as applicable to a single-region APIM installation, I want to focus on what a multi-region approach looks like. Therefore, suppose you have a distributed APIM setup in two or more regions. We will focus on the region-paired Central US and East US 2. For ease of example, both of these APIM instances source data from an external, 3rd party API, but any other backend implementation is adaptable. It is very probable that, as part of your fault-tolerant, region-based design, you have redundant backend APIs, perhaps replicated data stores in your region, and so on. That's very applicable, but we can focus on a simpler scenario that conveys the focus of this post.

 

As alluded to in our introduction, our objective is to provide a fast, robust caching experience to alleviate backend stress and reduce latency to our caller. We will implement an external Redis cache that will be used in a shared manner to benefit our APIM instances. Do note that I am not immediately focused on redundancy of the Redis implementation as I want to keep focus primarily on the APIM / Redis interaction. 

 

Setup

Resource Groups

  1. Create two new resource groups - one in Central US and the other in East US 2
    For example, use rg-apim-redis-demo-central-us and rg-apim-redis-demo-east-us2 as your resource group names.

    SimonKurtzMSFT_0-1643907371097.png

     

  2. Press Review + Create, followed by Create.

API Management

  1. Create two new API Management instances - one in each of the Central US and East US 2 resource groups.
    As APIM service names must be universally unique, consider using a combination of your initials and today's date (e.g. apim-sk-02032022-central-us and apim-sk-02032022-east-us2). 
    Note: Please ensure that you create the APIM instance in the correct resource group and region!

    SimonKurtzMSFT_0-1643914610976.png

  2. Press Review + Create, followed by Create.
    Note: While there are many APIM configuration options (and ones you should consider), we are keeping the scope of this exercise very focused.
  3. The deployment will take 45-75 minutes, and you will get an email as well as Azure Portal notification upon completion.

    SimonKurtzMSFT_0-1643923738468.png

     

Create Azure Cache for Redis

  1. Reference the Create Azure Cache for Redis steps and create a Basic C0 cache (version 4 or 6) in either the Central US or East US 2 resource group. Give it a unique name such as redis-sk-02032022-central-us.

    SimonKurtzMSFT_0-1643921668670.png

  2. Press Review + Create, followed by Create.
  3. The deployment will take a while, but you should eventually see the newly-added Redis instance in whichever of the two regions you used. For example, I created mine in Central US.

    SimonKurtzMSFT_1-1643923886814.png

     

Integrate Azure Cache for Redis with API Management

  1. For each of the two APIM instances, reference the Add An External Cache instructions.
    Note: It is very important to use Default as the Use from value. This value enables APIM regions from different regions to use the same cache - the very purpose of this exercise!

    SimonKurtzMSFT_2-1643924091159.png
  2. Both APIM instances will have an Redis cache entry for Default. Here's my East US 2 setup:

    SimonKurtzMSFT_3-1643924320415.png

     

Set Up Backend / External API

We will use the Color API that we also use in our APIM Hands-On Lab. Repeat these steps for each of the two APIM instances.

  1. Open the APIs blade and create an API from the OpenAPI definition.SimonKurtzMSFT_0-1643924491375.png
  2. Use https://markcolorapi.azurewebsites.net/swagger/v1/swagger.json, set API URL suffix to color, then press Create.
    SimonKurtzMSFT_1-1643924749349.png
  3. Open the Color API Settings tab and uncheck the Subscription required checkbox. While API security is important and advised, for ease of use, we will make this an openly accessible API.
    SimonKurtzMSFT_3-1643925135621.png

     

  4. Open the Code Editor Inbound processing section in the ApiRandomColorGet operation.
    SimonKurtzMSFT_2-1643924866122.png

     

  5. Replace everything with the following policies.
    This implementation has a bit of extra detail in it such as a time stamp and the region where the entry was cached to make it more explicit in our subsequent testing. I also added a custom header to indicate cache hit or miss. Please change as you see fit.
    <policies>
        <inbound>
            <base />
            <!-- 1) Check whether we have a cache entry in our Redis cache (external).-->
            <cache-lookup-value key="randomcolor" variable-name="cachedrandomcolor" caching-type="external" />
            <choose>
                <!-- 2) If we had a cache hit, we simply return that value. Otherwise, we automatically move on to a call to the backend API.-->
                <when condition="@(context.Variables.ContainsKey("cachedrandomcolor"))">
                    <return-response>
                        <set-header name="x-cache-status" exists-action="override">
                            <value>hit</value>
                        </set-header>
                        <set-body>@((string)context.Variables["cachedrandomcolor"])</set-body>
                    </return-response>
                </when>
            </choose>
        </inbound>
        <backend>
            <base />
        </backend>
        <outbound>
            <base />
            <!-- 1) Assemble the return value from the API response, the current managed APIM gateway regionm, and a time stamp.-->
            <set-variable name="returnValue" value="@{
                return $"\nAPI response: {context.Response.Body.As<string>()}\nAPIM region: {context.Deployment.Region}\nTimestamp: {DateTime.Now}\n\n";
            }" />
            <!-- 2) Store the value to be returned in the Redis cache (external) for 30 seconds. This is important for our lookup on subsequent requests.-->
            <cache-store-value key="randomcolor" duration="30" caching-type="external" value="@((string)context.Variables["returnValue"])" />
            <!-- 3) Return the same value to the APIM caller that we just stored in cache. That makes it indistinguishable where the value came from.-->
            <return-response>
                <set-header name="x-cache-status" exists-action="override">
                    <value>miss</value>
                </set-header>
                <set-body>@((string)context.Variables["returnValue"])</set-body>
            </return-response>
        </outbound>
        <on-error>
            <base />
        </on-error>
    </policies>​


This concludes our APIM / API / Redis setup.

 

Solution Validation 

To validate the shared cache, we will be calling both APIM instances in short succession. From the policies above, you can see that we cache the entry for 30 seconds.

  1. Obtain the Request URL from the Test tab of each APIM instance.
    SimonKurtzMSFT_0-1643925615021.png

     

  2. Open a command window to issue curl commands. We use -v to get verbose details to see the header. My two curl URLs are as follows. Issue the calls within 30 seconds of each other
    curl -v https://apim-redis-demo-central-us.azure-api.net/color/api/RandomColor
    curl -v https://apim-redis-demo-east-us2.azure-api.net/color/api/RandomColor​
  3.  Evaluate the results. I am calling both APIM instances within 30 seconds and am observing that the second request was served from cache as the region and timestamp match.

    SimonKurtzMSFT_1-1643926180679.png
  4. My next test calls East US 2 first, then Central US. Note how I obtained a new value from the backend API and am caching based on the call from East US 2. Recall that the Redis cache was provisioned in Central US.
    I am dropping the verbose (-v) option to focus more narrowly on the results in this test.
    SimonKurtzMSFT_2-1643926411972.png

     

Conclusion

I demonstrated how two APIM instances can successfully use a shared Redis cache. There are optimizations to be made to resiliency of the Redis cache, etc., but I hope I was able to convey the principle of the setup successfully. 

 

Please reach out and comment if you have any questions. Thanks so much!

 

Co-Authors
Version history
Last update:
‎Feb 03 2022 02:15 PM
Updated by: