本文重点介绍 ASP.NET 中输出缓存的实现。在 ASP.NET Core 7 中,输出缓存已成为一项内置功能,无需手动实现。输出缓存是 Core 中用于缓存频繁访问数据 ASP.NET 策略,主要目的是提高性能。通过减少对资源密集型依赖项(如数据库或网络调用)的重复请求,我们可以显著缩短应用程序的响应时间。这种优化对于有效扩展应用程序至关重要。以前,开发人员必须自己处理这些功能。Microsoft的这种集成是一个显着的优势。我们在之前的博客中看到了不同类型的缓存,要深入了解两者之间的差异,请使用以下链接阅读以前的博客:
现在,我们将使用 .NET 8 设置一个简单的 Web API,并演示输出缓存设置、缓存策略和缓存键。根据博客的长度,我们将通过单个博客或两个博客学习所有这些概念。
首先,让我们设置一个用于创建和查询客户的 API,我们将客户存储在 MongoDb 中。
[Route("api/[controller]")]
[ApiController]
public class CustomerController : ControllerBase
{
private readonly ICustomerRepository _customerRepository;
public CustomerController(ICustomerRepository customerRepository)
{
_customerRepository = customerRepository;
}
[HttpGet]
public async Task<IActionResult> GetCustomersAsync([FromQuery] string? city=null)
{
var customers = await _customerRepository.GetCustomersAsync(city);
var customerResponses = new List<GetCustomerResponse>();
foreach (var customer in customers)
{
var customerResponse = new GetCustomerResponse()
{
Id = customer.Id,
LastName = customer.LastName,
FirstName = customer.FirstName,
City = customer.City,
Email = customer.Email,
};
customerResponses.Add(customerResponse);
}
return Ok(customerResponses);
}
[HttpPost]
public async Task<IActionResult> CreateCustomerAsync([FromBody] CreateCustomerRequest createCustomerRequest)
{
var totalCustomerCount = await _customerRepository.GetTotalDocumentsCountAsync();
var customer = new Customer()
{
Id = (totalCustomerCount + 1).ToString(),
FirstName = createCustomerRequest.FirstName,
LastName = createCustomerRequest.LastName,
Email = createCustomerRequest.Email,
City = createCustomerRequest.City
};
await _customerRepository.CreateCustomerAsync(customer);
return Ok(customer.Id);
}
}
在提供的示例中,每次调用 GetCustomers 方法时,都会向 MongoDB 发送一个请求以检索客户数据。但是,如果我们假设客户数据不经常更改,那么不断查询数据库将是低效的。在这种情况下,缓存输出并最大程度地减少资源消耗会更省资源。现在让我们看看如何通过将中间件(如下所示)添加到服务收集和请求管道中来为此 API 设置输出缓存。
using BusinessLogic.Data;
var builder = WebApplication.CreateBuilder(args);
// This will load the MongoDb settings
builder.Services.Configure<MongoDbSettings>(builder.Configuration.GetSection(nameof(MongoDbSettings)));
// Add services to the container.
builder.Services.AddScoped<ICustomerRepository, CustomerRepository>();
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
//Caching
builder.Services.AddOutputCache();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseAuthorization();
app.MapControllers();
app.UseOutputCache();
app.Run();
现在为 GetCustomers 添加 [OutputCache],如下所示。
[HttpGet]
[OutputCache]
public async Task<IActionResult> GetCustomersAsync([FromQuery] string? city=null)
{
var customers = await _customerRepository.GetCustomersAsync(city);
var customerResponses = new List<GetCustomerResponse>();
foreach (var customer in customers)
{
var customerResponse = new GetCustomerResponse()
{
Id = customer.Id,
LastName = customer.LastName,
FirstName = customer.FirstName,
City = customer.City,
Email = customer.Email,
};
customerResponses.Add(customerResponse);
}
return Ok(customerResponses);
}
当您从 Swagger 发起请求时,您第一次会看到调用被发送到 MongoDb。但是,当您从 swagger 进行相同的调用时,它将返回缓存的响应。默认情况下,响应最多会缓存一分钟。这看起来很简单,但这将减少资源消耗并提高应用程序性能。假设我们有一个要求,即 GetCustomers 只能缓存 10 秒而不是一分钟,我们可以使用缓存策略来实现这一点。
缓存策略定义了管理缓存方式的规则和行为。对于上述要求,我们可以在配置输出缓存时或在单个端点上使用如下所示的过期策略。
[HttpGet]
[OutputCache(Duration =10)] //Cache for 10 seconds
public async Task<IActionResult> GetCustomersAsync([FromQuery] string? city=null)
{
var customers = await _customerRepository.GetCustomersAsync(city);
var customerResponses = new List<GetCustomerResponse>();
foreach (var customer in customers)
{
var customerResponse = new GetCustomerResponse()
{
Id = customer.Id,
LastName = customer.LastName,
FirstName = customer.FirstName,
City = customer.City,
Email = customer.Email,
};
customerResponses.Add(customerResponse);
}
return Ok(customerResponses);
}
此外,您可以定义缓存策略,如下所示,并在不同的时间点使用它们。
//Caching
builder.Services.AddOutputCache(options =>
{
// By default all the outputs will be cached for 30 seconds
options.AddBasePolicy(builder =>
builder.Expire(TimeSpan.FromSeconds(30)));
//Which ever endpoint using this policy that paritcular endpoint will be cached for 10 seconds
options.AddPolicy("CacheForTenSeconds", builder =>
builder.Expire(TimeSpan.FromSeconds(10)));
});
[HttpGet]
//[OutputCache(Duration =10)] //Cache for 10 seconds
[OutputCache(PolicyName = "CacheForTenSeconds")]
public async Task<IActionResult> GetCustomersAsync([FromQuery] string? city=null)
{
var customers = await _customerRepository.GetCustomersAsync(city);
var customerResponses = new List<GetCustomerResponse>();
foreach (var customer in customers)
{
var customerResponse = new GetCustomerResponse()
{
Id = customer.Id,
LastName = customer.LastName,
FirstName = customer.FirstName,
City = customer.City,
Email = customer.Email,
};
customerResponses.Add(customerResponse);
}
return Ok(customerResponses);
}
在 C# 中的输出缓存上下文中,缓存键是与缓存项关联的唯一标识符。它们用于检索、存储和管理缓存的数据。缓存键在输出缓存中起着至关重要的作用,因为它们允许开发人员指定应缓存哪些数据,并提供一种稍后检索缓存数据的方法。但是,如果您注意到我们到目前为止还没有定义任何用于缓存的键,则默认情况下,端点的完整 URL 将用作缓存资源的缓存键。现在让我们要求按城市缓存客户。我们可以使用 VaryByQuery 缓存键来实现这一点,如下所示。
//Caching
builder.Services.AddOutputCache(options =>
{
// By default all the outputs will be cached for 30 seconds
options.AddBasePolicy(builder =>
builder.Expire(TimeSpan.FromSeconds(30)));
options.AddPolicy("CacheByCity", builder =>
{
builder.Expire(TimeSpan.FromSeconds(10))
.SetVaryByQuery("city");
});
////Which ever endpoint using this policy that paritcular endpoint will be cached for 10 seconds
//options.AddPolicy("CacheForTenSeconds", builder =>
// builder.Expire(TimeSpan.FromSeconds(10))
// .SetVaryByQuery("city"));
});
[HttpGet]
//[OutputCache(Duration =10)] //Cache for 10 seconds
//[OutputCache(PolicyName = "CacheForTenSeconds")]
[OutputCache(PolicyName = "CacheByCity")]
public async Task<IActionResult> GetCustomersAsync([FromQuery] string? city=null)
{
var customers = await _customerRepository.GetCustomersAsync(city);
var customerResponses = new List<GetCustomerResponse>();
foreach (var customer in customers)
{
var customerResponse = new GetCustomerResponse()
{
Id = customer.Id,
LastName = customer.LastName,
FirstName = customer.FirstName,
City = customer.City,
Email = customer.Email,
};
customerResponses.Add(customerResponse);
}
return Ok(customerResponses);
}
我们甚至可以在单个端点控制 VaryByQuery。此外,还可以通过其他方式定义缓存键,例如 VaryByHeader 和 VaryByRouteValues。这些可以在缓存策略级别以及单个终结点级别实现,如上例所述。
OutputCacheOptions 提供用于控制适用于所有终结点的限制的设置:
在此文中,我们了解了如何在 .NET Web API 中配置输出缓存,并看到了它的局限性。此外,我们没有深入研究其他缓存策略,例如缓存失效、按位置缓存,因为它们变得非常复杂。
源代码获取:公众号回复消息【code:49376
】