- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
分布式缓存是由多个应用服务器共享的缓存,通常作为访问它的应用服务器的外部服务进行维护。 分布式缓存可以提高 ASP.NET Core 应用的性能和可伸缩性,尤其是当应用由云服务或服务器场托管时.
与其他将缓存数据存储在单个应用服务器上的缓存方案相比,分布式缓存具有多个优势.
当分发缓存数据时,数据:
.NET Core 框架下对于分布式缓存的使用是基于 IDistributedCache 接口的,通过它进行抽象,统一了分布式缓存的使用方式,它对缓存数据的存取都是基于 byte[] 的.
IDistributedCache 接口提供以下方法来处理分布式缓存实现中的项:
使用的时候只需要将其通过容器注入到相应的类中即可.
分布式缓存是基于特定的缓存应用实现的,需要依赖特定的第三方应用,在接入特定的分布式缓存应用时,需要应用对于的 Nuget 包,微软官方提供了基于 SqlServer 、Redis 实现分布式缓存的 Nuget 包,还推荐了基于 Ncache 的方案,除此之外还有像 Memcache 之类的方案,微软虽然没有提供相应的 Nuget 包,但是社区也有相关开源的项目.
这里只讲 .NET Core 下两种分布式缓存的接入和使用,一种是分布式内存缓存,一种是使用得比较广泛的 Redis。其他的在 .NET Core 框架下的使用是差不多的,仅仅只是接入的时候有点区别。当然,Redis 除了作为分布式缓存来使用,还有其他更加丰富的一些功能,后续也会找时间进行一些介绍.
分布式内存缓存 ( AddDistributedMemoryCache ) 是框架提供的 IDistributedCache 实现,用于将项存储在内存中,它就在 Microsoft.Extensions.Caching.Memory Nuget 包中。 分布式内存缓存不是真正的分布式缓存。 缓存项由应用实例存储在运行该应用的服务器上.
分布式内存缓存是一个有用的实现:
在开发和测试场景中.
当在生产环境中使用单个服务器并且内存消耗不重要时。 实现分布式内存缓存会抽象缓存的数据存储。 如果需要多个节点或容错,它允许在未来实现真正的分布式缓存解决方案.
当应用在 Program.cs 的开发环境中运行时,我们可以通过以下方式使用分布式缓存,以下示例代码基于 .NET 控制台程序:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddDistributedMemoryCache();
})
.Build();
host.Run();
之后还是和内存缓存差不多的例子,演示一下缓存的存取、删除、刷新.
public interface IDistributedCacheService
{
Task PrintDateTimeNow();
}
public class DistributedCacheService : IDistributedCacheService
{
public const string CacheKey = nameof(DistributedCacheService);
private readonly IDistributedCache _distributedCache;
public DistributedCacheService(IDistributedCache distributedCache)
{
_distributedCache = distributedCache;
}
public async Task FreshAsync()
{
await _distributedCache.RefreshAsync(CacheKey);
}
public async Task PrintDateTimeNowAsync()
{
var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
var cacheValue = await _distributedCache.GetAsync(CacheKey);
if(cacheValue == null)
{
// 分布式缓存对于缓存值的存取都是基于 byte[],所以各种对象必须先序列化为字符串,之后转换为 byte[] 数组
cacheValue = Encoding.UTF8.GetBytes(time);
var distributedCacheEntryOption = new DistributedCacheEntryOptions()
{
//AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(20),
AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(20),
SlidingExpiration = TimeSpan.FromSeconds(3)
};
// 存在基于字符串的存取扩展方法,内部其实也是通过 Encoding.UTF8 进行了编码
// await _distributedCache.SetStringAsync(CacheKey, time, distributedCacheEntryOption);
await _distributedCache.SetAsync(CacheKey, cacheValue, distributedCacheEntryOption);
}
time = Encoding.UTF8.GetString(cacheValue);
Console.WriteLine("缓存时间:" + time);
Console.WriteLine("当前时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
}
public async Task RemoveAsync()
{
await _distributedCache.RemoveAsync(CacheKey);
}
}
之后,在入口文件添加以下代码,查看控制台结果是否与预想的一致:
using DistributedCacheSample;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddDistributedMemoryCache();
services.AddTransient<IDistributedCacheService, DistributedCacheService>();
})
.Build();
var distributedCache = host.Services.GetRequiredService<IDistributedCacheService>();
// 第一次调用,设置缓存
Console.WriteLine("第一次调用,设置缓存");
await distributedCache.PrintDateTimeNowAsync();
await Task.Delay(TimeSpan.FromSeconds(1));
// 未过滑动时间,数据不变
Console.WriteLine("未过滑动时间,数据不变");
await distributedCache.PrintDateTimeNowAsync();
await Task.Delay(TimeSpan.FromSeconds(3));
// 已过滑动时间,数据改变
Console.WriteLine("已过滑动时间,数据改变");
await distributedCache.PrintDateTimeNowAsync();
await Task.Delay(TimeSpan.FromSeconds(1));
// 未过滑动时间,手动刷新过期时间
Console.WriteLine("未过滑动时间,手动刷新过期时间");
await distributedCache.FreshAsync();
await Task.Delay(TimeSpan.FromSeconds(2));
// 距离上一次调用此方法,已过滑动时间,但由于手动刷新过过期时间,过期时间重新计算,数据不变
Console.WriteLine("距离上一次调用此方法,已过滑动时间,但由于手动刷新过过期时间,过期时间重新计算,数据不变");
await distributedCache.PrintDateTimeNowAsync();
await Task.Delay(TimeSpan.FromSeconds(2));
// 移除缓存
Console.WriteLine("移除缓存");
await distributedCache.RemoveAsync();
// 原有的缓存已移除,调用此方法是重新设置缓存,数据改变
Console.WriteLine("原有的缓存已移除,调用此方法是重新设置缓存,数据改变");
await distributedCache.PrintDateTimeNowAsync();
host.Run();
结果和预想的是一致的.
Redis 是一种开源的基于内存的非关系型数据存储,通常用作分布式缓存。在 .NET Core 框架中使用 Redis 实现分布式缓存,需要引用 Microsoft.Extensions.Caching.StackExchangeRedis Nuget 包,包中通过 AddStackExchangeRedisCache 添加 RedisCache 实例来配置缓存实现,该类基于 Redis 实现了 IDistributedCache 接口.
(1) 安装 Redis 。
这里我在云服务器上通过 Docker 快速安装了 Redis ,映射容器内 Redis 默认端口 6379 到主机端口 6379,并且设置了访问密码为 123456 .
docker run -d --name redis -p 6379:6379 redis --requirepass "123456"
(2) 应用添加依赖包,并且通过配置服务依赖关系 。
Install-Package Microsoft.Extensions.Caching.StackExchangeRedis
或者通过 VS 的 Nuget 包管理工具进行安装 。
依赖关系配置如下:
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
// services.AddDistributedMemoryCache();
services.AddStackExchangeRedisCache(opyions =>
{
opyions.Configuration = "xxx.xxx.xxx.xxx:6379,password=123456";
});
})
.Build();
这里只需要将原来的分布式内存缓存服务的配置切换为分布式 Redis 缓存的配置即可,其他的什么都不用改,就可以从内存缓存切换到 Redis 分布式缓存了。所以我们在日常工作的应用搭建中推荐使用基于分布式缓存方案,前期或者开发环境中可以使用基于内存的分布式缓存,后面项目的性能要求高了,可以很方便地切换到真正的分布式缓存,只需改动一行代码.
之后基于前面的例子运行应用,可以看到输出的结果是一样的.
而在 Redis 上也可以看得到我们缓存上去的数据.
这里还有一个要注意的点,理论上使用分布式缓存是能够增强应用的性能和体验性的,但是像 Redis 这样的分布式缓存一般情况下是和应用部署在不同的服务器,每一次缓存的获取会存在一定的网络传输消耗,当缓存的数据量比较大,而且缓存存取频繁的时候,也会有很大的性能消耗。之前在项目中就遇到过这样的问题,由于一个查询功能需要实时进行计算,计算中需要进行循环,而计算依赖于基础数据,这部分的数据是使用缓存的,当初直接使用 Redis 缓存性能并不理想。当然可以说这种方式是有问题的,但是当时由于业务需要,封装的计算方法中需要在应用启动的时候由外部初始化基础数据,为基础数据能够根据前端改动而刷新,所以用了缓存的方式.
下面是一个示例进行内存缓存和 Redis 缓存的对比:
这里利用 BenchmarkDotNet 进行性能测试,需要先对原有的代码进行一下改造,这里调整了一下构造函数,自行实例化相关缓存的对象,之后有三个方法,分别使用 Redis 缓存、内存缓存、内存缓存结合 Redis 缓存,每个方法中模拟业务中的1000次循环,循环中缓存数据进行存取.
[SimpleJob(RuntimeMoniker.Net60)]
public class DistributedCacheService : IDistributedCacheService
{
public const string CacheKey = nameof(DistributedCacheService);
private readonly IDistributedCache _distributedCache;
private readonly IDistributedCache _distributedMemoryCache;
private readonly IMemoryCache _memoryCache;
[Params(1000)]
public int N;
public DistributedCacheService()
{
_distributedCache = new RedisCache(Options.Create(new RedisCacheOptions()
{
Configuration = "1.12.64.68:6379,password=123456"
}));
_distributedMemoryCache = new MemoryDistributedCache(Options.Create(new MemoryDistributedCacheOptions()));
_memoryCache = new MemoryCache(Options.Create(new MemoryCacheOptions()));
}
public async Task FreshAsync()
{
await _distributedCache.RefreshAsync(CacheKey);
}
public async Task PrintDateTimeNowAsync()
{
var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
var cacheValue = await _distributedCache.GetAsync(CacheKey);
if (cacheValue == null)
{
// 分布式缓存对于缓存值的存取都是基于 byte[],所以各种对象必须先序列化为字符串,之后转换为 byte[] 数组
cacheValue = Encoding.UTF8.GetBytes(time);
var distributedCacheEntryOption = new DistributedCacheEntryOptions()
{
//AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(10),
AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(20),
SlidingExpiration = TimeSpan.FromSeconds(3)
};
// 存在基于字符串的存取扩展方法,内部其实也是通过 Encoding.UTF8 进行了编码
// await _distributedCache.SetStringAsync(CacheKey, time, distributedCacheEntryOption);
await _distributedCache.SetAsync(CacheKey, cacheValue, distributedCacheEntryOption);
}
time = Encoding.UTF8.GetString(cacheValue);
Console.WriteLine("缓存时间:" + time);
Console.WriteLine("当前时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
}
[Benchmark]
public async Task PrintDateTimeNowWithRedisAsync()
{
for(var i =0; i< N; i++)
{
var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
var cacheValue = await _distributedCache.GetAsync(CacheKey);
if (cacheValue == null)
{
// 分布式缓存对于缓存值的存取都是基于 byte[],所以各种对象必须先序列化为字符串,之后转换为 byte[] 数组
cacheValue = Encoding.UTF8.GetBytes(time);
var distributedCacheEntryOption = new DistributedCacheEntryOptions()
{
//AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(10),
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10),
SlidingExpiration = TimeSpan.FromMinutes(5)
};
// 存在基于字符串的存取扩展方法,内部其实也是通过 Encoding.UTF8 进行了编码
// await _distributedCache.SetStringAsync(CacheKey, time, distributedCacheEntryOption);
await _distributedCache.SetAsync(CacheKey, cacheValue, distributedCacheEntryOption);
}
time = Encoding.UTF8.GetString(cacheValue);
}
}
[Benchmark]
public async Task PrintDateTimeWithMemoryAsync()
{
for (var i = 0; i < N; i++)
{
var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
var cacheValue = await _distributedMemoryCache.GetAsync(CacheKey);
if (cacheValue == null)
{
// 分布式缓存对于缓存值的存取都是基于 byte[],所以各种对象必须先序列化为字符串,之后转换为 byte[] 数组
cacheValue = Encoding.UTF8.GetBytes(time);
var distributedCacheEntryOption = new DistributedCacheEntryOptions()
{
//AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(10),
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10),
SlidingExpiration = TimeSpan.FromMinutes(5)
};
// 存在基于字符串的存取扩展方法,内部其实也是通过 Encoding.UTF8 进行了编码
// await _distributedCache.SetStringAsync(CacheKey, time, distributedCacheEntryOption);
await _distributedMemoryCache.SetAsync(CacheKey, cacheValue, distributedCacheEntryOption);
}
time = Encoding.UTF8.GetString(cacheValue);
}
}
[Benchmark]
public async Task PrintDateTimeWithMemoryAndRedisAsync()
{
for (var i = 0; i < N; i++)
{
var cacheValue = await _memoryCache.GetOrCreateAsync(CacheKey, async cacheEntry =>
{
var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
var redisCacheValue = await _distributedCache.GetAsync(CacheKey);
if (redisCacheValue == null)
{
// 分布式缓存对于缓存值的存取都是基于 byte[],所以各种对象必须先序列化为字符串,之后转换为 byte[] 数组
redisCacheValue = Encoding.UTF8.GetBytes(time);
var distributedCacheEntryOption = new DistributedCacheEntryOptions()
{
//AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(10),
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10),
SlidingExpiration = TimeSpan.FromMinutes(5)
};
// 存在基于字符串的存取扩展方法,内部其实也是通过 Encoding.UTF8 进行了编码
// await _distributedCache.SetStringAsync(CacheKey, time, distributedCacheEntryOption);
await _distributedCache.SetAsync(CacheKey, redisCacheValue, distributedCacheEntryOption);
}
time = Encoding.UTF8.GetString(redisCacheValue);
cacheEntry.SlidingExpiration = TimeSpan.FromSeconds(20);
return time;
});
}
}
public async Task RemoveAsync()
{
await _distributedCache.RemoveAsync(CacheKey);
}
}
Program.cs 文件中只保留以下代码:
Summary summary = BenchmarkRunner.Run<DistributedCacheService>();
Console.ReadLine();
测试结果如下:
可以看到这种情况下使用 Redis 缓存性能是惨不忍睹的,但是另外两种方式就不一样了.
我们在业务中的缓存最终就是第三种方法的方式,结合内存缓存和 Redis 缓存,根本的思路就是在使用时将数据临时保存在本地,减少网络传输的消耗,并且根据实际业务情况控制内存缓存的超时时间以保持数据的一致性.
参考文章: ASP.NET Core 中的分布式缓存 。
ASP.NET Core 系列:
目录: ASP.NET Core 系列总结 上一篇: ASP.NET Core - 缓存之内存缓存(下) 。
最后此篇关于ASP.NETCore-缓存之分布式缓存的文章就讲到这里了,如果你想了解更多关于ASP.NETCore-缓存之分布式缓存的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
在一个新的 ASP.NET Core RC2 类库中,我有以下 project.json 文件,我试图在其中遵循 How to trim your package dependencies 上的文档.
我在本地提要上将 nuget 包从 2.2.x 更新到 3.1.0。 然后,在构建机器上尝试构建项目,但是: Project.csproj(0,0): Error NU1102: Unable to
什么是Kafka Apache Kafka是一个分布式流处理平台,由LinkedIn开发并开源,后来成为Apache软件基金会的顶级项目。Kafka主要用于构建实时数据管道和流式应用程序。
快递100的物流信息查询接口,官方提供了一些demo;还好官方提供的代码是.netcore版本写的,不过写的有点low;根据官方提供的代码,我按照.netcore 的风格重构了代码;核心代码如下:
1、docker介绍 docker是用go语言编写基于linux操作系统的一些特性开发的,其提供了操作系统级别的抽象,是一种容器管理技术,它隔离了应用程序对基础架构(操作系统等)的依赖。相较于虚
demo运行在windows的docker中,系统是win10,所以需要先下载docker for windows,安装完毕后系统会重启,然后桌面上可以找到docker for windows的快捷
NetCore WebSocket 即时通讯示例,供大家参考,具体内容如下 1.新建Netcore Web项目 2.创建简易通讯协议 ?
.NET Core:架构、特性和优势详解 在软件开发领域,保持领先地位至关重要。随着技术以指数级的速度发展,开发人员不断寻求高效、可扩展且多功能的解决方案来应对现代挑战。.NET Core 就是这样
需求场景: 我需要部署的项目是在Windows上开发的,目标框架为.net core 6.0 因此我们需要先在kylin上部署项目运行所需要的环境。 借助百
我正在 .NET Core 中重写一个调用外部 Web 服务的控制台应用程序。 我目前收到以下错误: One or more errors occurred. (The HTTP request is
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 6 年前。 Improve th
我有一组库,我想将其从 PCL 转移到 netcore。通过此举,我想简化 DI 系统并更新一些内部工作方式。 我想添加的其中一件事是内部对象的配置,就像在 Asp.Net Core 中一样(即 se
注:本文隶属于《理解ASP.NET Core》系列文章,请查看置顶博客或 点击此处查看全文目录 概述 在微服务化的架构设计中,网关扮演着重要的看门人角色,它所提供的功能之一
对于有多个应用系统的企业来说,每一个应用系统都有自己的用户体系,这就造成用户在切换不同应用系统时,就要多次输入账号密码,导致体验非常不好,也造成使用上非常不便。 针对这个问题,我们就可以采用单
就像 Web Api 接口可以对入参进行验证,避免用户传入非法的或者不符合我们预期的参数一样,选项也可以对配置源的内容进行验证,避免配置中的值与选项类中的属性不对应或者不满足预期,毕竟大部分配置
.NET Core 选项系统的主要实现在 Microsoft.Extensions.Options 和 Microsoft.Extensions.Options.ConfigurationExten
漏洞说明: 跨站脚本攻击(Cross Site Scripting),为了不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS。恶意攻击
分布式缓存是由多个应用服务器共享的缓存,通常作为访问它的应用服务器的外部服务进行维护。 分布式缓存可以提高 ASP.NET Core 应用的性能和可伸缩性,尤其是当应用由云服务或服务器场托管时。
一个应用要运行起来,往往需要读取很多的预设好的配置信息,根据约定好的信息或方式执行一定的行为。 配置的本质就是软件运行的参数,在一个软件实现中需要的参数非常多,如果我们以 Hard Code(
2. 配置添加 配置系统可以读取到配置文件中的信息,那必然有某个地方可以将配置文件添加到配置系统中。之前的文章中讲到 ASP.NET Core 入口文件中,builder(WebApplica
我是一名优秀的程序员,十分优秀!