Using Redis Cache with .NET: A Powerful Solution for Performance and Scalability (Cache Series Part 2)
Modern web applications, especially in high-traffic and distributed architectures, demand significant performance optimizations to enhance user experience and reduce server load. In previous parts of our Caching in .NET series, we've explored in-memory caching solutions. Now, we'll delve into Redis Cache, a distributed caching solution that becomes indispensable when your application is scaled across multiple servers or requires high scalability!
Expensive operations like database queries, external API calls, or complex computations can slow down application response times. Redis Cache alleviates such bottlenecks by providing a performance boost. In this blog post, we'll comprehensively explore what Redis cache is, why it's so popular, its benefits, common use cases, and provide a simple example of how you can implement Redis, especially with your ASP.NET Core Web API applications.
What is Redis Cache?
Redis (Remote Dictionary Server) is an open-source, in-memory data structure store. It's commonly used as a cache and message broker. Because Redis keeps data in RAM, it offers incredibly fast read and write operations. Beyond simple key-value storage, it supports rich data structures like lists, sets, sorted sets, and hashes. As a distributed cache, Redis allows multiple application servers to access and share the same cached data.
Why Should You Use Redis Cache? Benefits and Necessity
Here are the primary benefits and necessities of using a cache like Redis in a distributed environment:
Performance Improvements: Reading data from Redis is significantly faster than fetching it from the primary storage unit (e.g., database). This reduces application response times to milliseconds.
Consistency in Distributed Environments: If your application runs on multiple servers, in-memory caches can hold different copies of data on each server. A distributed cache like Redis ensures all servers access the same, up-to-date cached data, guaranteeing data consistency.
Reduced Database and API Load: By caching frequently accessed data in Redis, you significantly reduce the load on your underlying database servers or external APIs. This optimizes resource utilization and allows the system to handle more concurrent requests.
Scalability: When your application's traffic load increases, scaling Redis servers horizontally is relatively easy. This ensures your application can support more users seamlessly.
Flexibility: Redis allows for various use cases, from simple key-value pairs to more complex data structures.
Durability (Optional): Redis can optionally persist data to disk, which can ensure that cached data is not lost even if the Redis server restarts.
Common Redis Cache Use Cases
Redis cache plays a critical role, especially in the following scenarios:
Frequently Read Dynamic Data: Dynamic data that changes but is frequently accessed, such as product information on e-commerce sites, articles on news sites, or user profiles.
Caching API Responses: JSON responses from external APIs that remain valid for a certain period.
Session Management: Using Redis for session data that requires high availability and scalability allows sessions to be shared seamlessly across application servers.
Real-time Analytics and Leaderboards: Redis's fast data structures are ideal for real-time leaderboards or instant analytics data.
Message Queues and Publish/Subscribe (Pub/Sub): Redis can also be used for simple message queues or publish/subscribe mechanisms.
Rate Limiting: Used to limit API requests within a specific time frame.
Using Redis Cache with ASP.NET Core Web API
To use Redis cache in your ASP.NET Core applications, we use the IDistributedCache
interface. This interface allows you to use different distributed cache providers (Redis, SQL Server, NCache, etc.) with the same code.
Here's a simple example of how to use Redis cache in a simple ASP.NET Core Web API application:
1. Install NuGet Package:
First, you need to add the Microsoft.Extensions.Caching.StackExchangeRedis
NuGet package to your project:
Install-Package Microsoft.Extensions.Caching.StackExchangeRedis
2. Service Configuration (Program.cs
):
You need to configure the Redis cache service in your Program.cs
file. Here you specify the connection string for your Redis server.
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Caching.Distributed;
using System.Text;
using System.Text.Json;
var builder = WebApplication.CreateBuilder(args);
// Add controllers
builder.Services.AddControllers();
// Add Swagger/OpenAPI support
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// Add Redis cache service
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = builder.Configuration.GetConnectionString("RedisConnection");
options.InstanceName = "SampleApi_"; // Prefix to be added to cache keys
});
var app = builder.Build();
// Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
3. Add Redis Connection String to appsettings.json
:
Add the connection string for your Redis server to your appsettings.json
file:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"RedisConnection": "localhost:6379,abortConnect=false" // Address and port of your Redis server
}
}
4. Cache Usage (Example Web API Controller):
You can perform cache operations by injecting the IDistributedCache
interface into a controller. This example includes an API endpoint that will cache and read a mock product list.
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Distributed;
using System.Text;
using System.Text.Json; // For JSON serialization
using System.Threading.Tasks;
using System.Collections.Generic; // For List
namespace MyWebApp.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly IDistributedCache _cache;
public ProductsController(IDistributedCache cache)
{
_cache = cache;
}
[HttpGet("get-cached-products")]
public async Task<IActionResult> GetCachedProducts()
{
string cacheKey = "AllProducts";
string cachedProductsString = await _cache.GetStringAsync(cacheKey);
List<Product> products;
if (string.IsNullOrEmpty(cachedProductsString))
{
// If not in cache, fetch from a database or external source
// This part would normally be a database call or API request.
// Simulating with a 2-second delay.
await Task.Delay(2000);
products = new List<Product>
{
new Product { Id = 1, Name = "Laptop", Price = 1500, Category = "Electronics" },
new Product { Id = 2, Name = "Desk Chair", Price = 300, Category = "Furniture" },
new Product { Id = 3, Name = "External SSD", Price = 100, Category = "Electronics" }
};
// Serialize data to JSON and add to cache
cachedProductsString = JsonSerializer.Serialize(products);
var options = new DistributedCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromMinutes(10)) // Remove definitively after 10 minutes
.SetSlidingExpiration(TimeSpan.FromMinutes(2)); // Remove if not accessed within 2 minutes
await _cache.SetStringAsync(cacheKey, cachedProductsString, options);
return Ok(new { Source = "Database/API", Data = products });
}
else
{
// If in cache, read directly from cache and deserialize from JSON
products = JsonSerializer.Deserialize<List<Product>>(cachedProductsString);
return Ok(new { Source = "Redis Cache", Data = products });
}
}
// Sample product model
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
}
}
}
In this example:
We add the Redis service with
AddStackExchangeRedisCache
.We inject the
IDistributedCache
interface into the controller.We try to read data from Redis using
GetStringAsync
.If data is not found, we simulate fetching from a data source with a 2-second delay and create the data.
We convert the data to JSON format using
JsonSerializer.Serialize
.We write the data to Redis with specific expiration times (absolute and sliding expiration) using
SetStringAsync
.We convert the JSON data received from Redis back into an object using
JsonSerializer.Deserialize
.
Conclusion
Redis Cache is an invaluable tool for .NET developers, radically simplifying background task management and enhancing its reliability. With its durability, flexible job types, comprehensive monitoring dashboard, and seamless integration with ASP.NET Core, you can significantly improve your application's performance and user experience.
If you are considering adding long-running or periodic operations to your applications, Redis should definitely be at the top of your list! This blog post constituted an important part of our Caching in .NET series. Don't forget to follow the other parts of our series!