.netcore入门18:aspnetcore分布式缓存(分布式内存、sqlserver、redis)
环境:
- aspnetcore 3.1.1
- vs2019 16.4.5
.netcore的本地缓存请参考:.net core中使用缓存之MemoryCache(本机内存)
一、分布式缓存介绍
分布式的缓存由多个应用程序服务器共享,缓存中的信息不存储在单独的 Web 服务器的内存中,并且缓存的数据可用于所有应用服务器。这具有几个优点:
- 所有 Web 服务器上的缓存数据都是一致的。
- 缓存的数据在 Web 服务器重新启动后和部署后仍然存在。
- 对数据库的请求变的更少 。
aspnetcore框架定义了分布式缓存的接口IDistributedCache
,我们在自己的代码中应该与这个接口做交互,同样,aspnetcore平台上的所有分布式缓存实现也应该实现这个接口。我们来看下这个接口定义的方法:
- Get,GetAsync :
接受字符串键,并检索缓存项作为 byte[] 数组(如果在缓存中找到)。 - Set,SetAsync:
使用字符串键将项(作为 byte[] 数组)添加到缓存中。 - Refresh,RefreshAsync :
基于其键刷新缓存中的项,并重置其可调过期超时值(如果有)。 - Remove,RemoveAsync:
会根据其字符串键删除缓存项。
aspnetcore已经内置了分布式内存缓存,并以nuget包形式提供了分布式 SQL Server 缓存和分布式 Redis 缓存的实现,下面具体看一下:
二、分布式内存缓存:
aspnetcore提供的这个缓存实现是为了开发测试用的,其实它并没有实现真正的分布式。你观察它的源码后会发现,它内部调用的还是IMemoryCache
。下面看一下它的使用方式:
2.1 第一步:向容器注册服务
public void ConfigureServices(IServiceCollection services)
{
services.AddDistributedMemoryCache();
}
2.2 第二步:Controller中调用
namespace _2webapidemo.Controllers
{
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
private readonly ILogger<WeatherForecastController> _logger;
private readonly IDistributedCache distributedCache;
public WeatherForecastController(ILogger<WeatherForecastController> logger,/* IMemoryCache cache, */IDistributedCache distributedCache)
{
_logger = logger;
this.distributedCache = distributedCache;
}
[HttpGet]
public async Task<IEnumerable<WeatherForecast>> Get()
{
#region 测试分布式内存缓存
var key = Guid.NewGuid().ToString();
var message = "Hello, World!";
var value = Encoding.UTF8.GetBytes(message);
Console.WriteLine("Connecting to cache");
Console.WriteLine("Connected");
Console.WriteLine("Cache item key: {0}", key);
Console.WriteLine($"Setting value '{message}' in cache");
await distributedCache.SetAsync(key,value,new DistributedCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromSeconds(10)));
Console.WriteLine("Set");
Console.WriteLine("Getting value from cache");
value = await distributedCache.GetAsync(key);
if (value != null)
{
Console.WriteLine("Retrieved: " + Encoding.UTF8.GetString(value, 0, value.Length));
}
else
{
Console.WriteLine("Not Found");
}
Console.WriteLine("Refreshing value in cache");
await distributedCache.RefreshAsync(key);
Console.WriteLine("Refreshed");
Console.WriteLine("Removing value from cache");
await distributedCache.RemoveAsync(key);
Console.WriteLine("Removed");
Console.WriteLine("Getting value from cache again");
value = await distributedCache.GetAsync(key);
if (value != null)
{
Console.WriteLine("Retrieved: " + Encoding.UTF8.GetString(value, 0, value.Length));
}
else
{
Console.WriteLine("Not Found");
}
#endregion
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
}
}
上面代码的控制台输出如下图:
三、分布式SqlServer缓存
aspnetcore框架提供的这个实现可以让我们很方便的把sqlserver当成一个缓存容器,它的源码也很简单,就是对一张缓存表的增删改查。我们看一下它的源码文件:
有兴趣的可以点开看看里面的内容,里面操作数据库没有依赖EFCore,而是直接使用的SqlConnection、SqlCommand等这些原始对象。下面来看一下它的使用步骤:
3.1 第一步:准备sqlserver数据库
你可以手动建表或者是使用dotnet sql-cache
工具自动建表,两种方法任选其一,但是要注意三个名称“数据库名称”、“模式名称”和“表名称”,因为这三个名称要在代码中进行配置。下面以数据库test
的dbo
下的表global_cache
为例说明两种方案:
3.1.1 方案1:手动建表
直接执行下面脚本即可:
--****************<global_cache>**************
if exists (select 1
from sysobjects
where id = object_id('global_cache')
and type = 'U')
begin
drop table global_cache
print '已删除表:global_cache'
end
go
create table [global_cache] (
[Id] nvarchar(449) not null unique,
[Value] varbinary(max) not null ,
[ExpiresAtTime] datetimeoffset(7) not null ,
[SlidingExpirationInSeconds] bigint ,
[AbsoluteExpiration] datetimeoffset(7)
)
ALTER TABLE [global_cache] ADD CONSTRAINT PK_gene_global_cache_Id PRIMARY KEY(Id)
--************索引<Index_ExpiresAtTime>*****************
CREATE NONCLUSTERED INDEX Index_ExpiresAtTime ON global_cache(ExpiresAtTime)
--************索引</Index_ExpiresAtTime>*****************
print '已创建:global_cache'
--****************</global_cache>**************
3.1.2 方案2:使用dotnet sql-cache
工具:
这种方法是微软官方提供的(参考:分布式 SQL Server 缓存)
1)、首先,你要确保你电脑中安装了sql-cache
工具,如果没有的话,在cmd中执行如下命令安装:
dotnet tool install --global dotnet-sql-cache --version 3.1.1
注意,sql-cache
的版本要和.netcore的版本保持一致,否则,建议卸载重装,卸载的命令如下:
dotnet tool uninstall dotnet-sql-cache --global
2)、然后, 执行如下命令建表
dotnet sql-cache create "Data Source=.;Initial Catalog=test;User ID=sa;Password=xxxxxx;" dbo global_cache
执行完后提示:“Table and index were created successfully.” 则表示建表成功!
3.3 第二步:引入nuget包
虽然微软默认提供了分布式SqlServer缓存的实现,但并没有集成到aspnetcore框架中,所以我们需要将nuget包引入进来:
3.2 第二步:向容器注册服务
public void ConfigureServices(IServiceCollection services)
{
services.AddDistributedSqlServerCache(options=>
{
options.ConnectionString = "Data Source=.;Initial Catalog=test;User ID=sa;Password=sa;";
options.SchemaName = "dbo";
options.TableName = "global_cache";
});
services.AddControllers();
}
3.3 第三步:Controller中调用
同分布式内存。
附:程序运行过程中sqlserver数据库中存储的情况:
四、分布式Redis缓存
redis服务器搭建参考:redis入门(1): linux下安装redis与简单配置。
redis客户端下载地址:
链接:https://pan.baidu.com/s/1l2NPkhgVdnEM89vpJIwGfA
提取码:beg4
说明:
aspnetcore中分布式Redis缓存的源码很简单,因为它对redis的操作依赖于StackExchange.Redis
,先来看看它的源码文件有哪些:
可以看到源码非常少。我们知道redis中有五大数据类型:string(字符串)、hash(哈希)、list(列表)、set(集合)和zset(sorted set:有序集合),这个分布式缓存实现的时候仅使用了hash(哈希)这一种格式进行存储。下面直接看使用步骤:
4.1 第一步:准备redis服务
假设你已经准备好了redis服务为:
ip:192.168.3.28
port:6379
password:123456
4.2 第二步:引入nuget包
虽然微软默认实现了分布式redis缓存,但是并未将程序集集成在aspnetcore框架里面,所以需要我们手动添加上去:
4.3 第三步:向容器注册服务
public void ConfigureServices(IServiceCollection services)
{
services.AddStackExchangeRedisCache(options =>
{
//options.Configuration = "192.168.3.28:6379";
options.ConfigurationOptions = new StackExchange.Redis.ConfigurationOptions()
{
EndPoints = { { "192.168.3.28", 6379 } },
Password = "123456"
};
options.InstanceName = "TestRedisCache";
});
services.AddControllers();
}
4.4 第四步:Controller中调用
同分布式内存缓存
附:代码运行过程中redis存储数据截图
从上面的截图中可以看到,存储的时候八absexp和sldexp也存储了进去,也就是一个缓存项被当成一个hash存储了起来。
五、总结
从上面的介绍中可以看到,虽然我们分别使用了Sqlserver、Redis缓存方式但调用代码不用做任何改变,而这正是由于我们调用的是IDistributedCache
接口提供的服务。