Graduate Program KB

Azure Cache for Redis

  • Provides in-memory data store based on Redis software
  • Redis improves performance and scalability of an app using a backend data store heavily
    • Critical for low-latency and high-throughput
  • Key scenario patterns:
    • Data cache: Load data into cache, update cache if there are data changes
    • Content cache: In-memory cache to store static content such as website headers, footers and banners
    • Session store: Use cookie as a key to query data in a database, rather than overloading lots of information in user cookies
    • Job and message queuing: Add tasks to a queue
    • Distributed transactions: Batch commands to perform an operation, all must succeed otherwise roll back to initial state
  • Azure Cache for Redis service tiers:
    • Basic: OSS Redis cache running on a single VM, no SLA and used only for non-critical workloads
    • Standard: OSS Redis cache running on two VMs in a replicated configuration
    • Premium: High-performance OSS Redis cache deployed on more powerful VMs. Higher throughput, lower latency, better availability and more features
    • Enterprise: High-performance cache powered by Redis Labs' Redis Enterprise software. Supports Redis modules (RediSearch, RedisBloom, RedisTimeSeries). More availability than Premium tier
    • Enterprise Flash: Cost-effective large caches powered by same software as Enterprise tier. Extends data storage to nonvolatile memory on a VM (cheaper than DRAM, less per-GB memory cost)

Configuring Azure Cache for Redis

  • Use portal, CLI or PowerShell
  • Parameters:
    • Name: Globally unique name used to generate a public-facing URL for service communication
      • 1-63 characters
      • Numbers, letters and a hyphen
      • Can't start or end with a hyphen, consecutive hyphens aren't valid
    • Location: Same region as your application
    • Cache type: Service tier
    • Clustering support: Premium tiers and above can implement clustering to automatically split a dataset among multiple nodes
      • Max. shards is 10
      • Cost incurred is the cost of the original node multipled by number of shards
  • Redis has a CLI tool for interacting with the service as a client
    • Download
    • Clients need the hostd name, port and access key for the cache
    • Host name is the public URL mentioned previously, the access key acts as a password
    • A primary and secondary key are generated, either works. Good practice to periodically regenerate keys
  • Expire stale cached values by applying a TTL to a key, causing it to automatically delete
    • Set using seconds or milliseconds
    • Expire time resolution is always 1 millisecond
    • Information about expires are replicated and persisted on a disk (saves expiry date of key)

Interact with Azure Cache for Redis by Using .NET

  • StackExchange.Redis is a popular high-performance Redis client for .NET
  • Connection string for a Redis server:
    • ssl: Ensures encrypted communication
    • abortConnection: Allows connection to be created even if server is not available at that moment
    [cache-name].redis.cache.windows.net:6380,password=[password-here],ssl=True,abortConnect=False
    
  • Main connection object is ConnectionMultiplexer to abstract the process of connecting to a Redis server, optimised to handle many connections efficiently
    • Provides access to a Redis database
    • Make use of publisher/subscribe features of Redis
    • Access an individual server for maintenance or monitoring purposes
    using StackExchange.Redis;
    ...
    var connectionString = "[cache-name].redis.cache.windows.net:6380,password=[password-here],ssl=True,abortConnect=False";
    var redisConnection = ConnectionMultiplexer.Connect(connectionString);
    
  • Accessing a Redis database
    • With the database object, you can store key/values in the cache
    IDatabase db = redisConnection.GetDatabase();
    
    bool wasSet = db.StringSet("favorite:flavor", "i-love-rocky-road");
    
    string value = db.StringGet("favorite:flavor");
    Console.WriteLine(value); // displays: ""i-love-rocky-road""
    
  • Getting/Setting binary values
    • Redis keys and values are binary safe
    byte[] key = ...;
    byte[] value = ...;
    
    db.StringSet(key, value);
    
    byte[] key = ...;
    byte[] value = db.StringGet(key);
    
  • Other common operations:
    • CreateBatch, CreateTransaction, KeyDelete, KeyExists, KeyExpire, KeyRename, KeyTimeToLive, KeyType
  • Complex values such as objects can be serialised to a textual format (XML, JSON) and stored as a string in the cache
    • Newtonsoft.Json library in C#
  • Cleaning up connection, need to Dispose of the ConnectionMultiplexer
    redisConnection.Dispose();
    redisConnection = null;
    

Azure Content Delivery Networks

  • Global solution for rapidly delivering high-bandwith content to users
  • Caches content at edge location across the world
  • Can also accelerate dynamic content which can't be cached, using various network optimisations using CDN POPs
  • Benefits of using Azure CDN to deliver web assets:
    • Better performance and UX for end users, especially apps which use multiple round-trips to load content
    • Large scaling to handle high loads, such as launch of a product
    • Distribution of user requests and serving content directly from edge servers so there's less traffic sent to the origin server
  • Key features:
    • Dynamic site acceleration
    • CDN caching rules
    • HTTPS custom domain support
    • Azure diagnostics log
    • File compression
    • Geo-filtering

How It Works

  • Steps:
    1. Request Routing: A user requests a file via a URL (ex. <endpoint name>.azureedge.net). The DNS routes the request to the nearest or best-performing POP
    2. Cache Lookup: If the file isn't in the POP's cache, the POP fetches it from the origin server (ex. Azure Web App, Storage Account or any web server)
    3. File Delivery: The origin server sends the file to the POP, where it's cached and delivered to the user
    4. Caching: The file remains in the cache for a TTL duration, improving performance for subsequent users requesting the same file
  • Requirements:
    • Own at least one Azure subscription
    • Need to create a CDN profile (collection of CDN endpoints)
    • Create multiple CDN profiles if you want to use a mix of pricing tiers (Azure CDN pricing is applied at the CDN profile level)
  • Limitations (each subscription has default limits for the following resources):
    • Number of CDN profiles created
    • Number of endpoints created in a CDN profile
    • Number of custom domains mapped to an endpoint

