• NET 5 MemoryCache与Redis使用以及StackExchange.Redis和CSRedisCore


    简介以及区别

    ASP.NET Core 缓存Caching,.NET Core 中为我们提供了Caching 的组件。

    目前Caching 组件提供了三种存储方式。

    Memory

    Redis

    SqlServer

    1.MemoryCache

    Cache是一个绝大多数项目会用到的一个技术 为了减少磁盘的读取次数,提高程序性能,将频繁读取的配置文件缓存到内存中,加速配置的读取。并且,在磁盘的配置文件更改后,更新缓存

    1. 绝对过期支持
    2. 滑动过期支持(指定一个时间,TimeSpan,指定时间内有被Get缓存时间则顺延,否则过期)
    3. 过期回调
    4. 自定义过期

    仓库地址是:https://github.com/aspnet/Caching

    IMemoryCache,它表示存储在 Web 服务器内存中的缓存,内存缓存可以存储任何对象,存储形式键值对

    Memorycache无法进行持久化

    Memorycache只支持简单的key/value数据结构

    Memorycache 是多线程  速度快

    2.Redis

    简单来说 redis 就是一个数据库,不过与传统数据库不同的是 redis 的数据是存在内存中的,所以读写速度非常快,因此 redis 被广泛应用于缓存方向。另外,redis 也经常用来做分布式锁。redis 提供了多种数据类型来支持不同的业务场景。除此之外,redis 支持事务 、持久化、LUA脚本、LRU驱动事件、多种集群方案。

    Redis是一个支持持久化的内存数据库,通过持久化机制把内存中的数据同步到硬盘文件来保证数据持久化 ,当Redis重启后通过把硬盘文件重新加载到内存,就能达到恢复数据的目的。

    如果你对数据持久化和数据同步有所要求,那么推荐你选择Redis,因为这两个特性Memcached都不具备。即使你只是希望在升级或者重启系统后缓存数据不会丢失,选择Redis也是明智的。

    Redis支持数据类型比如key-value string  list hash zset 等

    Redis缓存数据库(多个)   Redis缓存文件夹(多个目录)

    Redis只能使用单线程,性能受限于CPU性能

    区别

    对于 redis 和 memcached 我总结了下面四点。现在公司一般都是用 redis 来实现缓存,而且 redis 自身也越来越强大了!

      1、redis支持更丰富的数据类型(支持更复杂的应用场景):Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。memcache支持简单的数据类型,String。

      2、Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而Memecache把数据全部存在内存之中。

      3、集群模式:memcached没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;但是 redis 目前是原生支持 cluster 模式的.

      4、Memcached是多线程,非阻塞IO复用的网络模型;Redis使用单线程的多路 IO 复用模型。

    应用场景

    redis:数据量较小的更性能操作和运算上。

    memcache:用于在动态系统中减少数据库负载,提升性能;做缓存,提高性能(适合读多写少,对于数据量比较大,可以采用sharding)。

    1.MemoryCache使用

    Session缓存和Cache缓存的区别如下:

    (1)最大的区别是Cache提供缓存依赖来更新数据,而Session只能依靠定义的缓存时间来判断缓存数据是否有效。

    (2)即使应用程序终止,只要Cache.Add方法中定义的缓存时间未过期,下次开启应用程序时,缓存的数据依然存在。而Session缓存只是存在于一次会话中,会话结束后,数据也就失效了。

    (3)Session容易丢失,导致数据的不确定性,而Cache不会出现这种情况。

    (4)由于Session是每次会话就被加载,所以不适宜存放大量信息,否则会导致服务器的性能降低。而Cache则主要用来保存大容量信息,如数据库中的多个表。

    需要特别注意:为了提高Cache的有效利用率,建议对于不经常改动的数据使用Cache。

    public void ConfigureServices(IServiceCollection services)
    {  
        //如何处理session
       services.AddSession();  
        //memoryCache   
       services.AddMemoryCache();
       
        //.......
    }
    
     public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        //启用session
        app.UseSession();
        app.UseRouting();
        //......
    }

     

    public void ConfigureServices(IServiceCollection services)
            {
                services.AddMemoryCache();
                // Add framework services.
                      
            }
    private IMemoryCache _cache;
    
            public LongLongController(IMemoryCache memoryCache)
            {
                _cache = memoryCache;
            }

    1、方法:TryGetValue 及 方法缓存的存取(在TryGetValue 中,Out 参数的类型应与缓存中存储的值的类型一致。否则TryGetValue 中的Out参数永远为NULL。)

    public IActionResult Index()
            {
                string cacheKey_2 = "CacheKey";
                List<string> cacheEntry;
                //如果缓存没有过期,则Out测试就是缓存存储的值,注意存放值的类型应该和Out参数一致。
                var bol = _cache.TryGetValue<List<string>>(cacheKey_2, out cacheEntry);
                //判断缓存是否存在
                if (!bol)
                {
                    List<string> lst = new List<string>() { "陈大六", "陈卧龙", "陈新宇", "刘丽", "花国锋" };
                    var cacheEntryOptions = new MemoryCacheEntryOptions()
                     .SetSlidingExpiration(TimeSpan.FromMinutes(10));
                    _cache.Set(cacheKey_2, lst, cacheEntryOptions);
                }
                ViewBag.cacheEntry = _cache.Get(cacheKey_2);
                return View();
            }

    2、设置缓存的过期时间,可采用绝对过期时间或相对过期时间两种模式;

    相对过期时间设置方式:

    var cacheEntryOptions = new MemoryCacheEntryOptions()
                     .SetSlidingExpiration(TimeSpan.FromSeconds(10));

    注意上述代码中的备注;假设我们设置一个缓存的相对过期时间为10秒,缓存由A创建,十秒中内,B进入可系统,并读取了缓存,这时缓存的有效时间又会变成十秒,同理,只要缓存在十秒钟内不间断有其他人访问,则缓存永远不会过期。如果大于十秒,则缓存过期。

    绝对过期时间设置方式:

     var cacheEntryOptions = new MemoryCacheEntryOptions()
                     .SetAbsoluteExpiration(TimeSpan.FromSeconds(10));

    绝对过期时间不管期间有没有人访问,在时间过后,就会过期,清空。

    3、移除缓存

    _cache.Remove(cacheKey_2);

    4、缓存的优先级分为四种:永不过期  大于  高优先级  大于  一般优先级  大于  低优先级。

    /缓存优先级 (程序压力大时,会根据优先级自动回收)
    View Code

    5、缓存过期后,执行回调函数,

    public IActionResult cacheCallback()
            {
                List<string> lst = new List<string>() { "陈大六", "陈卧龙", "陈新宇", "刘丽", "花国锋" };
                //缓存回调 10秒过期会回调
                string cacheKey = "cacheKey";
                _cache.Set(cacheKey, lst, new MemoryCacheEntryOptions()
                    .SetAbsoluteExpiration(TimeSpan.FromSeconds(10))
                    .RegisterPostEvictionCallback((key, value, reason, substate) =>
                    {
                        //调用回调函数
                        GetIntList(key,value,reason,substate);
                    }));
                //
                return View();
            }
            public void GetIntList(object key,object value, object reason, object substate)
            {
                List<string> lst=(List<string>)value;
                //说白了就是被释放了
                Console.WriteLine($"键{key}的缓存因:{reason}而改变。");
                foreach(var item in lst)
                {
                    Console.WriteLine(item);
                }
            }

    6、缓存回调 根据Token过期

    public ActionResult CacheToken()
            {
                List<string> lst = new List<string>() { "陈大六", "陈卧龙", "陈新宇", "刘丽", "花国锋" };
                string cacheKey = "CacheToken";
                //缓存回调 根据Token过期
                var cts = new CancellationTokenSource();
                _cache.Set(cacheKey, lst, new MemoryCacheEntryOptions()
                    .AddExpirationToken(new CancellationChangeToken(cts.Token))
                    .RegisterPostEvictionCallback((key, value, reason, substate) =>
                    {
                        Console.WriteLine($"键{key}值{value}改变,因为{reason}");
                    }));
                cts.Cancel(); //执行到Cancel()方法时,会执行回调删除
                return View();
            }

    2.Redis下载

    redis安装包:https://github.com/microsoftarchive/redis/releases

    redis客户端:https://redisdesktop.com/download  最新的要付费可以使用旧版

    https://github.com/uglide/RedisDesktopManager/releases/tag/0.8.8

    为什么要用 redis?/为什么要用缓存?
      主要从“高性能”和“高并发”这两点来看待这个问题。

    高性能:

    假如用户第一次访问数据库中的某些数据。这个过程会比较慢,因为是从硬盘上读取的。将该用户访问的数据存在缓存中,这样下一次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接操作内存,所以速度相当快。如果数据库中的对应数据改变的之后,同步改变缓存中相应的数据即可!

    高并发:

    直接操作缓存能够承受的请求是远远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。

    redis 的线程模型
      redis 内部使用文件事件处理器 file event handler,这个文件事件处理器是单线程的,所以 redis 才叫做单线程的模型。它采用 IO 多路复用机制同时监听多个 socket,根据 socket 上的事件来选择对应的事件处理器进行处理。

      文件事件处理器的结构包含 4 个部分:

      多个 socket
      IO 多路复用程序
      文件事件分派器
      事件处理器(连接应答处理器、命令请求处理器、命令回复处理器)
      多个 socket 可能会并发产生不同的操作,每个操作对应不同的文件事件,但是 IO 多路复用程序会监听多个 socket,会将 socket 产生的事件放入队列中排队,事件分派器每次从队列中取出一个事件,把该事件交给对应的事件处理器进行处理。

    3.StackExchange.Redis

    在NuGet上安装StackExchange.Redis,然后在appsettings.json文件里面添加Redis相关配置信息:

    {
      "Logging": {
        "LogLevel": {
          "Default": "Warning"
        }
      },
      "AllowedHosts": "*",
      "Redis": {
        "Default": {
          "Connection": "127.0.0.1:6379",
          "InstanceName": "local",
          "DefaultDB": 8
        }
      }
    }

    帮助类

    using StackExchange.Redis;
    using System;
    using System.Collections.Concurrent;
    
    namespace RedisDemo
    {
        public class RedisHelper : IDisposable
        {
            //连接字符串
            private string _connectionString;
            //实例名称
            private string _instanceName;
            //默认数据库
            private int _defaultDB; 
            private ConcurrentDictionary<string, ConnectionMultiplexer> _connections;
            public RedisHelper(string connectionString, string instanceName, int defaultDB = 0)
            {
                _connectionString = connectionString;
                _instanceName = instanceName;
                _defaultDB = defaultDB;
                _connections = new ConcurrentDictionary<string, ConnectionMultiplexer>();
            }
    
            /// <summary>
            /// 获取ConnectionMultiplexer
            /// </summary>
            /// <returns></returns>
            private ConnectionMultiplexer GetConnect()
            {
                return _connections.GetOrAdd(_instanceName, p => ConnectionMultiplexer.Connect(_connectionString));
            }
    
            /// <summary>
            /// 获取数据库
            /// </summary>
            /// <param name="configName"></param>
            /// <param name="db">默认为0:优先代码的db配置,其次config中的配置</param>
            /// <returns></returns>
            public IDatabase GetDatabase()
            {
                return GetConnect().GetDatabase(_defaultDB);
            }
    
            public IServer GetServer(string configName = null, int endPointsIndex = 0)
            {
                var confOption = ConfigurationOptions.Parse(_connectionString);
                return GetConnect().GetServer(confOption.EndPoints[endPointsIndex]);
            }
    
            public ISubscriber GetSubscriber(string configName = null)
            {
                return GetConnect().GetSubscriber();
            }
    
            public void Dispose()
            {
                if (_connections != null && _connections.Count > 0)
                {
                    foreach (var item in _connections.Values)
                    {
                        item.Close();
                    }
                }
            }
        }
    }

    在Startup.cs类的ConfigureServices方法里面添加服务注入:

    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    
    namespace RedisDemo
    {
        public class Startup
        {
            public Startup(IConfiguration configuration)
            {
                Configuration = configuration;
            }
    
            public IConfiguration Configuration { get; }
    
            // This method gets called by the runtime. Use this method to add services to the container.
            public void ConfigureServices(IServiceCollection services)
            {
                //redis缓存
                var section = Configuration.GetSection("Redis:Default");
                //连接字符串
                string _connectionString = section.GetSection("Connection").Value;
                //实例名称
                string _instanceName = section.GetSection("InstanceName").Value;
                //默认数据库 
                int _defaultDB = int.Parse(section.GetSection("DefaultDB").Value ?? "0");           
                services.AddSingleton(new RedisHelper(_connectionString, _instanceName, _defaultDB));
                services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
            }
    
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IHostingEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
    
                app.UseMvc();
            }
        }
    }

    新建一个控制器,然后通过构造函数注入:

    using Microsoft.AspNetCore.Mvc;
    using StackExchange.Redis;
    
    namespace RedisDemo.Controllers
    {
        [Route("api/redis")]
        [ApiController]
        public class RedisController : ControllerBase
        {
            private readonly IDatabase _redis;
            public RedisController(RedisHelper client)
            {
                _redis = client.GetDatabase();
            }
    
            [HttpGet]
            public string Get()
            {
                // 往Redis里面存入数据
                _redis.StringSet("Name", "Tom");
                // 从Redis里面取数据
                string name = _redis.StringGet("Name");
                return name;
            }
        }
    }

    CSRedisCore

    在NuGet上安装CSRedisCore,然后在appsettings.json文件里面添加Redis相关配置信息:

    {
      "RedisServer": {
        "Cache": "192.168.0.3:6379,password=redis,preheat=5,idleTimeout=600,defaultDatabase=13,prefix=Cache"
      },
      "Logging": {
        "IncludeScopes": false,
        "LogLevel": {
          "Default": "Trace",
          "Microsoft": "Warning",
          "Microsoft.Hosting.Lifetime": "Information"
        }
      }
    }

    新建一个 IRedisClient 接口

    1 public interface IRedisClient
    2     {
    3         string Get(string key);
    4         Task<string> GetAsync(string key);
    5         void Set(string key, object t, int expiresSec = 0);
    6         Task SetAsync(string key, object t, int expiresSec = 0);
    7         T Get<T>(string key) where T : new();
    8         Task<T> GetAsync<T>(string key) where T : new();
    9     }

    实现接口

     1 public class CustomerRedis : IRedisClient
     2     {
     3         public string Get(string key)
     4         {
     5             return RedisHelper.Get(key);
     6         }
     7 
     8         public T Get<T>(string key) where T : new()
     9         {
    10             return RedisHelper.Get<T>(key);
    11         }
    12 
    13         public async Task<string> GetAsync(string key)
    14         {
    15             return await RedisHelper.GetAsync(key);
    16         }
    17 
    18         public async Task<T> GetAsync<T>(string key) where T : new()
    19         {
    20             return await RedisHelper.GetAsync<T>(key);
    21         }
    22 
    23         public void Set(string key, object t, int expiresSec = 0)
    24         {
    25             RedisHelper.Set(key, t, expiresSec);
    26         }
    27 
    28         public async Task SetAsync(string key, object t, int expiresSec = 0)
    29         {
    30             await RedisHelper.SetAsync(key, t, expiresSec);
    31         }
    32     }

    Startup

    1 services.AddScoped<IRedisClient, CustomerRedis>();
    2 
    3             var redisConn = Configuration["Cache:RedisConnection"];
    4             //services.Configure<WeChatPayOptions>(Configuration.GetSection("WeChatPay"));
    5             var csredis = new CSRedis.CSRedisClient(redisConn);
    6             RedisHelper.Initialization(csredis);

    调用

     1 private readonly IRedisClient _redisClient;
     2         public ValuesController(IRedisClient redisClient)
     3         {
     4             this._redisClient = redisClient;
     5         }
     6 
     7         [HttpGet("test")]
     8         public async Task<ActionResult> Test()
     9         {
    10             await this._redisClient.SetAsync("test", "test",100);
    11             var res = await this._redisClient.GetAsync("test");
    12             return Ok(res);
    13         }

    github地址:https://github.com/2881099/csredis

     

  • 相关阅读:
    docker
    perl有用的字符串匹配
    nginx 反向代理 kuberntes service 出现 502 问题排查
    应用应用容器化部署最佳实践
    监控系统设计
    kubernetes events 事件机制源码分析
    golang gc 原理和内存分配
    golang 使用 websocket 实现后台消息分组推送
    kubeadm 创建的 k8s 集群的升级
    在 kubernetes pod 中构建 docker image
  • 原文地址:https://www.cnblogs.com/netlock/p/13552657.html
Copyright © 2020-2023  润新知