WebAPI-缓存
浏览器缓存
使用[ResponseCache(Duration = 60)]特性允许浏览器缓存服务器响应的页面信息60s
1 2 3 4 5 6
| [HttpGet] [ResponseCache(Duration = 60)] public IActionResult GetWeatherForecast() { return Ok(); }
|
除了允许在客户端缓存页面,我们可以通过使用响应缓存中间件来配置服务端的缓存
需要在program.cs文件中配置响应缓存中间件
*这个中间件比较鸡肋,实际开发中也几乎用不上,更多的是采用内存缓存或分布式缓存等 *
1 2 3 4 5 6 7 8
| app.UseAuthorization();
app.UseResponseCaching();
app.MapControllers();
app.Run();
|
内存缓存
在program.cs文件中添加内存缓存服务
1
| builder.Services.AddMemoryCache();
|
在控制器中注册服务,并使用缓存
通过调用GetOrCreate方法从缓存中获取数据,如果数据不存在则生成数据并缓存
当我们对数据库的数据进行操作时,我们可以选择调用Remove或者Set方法来操作或删除缓存中的数据
也可以配置缓存的过期时间,只要过期时间短,缓存数据不一致的情况不会持续很长时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| [ApiController] [Route("api/[controller]")] public class WeatherForecastController : ControllerBase { private readonly IMemoryCache _memoryCache;
public WeatherForecastController(IMemoryCache memoryCache) { _memoryCache = memoryCache; }
[HttpGet] public async Task<IActionResult> Get(long id) { string cacheKey = "WeatherForecast" + id;
var weatherForecasts = await _memoryCache.GetOrCreateAsync(cacheKey, async entry => { entry.SlidingExpiration = TimeSpan.FromMinutes(5);
return await FetchWeatherForecastsFromDataSource(); });
return Ok(weatherForecasts); } }
|
缓存穿透
对于恶意用户,访问一个不存在的缓存key,内存缓存会一直查询数据库,会给数据库造成很大的压力,这种现象被称之为缓存穿透
解决方案就是将数据查不到(null)也作为缓存结果存放在缓存中
调用GetOrCreateAsync方法,即可避免缓存穿透的问题
缓存雪崩
如果在请求频繁的时,缓存过期,会造成同一时间对数据库大量查询,造成缓存雪崩
解决方案就是在基础的过期时间之上,加入随机过期时间,让缓存不在统一时间内被清空,这样就不会一瞬间对数据库发送大量的查询请求
缓存数据混乱
当缓存中的Key冲突时,缓存中的value就会出现数据混乱
所以将Key设置为唯一值,即可解决这个问题,通常我们将key+唯一Id命名
分布式缓存
当Web服务被部署到多个服务器中,我们需要统一的调配同一个缓存,这时,继续使用内存缓存就不是最好的选择了
我们将缓存独立出来,放到一台单独的服务器中,所有的Web服务都来调配这台服务器的缓存,即可完成分布式缓存服务器
.NET Core提供了统一的分布式缓存服务器的操作接口IDistributedCache,用法和内存缓存类似
微软官方发布了针对于Redis数据库支持的包,在实际项目中我们也是使用Redis进行数据缓存
在实际开发中,公司已经将相关框架封装完成
Redis
安装依赖:Microsoft.Extensions.Caching.StackExchangeRedis
注册服务
在program.cs中注册服务,并使用系统环境变量的方式配置Redis链接字符串
1 2 3 4 5 6
| builder.Services.AddStackExchangeRedisCache(option => { option.Configuration = Environment.GetEnvironmentVariable("REDIS_CONNECTION_STRING"); option.InstanceName = "cache1_"; });
|
注入依赖
在控制器或Service中注入依赖,直接使用即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| [ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { private readonly IDistributedCache _distributedCache;
public WeatherForecastController(IDistributedCache distributedCache) { _distributedCache = distributedCache; }
[HttpGet] public async Task<IActionResult> Get(long id) { Person? person; string? s = await _distributedCache.GetStringAsync("Person" + id); if (s == null) { person = new Person(); _distributedCache.SetStringAsync("Person" + id, JsonSerializer.Serialize(person)); } else { person = JsonSerializer.Deserialize<Person?>(s); }
if (person == null) { return NotFound("Person not found"); } return Ok(); } }
|