Control Cache Behaviour on Azure CDN

  • Use CDN caching rules to set or modify default cache expiration (either global or with custom conditions)
    • Global: Set one global caching rule for each endpoint in your profile, affecting all requests to the endpoint. Overrides any HTTP cache-directive headers if applied
    • Custom: Set one or more custom caching rules for each endpoint in your profile. Match specific paths and file extensions, processed in order and override global caching rule if applied
  • Query string caching adjusts how Azure CDN treats caching for requests
  • In the Standard ruels engine for Azure CDN, a rule consists of one or more match conditions and an action
    • Common uses include overriding / defining a custom cache policy, redirecting requests and modifying HTTP request and response headers
    • Each rule can have up to four match conditions
    • Match conditions:
      • Device type, HTTP version, request cookies, post argument, query string
  • TTL is determined by the Cache-Control header in the HTTP response from origin server
    • Azure CDN sets a default TTL if the user doesn't set one on the file (can be overridden via caching rules)
    • Default TTL values:
      • Generalised web delivery optimisations (7 days)
      • Large file optimisations (1 day)
      • Media streaming optimisations (1 year)
  • Azure CDN caches content until its TTL expires
    • After expiration, the edge node retrieves the latest version from origin to ensure users always get updated assets, version assets with new URLs for updates
    • For urgent updates, purge cached content to refresh assets across edge notes using the portal, CLI or wildcards
    az cdn endpoint purge \
        --content-paths '/css/*' '/js/app.js' \
        --name ContosoEndpoint \
        --profile-name DemoProfile \
        --resource-group ExampleGroup
    
    • Preloading assets into an endpoint is useful if your app creates a large number of assets, this improves UX by prepopulating the cache before any actual requests occur
    az cdn endpoint load \
        --content-paths '/img/*' '/js/module.js' \
        --name ContosoEndpoint \
        --profile-name DemoProfile \
        --resource-group ExampleGroup
    
  • Geo-filtering enables you to allow or block content from specific countries/regions (based on country/region code)
    • In the Standard tier, you can only allow or block the entire site

Interact with Azure CDN Using .NET

  • From Visual Studio Package Manager console or .NET CLI, install Microsoft.Azure.Management.Cdn
  • Creating a CDN client
    static void Main(string[] args)
    {
        // Create CDN client
        CdnManagementClient cdn = new CdnManagementClient(new TokenCredentials(authResult.AccessToken))
            { SubscriptionId = subscriptionId };
    }
    
  • List CDN profiles and endpoints
    private static void ListProfilesAndEndpoints(CdnManagementClient cdn)
    {
        // List all the CDN profiles in this resource group
        var profileList = cdn.Profiles.ListByResourceGroup(resourceGroupName);
        foreach (Profile p in profileList)
        {
            Console.WriteLine("CDN profile {0}", p.Name);
            if (p.Name.Equals(profileName, StringComparison.OrdinalIgnoreCase))
            {
                // Hey, that's the name of the CDN profile we want to create!
                profileAlreadyExists = true;
            }
    
            //List all the CDN endpoints on this CDN profile
            Console.WriteLine("Endpoints:");
            var endpointList = cdn.Endpoints.ListByProfile(p.Name, resourceGroupName);
            foreach (Endpoint e in endpointList)
            {
                Console.WriteLine("-{0} ({1})", e.Name, e.HostName);
                if (e.Name.Equals(endpointName, StringComparison.OrdinalIgnoreCase))
                {
                    // The unique endpoint name already exists.
                    endpointAlreadyExists = true;
                }
            }
            Console.WriteLine();
        }
    }
    
  • Create CDN profiles and endpoints
    private static void CreateCdnProfile(CdnManagementClient cdn)
    {
        if (profileAlreadyExists)
        {
            Console.WriteLine("Profile {0} already exists.", profileName);
        }
        else
        {
            Console.WriteLine("Creating profile {0}.", profileName);
            ProfileCreateParameters profileParms =
                new ProfileCreateParameters() { Location = resourceLocation, Sku = new Sku(SkuName.StandardVerizon) };
            cdn.Profiles.Create(profileName, profileParms, resourceGroupName);
        }
    }
    
    private static void CreateCdnEndpoint(CdnManagementClient cdn)
    {
        if (endpointAlreadyExists)
        {
            Console.WriteLine("Endpoint {0} already exists.", endpointName);
        }
        else
        {
            Console.WriteLine("Creating endpoint {0} on profile {1}.", endpointName, profileName);
            EndpointCreateParameters endpointParms =
                new EndpointCreateParameters()
                {
                    Origins = new List<DeepCreatedOrigin>() { new DeepCreatedOrigin("Contoso", "www.contoso.com") },
                    IsHttpAllowed = true,
                    IsHttpsAllowed = true,
                    Location = resourceLocation
                };
            cdn.Endpoints.Create(endpointName, endpointParms, profileName, resourceGroupName);
        }
    }
    
  • Purge an endpoint
    private static void PromptPurgeCdnEndpoint(CdnManagementClient cdn)
    {
        if (PromptUser(String.Format("Purge CDN endpoint {0}?", endpointName)))
        {
            Console.WriteLine("Purging endpoint. Please wait...");
            cdn.Endpoints.PurgeContent(resourceGroupName, profileName, endpointName, new List<string>() { "/*" });
            Console.WriteLine("Done.");
            Console.WriteLine();
        }
    }