Redis 入门与 ASP.NET Core 缓存

若是你尚未 redis 集群,能够参考笔者的另外一篇文章:搭建分布式 Redis Cluster 集群与 Redis 入门html

本文将使用 StackExchange.Redis 库来链接和操做 Redis 。git

StackExchange.Redis 的使用,本文只是参照文档,换种方式表示,若是英文基础好,建议阅读文档:https://stackexchange.github.io/StackExchange.Redis/Basicsgithub

本文内容介绍 StackExchange.Redis 的使用基础,而后介绍 ASP.NET Core 中的缓存、如何使用 Redis。redis

基础

Redis 库

C# 下 Redis-Client 开源的库不少,有 BeetleX.Redis、csredis、Nhiredis、redis-sharp、redisboost、Rediska、ServiceStack.Redis、Sider、StackExchange.Redis、TeamDev Redis Client。算法

这里咱们使用 StackExchange.Redis,另外 csredis 如今叶老板(Freesql做者)贡献了大量维护,而且叶老板新开了一个叫 FreeRedis 的框架,目前正在开发中,有兴趣能够参与开发或提出建议。sql

链接 Redis

建立一个 .NET Core 项目,Nuget 库添加引用 StackExchange.Redis ,使用最新版本。数据库

Redis 默认端口为 6379,若是要链接本地 Redis 服务:api

ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost");
ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost:6379");
// ”{ip}:{port}“

若是使用 redis 集群,则使用 , 分隔地址:缓存

ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("server1:port1,server2:port2,server3:port3");

可能要注意区分集群模式,多 redis 实例的地址,适合主从模式的集群或者 redis culster 集群,哨兵模式笔者尚未测试过。mvc

能用 redis 干啥

redis 具备不少应用场景,通常使用到的场景有:

  • 存储数据(当数据库使用)
  • 利用 pub/sub 作消息队列

接下来将介绍这两种场景的使用方法。

Redis 数据库存储

访问 redis 数据库:

IDatabase db = redis.GetDatabase();

Redis 默认有 16 个数据库,能够 GetDatabase(int db ) 获取指定的数据库。

使用了 redis cluster 集群的 redis 节点,只有一个数据库,不能自由选择。这里咱们只须要使用 redis.GetDatabase() 便可 。

Redis 使用比较简单的,大多时候,只要有相应的应用场景,咱们查询文档很快就能够掌握,因此这里只介绍字符串的使用。

字符串

redis 的字符串参考:https://www.cnblogs.com/whuanle/p/13837153.html#字符串string

IDatabase 中包含 string 类型的数据操做,其 API 使用 String 开头,辨识度高。

设置一个字符串数据:

db.StringSet("A", "这是一条字符串数据的值");
            var value = db.StringGet("A");

若是字符串使用 byte[] (二进制)存储,也能够设置值:

