Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
March 1, 2021 11:38 am GMT

Distributed Caching in ASP.NET Core with Redis

About a year ago, I wrote a blog post on simple In-Memory Caching in ASP.NET Core with IMemoryCache. This article mainly introduced the concept of caching and how we can store stuff in the servers memory for simple tasks. Todays objective is to leverage the IDistributedCache to do some distributed caching so that we can horizontally scale out our web app.

For this specific tutorial, I will be using Redis as my cache provider. Redis is a battle-tested, fast memory cache that can store many types of objects. Redis is being used by giants such as Twitter, Github, Instagram, Stackoverflow, Medium, Airbnb etc.

You can find the accompanying code for this blog post from here.

Heres a snapshot of what we are going to be building.

distributed-caching-in-aspdotnet-core-with-redis-1.png

  1. User requests a user object.
  2. App server checks if we already have a user in the cache and return the object if present.
  3. App server makes an HTTP call to retrieve the list of users.
  4. Users service returns the users list to the app server.
  5. App server sends the users list to the distributed (Redis) cache.
  6. App server gets the cached version until it expires (TTL).
  7. User gets the cached user object.

The main reason why we call this a distributed cache is that it lives outside of our application server (as opposed to traditional in-memory caching) and we have the flexibility of scaling it horizontally (when operating in the cloud), if need be. Head over here to have a look at how this could be useful in enterprise applications.

The IDistributedCache interface provides us with a bunch of methods to manipulate your cache. And the actual implementation is specific to the technology we want to use. Heres a summary of different ways you can do this.

TechnologyNuGet packageNotes
Distributed Memory Cache-This is only recommended for dev and testing purposes. This is not an actual distributed cache.
Distributed SQL Server CacheMicrosoft.Extensions.Caching.SqlServerUse SQL Server instance as a cache (locally or in cloud with Azure SQL Server).
Distributed Redis CacheMicrosoft.Extensions.Caching.StackExchangeRedisUse Redis as a backing store (locally or in cloud with Azure Redis Cache)client package is Developed by peeps at StackExchange.
Distributed NCache CacheNCache.Microsoft.Extensions.Caching.OpenSourceWrapper around the NCache Distributed Cache

Scaffolding a sample app

We will create a Web MVC app in ASP.NET Core 5.

dotnet new mvc -n DistributedCachedotnet new slndotnet sln add DistributedCache
Enter fullscreen mode Exit fullscreen mode

Lets go ahead and add the Redis client package from NuGet.

dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis --version 5.0.1
Enter fullscreen mode Exit fullscreen mode

Creating a Redis docker container

For this step, I assume that you have already installed Docker on your machine. Its handy to have this so that you can spin up your own Redis container whenever you want for development purposes.

docker run --name redis-cache -p 5002:6379 -d redis
Enter fullscreen mode Exit fullscreen mode

We are telling docker to use the official redis image and spin up a container with the name redis-cache and bind port 6379 of the container to the port 5002 of your host machine. Why I chose port 5002 is that it might be open as its a less obvious port number.

If you havent got the Redis image locally, it will fetch that from the DockerHub and spin up a new container under the name redis-cache. Next lets verify that our docker instance is up and running. You could do so with,

docker ps -a
Enter fullscreen mode Exit fullscreen mode

or alternatively with docker ps -a | grep redis-cache to filter our the output, if you have a bunch of containers running in the background like I do

distributed-caching-in-aspdotnet-core-with-redis-2.png

Now that we have the Redis container up and running lets configure our web app to use it.

Application Configuration

Startup.cs

Since we have already added the required NuGet package, we only need to register its service in our apps DI container and tell it where to find our Redis instance.

// Register the RedisCache serviceservices.AddStackExchangeRedisCache(options =>{    options.Configuration = Configuration.GetSection("Redis")["ConnectionString"];});
Enter fullscreen mode Exit fullscreen mode

When we call AddStackExchangeRedisCache on the services object, it registers a singleton of RedisCache class against the IDistributedCache interface under the covers. This is what it looks like in the source:

// ...services.Add(ServiceDescriptor.Singleton<IDistributedCache, RedisCache>());// ..
Enter fullscreen mode Exit fullscreen mode

appsettings.json

Since we have the Docker instance up and running at port 5002, we can mention that for development settings.

"Redis": {  "ConnectionString": "localhost:5002"}
Enter fullscreen mode Exit fullscreen mode

I have brought across the service from my previous tutorial and added them to this project. You can find them under the Services folder. In fact, I have made the code to look more bit simpler as well.

Implementation

The functionality is pretty simple, and heres what we going to do:

  1. Get the cached user (if any) and display its email address
  2. A button to invoke a HTTP call and cache a list of users
  3. A button to clear the cache

The UI would look something like the following.

distributed-caching-in-aspdotnet-core-with-redis-3.png

Lets look at the main entry point of the actions, the HomeController class.

HomeController.cs

public async Task<IActionResult> Index(){    var users = (await _cacheService.GetCachedUser())?.FirstOrDefault();    return View(users);}public async Task<IActionResult> CacheUserAsync(){    var users = await _usersService.GetUsersAsync();    var cacheEntry = users.First();    return View(nameof(Index), cacheEntry);}public IActionResult CacheRemoveAsync(){    _cacheService.ClearCache();    return RedirectToAction(nameof(Index));}
Enter fullscreen mode Exit fullscreen mode

The code here is pretty self-explanatory, and we implement the 3 features we discussed in the Index, CacheUserAsync and CacheRemoveAsync actions.

Tip: You would ideally want to decorate the UsersService class with CacheService by using a DI container such as Scrutor. You dont want to write the plumbing code we have written here to emulate a similar thing as the default DI container doesnt support the behaviour. Refer to Andrew Locks excellent article on this topic.

Im going to skip all the other plumbing code and show you how we Get and Set values with the Redis cache. The real magic happens in the ICacheProvider class.

The code itself it pretty self-explanatory. In the GetFromCache method, we call the GetStringAsync with a given key (_Users in this case). Its worth noting that we need to deserialise it to the type we want before returning it to the caller. Similarly, we serialise our users list and save it as a string in the Redis cache under the _Users key.

CacheProvider.cs

public class CacheProvider : ICacheProvider{    private readonly IDistributedCache _cache;    public CacheProvider(IDistributedCache cache)    {        _cache = cache;    }    public async Task<T> GetFromCache<T>(string key) where T : class    {        var cachedResponse = await _cache.GetStringAsync(key);        return cachedResponse == null ? null : JsonSerializer.Deserialize<T>(cachedResponse);    }    public async Task SetCache<T>(string key, T value, DistributedCacheEntryOptions options) where T : class    {        var response = JsonSerializer.Serialize(value);        await _cache.SetStringAsync(key, response , options);    }    public async Task ClearCache(string key)    {        await _cache.RemoveAsync(key);    }}
Enter fullscreen mode Exit fullscreen mode

So what gets saved under the covers?

We can connect to the container and open up the redis-cli to see whats inside. To do that, you could run the following command.

docker exec -it redis-cache redis-cli
Enter fullscreen mode Exit fullscreen mode

Once you are in, you could issue a hgetall _Users command to inspect whats inside the hash that got saved in our request.

distributed-caching-in-aspdotnet-core-with-redis-4.png

If you like to use a GUI, heres a nice representation of what our web app saved under the hood. I used RedisInsight tool for this.

distributed-caching-in-aspdotnet-core-with-redis-5.png

Demo

Heres a working demo when you run the code from my repo:

As you can see, it will only fetch the users list only the first time we click the Cache It button. Every subsequent request will fetch the users list from the Redis cache and serve to our app. The cache expiry can be configured by setting a sliding window or an absolute expiry by passing in the configuration. In this demo, I have set a sliding expiry for 2 minutes.

Conclusion

In this article, we converted our previous In-Memory example to use the IDistributedCache interface provided by ASP.NET Core and used Redis as a backing store. This approach can be utilised to leverage cloud service such as Azure Redis Cache for use-cases such as response caching, session storage etc.

Hope you enjoyed this article and feel free to share your thoughts and feedback. Until next time

References

  1. https://docs.microsoft.com/en-us/aspnet/core/performance/caching/distributed?view=aspnetcore-5.0
  2. https://redis.io/commands
  3. https://aspnetcore.readthedocs.io/en/stable/performance/caching/distributed.html#using-a-redis-distributed-cache

Original Link: https://dev.to/sahan/distributed-caching-in-asp-net-core-with-redis-1io0

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To