byte[] str=... ...
            db.StringSet("A", str;

Redis 里面,还有其它不少类型,这里咱们只介绍字符串,由于 API 其实就那么些,用到的时候再学也能够的。先学字符串的使用,其它就是举一反三了。

订阅发布

订阅某个 Topic,当其改变状态时,订阅者能够收到通知,作分布式消息队列也行。相似 MQTT 协议这样。

获取订阅器:

ISubscriber sub = redis.GetSubscriber();

选择订阅的 Topic,并设置回调函数:

sub.Subscribe("Message", (channel, message) => {
    Console.WriteLine((string)message);
});

当某一方订阅了 Message ,在另外一个地方,有别的客户端(也能够是本身)推送 Topic :

sub.Publish("Message","你有一条新的消息,请注意查收");

Topic 推送后,订阅方能够收到推送的消息。

测试代码

ISubscriber sub = redis.GetSubscriber();

            sub.Subscribe("Message", (channel, message) => {
                Console.WriteLine((string)message);
            });

            Thread.Sleep(1000);

            sub.Publish("Message","你有一条新的消息,请注意查收");

channel :Topic 的名称,即上面的 Message。

message:推送的消息内容。

RedisValue

使用 API 设置值的时候,都会有这个参数。由于 Redis 中的值只能是 “字符串”,所以 C# 中也要遵照这种规则,可是 C# 是强类型语言,并且有那么多值类型,只使用 string ,编写代码时会有诸多不便。

所以,就建立了 RedisValue 这个类型,里面有大量的隐式转换重载,因此咱们可使用 C# 的简单类型存储数据以及获取数据,避免手工转换。

固然这个说法不是很准确,使用 RedisValue 主要考虑转换方便。

RedisValue隐式转换

入门的知识就介绍到这里,更多的 Redis 知识能够查看官方文档。下面开始介绍 AS.NET Core 使用分布式缓存。

ASP.NET Core 缓存与分布式缓存

ASP.NET Core 里面有不少定义的标准接口,例如日志、缓存等,这些接口为开发者设置了统一的定义和功能,上层服务不须要变动代码就能切换类库,底层使用哪一种库对上层没有影响。

ASP.NET Core 中的缓存,可使用多种方式完成,例如 Redis,内存,关系型数据库,文件缓存等。并且根据拓展性,能够分为本机缓存,分布式缓存。

本机缓存常见的是内存缓存,内存缓存能够存储任何对象。 分布式缓存最多见的是 Redis,分布式缓存接口仅限 byte[](指参数,继续看到后面的小节就明白了) 。 内存缓存和分布式缓存都使用键值对来存储缓存项。

内存中的缓存

ASP.NET Core 的内存缓存

ASP.NET Core 内存缓存是指通常是单机(本机)使用的,通常这种内存缓存框架是 System.Runtime 或 Microsoft 包提供的,由于不须要考虑分布式或者复杂的结构,因此通常不须要第三方库。这里的内存缓存并不仅是指数据在内存中,因此须要区分 Redis 这类专业的缓存框架。且这里缓存只是做为提升性能而用。

这种缓存主要有两种功能比较丰富的实现 System.Runtime.CachingMemoryCache`。

在内存中缓存、存储数据

在 ASP.NET Core 的内存缓存以外,咱们来讨论一下,编写代码时,本身设置的内存缓存是否合理。

咱们都知道,使用内存缓存是为了提升代码性能而用的

这里笔者我的认为能够从两个层次来对这种缓存归类讨论。

第一种,对于要屡次使用、而每次使用都须要计算、源数据相同则结果相同的,可使用内存缓存。例如反射就比较消耗时间(速度慢),可使用内存缓存起来,下次直接取得信息而不须要从新计算。

下面笔者说一下理由。

内存缓存用在反射缓存这类缓存上,缓存的数据源是可肯定的、可计算总量的,并且这部份内存不须要频繁增长或者减小,不只提升了性能,对 GC 来讲也能够必定程度上减小回收压力,更重要的是开发者能够下降缓存的复杂程度。

这种缓存主要为了不重复计算,或者重复导入(例如加载程序集、从文件加载数据)等。若是数据最近出现过,并且后面一段时间不会变化,使用内存来缓存也很实在,例如 MVC 的视图、每15分钟刷新一次的排行榜等。

第二种是使用内存存储数据,不少人单纯是由于内存存储数据特别快,把内存看成数据库来玩,所以很容易致使内存泄露。最多见的就是使用静态字典、静态列表等,而后编写方法增删查改数据,这一类在压力测试下或者请求量大一些、变更比较频繁的时候,内存堆积特别厉害。

须要频繁变化或须要实时变化的数据,存储在内存中确实速度很是快,如何肯定数据失效、去除无用数据等须要有很深的考虑。

另外,在内存中如使用字典大量存储数据,数据量不少的状况下,每次索引数据的时间都会变长,若是使用了 Linq 或者 for 或者 foreach 等检索数据,也很容易出现耗时长的时间复杂度。这种状况下,你是相信本身的代码,仍是相信 Mysql、SqlServer 等数据库? Hash 算法和红黑树都了解了嘛?

若是实在有需求须要使用内存缓存数据,而且可能动态增长或移除数据的话,可使用 WeakReference 弱引用,即在引用对象的同时仍然容许 GC 回收该对象。缺点是数据可能丢失,不适合须要持久化的数据。

但不管状况,咱们能够肯定:

  • 缓存都是副本
  • 缓存丢失不影响程序的使用
  • 缓存不能无限增加
  • 缓存避免复杂结构
  • ... ...

IMemoryCache

IMemoryCache 提供的接口太少了:

ICacheEntry CreateEntry(object key);
        void Remove(object key);
        bool TryGetValue(object key, out object value);

适合单一的键值缓存。

此接口在 Microsoft.Extensions.Caching.Memory 中有实现,例如 MemoryCache 。适合 ASP.NET Core 中使用。

MemoryCache

这里的 MemoryCache 并非指 IMemoryCache 的实现,而是指 System.Runtime.Caching.MemoryCache,须要安装 Nuget 包。

能够实现对实例对象的缓存,请查看查看官方文档:https://docs.microsoft.com/zh-cn/dotnet/api/system.runtime.caching.memorycache?view=dotnet-plat-ext-3.1

另外内存缓存还有一个分布式内存缓存,但不是真正的分布式,信息能够参考:https://docs.microsoft.com/zh-cn/aspnet/core/performance/caching/distributed?view=aspnetcore-3.1#distributed-memory-cache

分布式缓存

ASP.NET Core 分布式缓存,则使用了 IDistributedCache 这个统一的接口。若是你在 Nuget 搜索 IDistributedCache ,会发现相关的库很是多。

分布式缓存的使用,除了最多见的 Redis,SQLServer 也行,只要实现了 IDistributedCache 就ok。

IDistributedCache

IDistributedCache

IDistributedCache 接口提供的方法实在太少了,有四个异步方法四个同步方法,这里只介绍异步方法。

方法 说明
GetAsync(String, CancellationToken) 获取一个键的值
RefreshAsync(String, CancellationToken) 基于缓存中某个值的键刷新该值,并重置其可调到期超时(若是有)
RemoveAsync(String, CancellationToken) 删除一个键
SetAsync(String, Byte[], DistributedCacheEntryOptions, CancellationToken) 设置一个键的值

局限仍是很大的,只能使用字符串。估计你们可能没怎么使用?

ASP.NET Core 官方支持的分布式缓存,目前主要有 NCache、Redis、SqlServer。本节只讨论 Redis。

Redis 缓存

StackExchange.Redis 是 ASP.NET Core 官方推荐的 Redis 框架,而且官方对其作了封装,能够到 Nuget 搜索 Microsoft.Extensions.Caching.StackExchangeRedis

RedisCache 继承了 IDistributedCache 接口。

Startup.ConfigureServices 中配置服务注册:

services.AddStackExchangeRedisCache(options =>
            {
                options.Configuration = "ip:端口,ip1:端口,ip2:端口";	// redis 集群或单机
                options.InstanceName = "mvc";						// 实例 名称
            });

依赖注入:

private readonly IDistributedCache _cache;

示例:

public async Task<string> Test(string key,string value)
        {
            await _cache.SetStringAsync(key, value);
            return await _cache.GetStringAsync(key);
        }

设置缓存时间:

var options = new DistributedCacheEntryOptions()
            .SetSlidingExpiration(TimeSpan.FromSeconds(20));
            await _cache.SetStringAsync(key, value, options);
相关文章
相关标签/搜索