zoukankan      html  css  js  c++  java
  • 【Rdeis 20】dotnetcore三大Redis客户端对比和使用心得 前言

    前言

      稍微复杂一点的互联网项目,技术选型都可能会涉及Redis,.NetCore的生态越发完善,支持.NetCore的Redis客户端越来越多,

    下面三款常见的Redis客户端,相信大家平时或多或少用到一些,结合平时对三款客户端的使用,有些心得体会。

    先比较宏观的背景: 

    包名称

    背景

    github star

    .NetStandard2.0目标框架上 依赖

    Stackexchange.redis

    老牌.Net Redis客户端,免费无限制,Stackoverflow背书

    3700+

    Microsoft.Extensions.Caching.StackExchangeRedis

    .Netcore 2.2针对IDistributedCache接口实现的Redis分布式缓存

     

    CSRedisCore

    国人实现的著名第三方客户端

    894+

     

    使用心得

    三款客户端Redis支持的连接字符串配置基本相同

      "connectionstrings": {
        "redis": "localhost:6379,password=abcdef,connectTimeout=5000,writeBuffer=40960"
      } 

    StackExchange.Redis

      定位是高性能、通用的Redis .Net客户端;方便地应用Redis全功能;支持Redis Cluster

    • 高性能的核心在于:多路复用连接允许有效使用来自多个调用线程的共享连接), 服务器端操作使用ConnectionMultiplexer 类
    复制代码
    ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("server1:6379,server2:6379");
    // 日常应用的核心类库是IDatabase
    IDatabase db = redis.GetDatabase();
    
    // 支持Pub/Sub
    ISubscriber sub = redis.GetSubscriber();
    sub.Subscribe("messages", (channel, message) => {
        Console.WriteLine((string)message);
    });
    ---
    sub.Publish("messages", "hello");
    复制代码

    也正是因为多路复用,StackExchange.Redis唯一不支持的Redis特性是 "blocking pops",这个特性是RedisMQ的关键理论。

    如果你需要blocking pops, StackExchange.Redis官方推荐使用pub/sub模型模拟实现。

    • 日常操作的API请关注IDatabase接口,支持异步方法,这里我对【客户端操作Redis尽量不要使用异步方法】的说法不敢苟同,对于异步方法我认为还是遵守微软最佳实践:对于IO密集的操作,能使用异步尽量使用异步

    复制代码
    // 对应redis自增api:DECR mykey
    _redisDB0.StringDecrementAsync("ProfileUsageCap", (double)1)
    // 对应redis api: HGET KEY field1
    _redisDB0.HashGetAsync(profileUsage, eqidPair.ProfileId))       
    // 对应redis哈希自增api:  HINCRBY myhash field -1
    _redisDB0.HashDecrementAsync(profileUsage, eqidPair.ProfileId, 1)
    复制代码
    • ConnectionMultiplexer 方式支持随时切换Redis DB,对于多个Redis DB的操作,我封装了一个常用的Redis DB 操作客户端。
     public class RedisStore
        {
            private static Lazy<ConnectionMultiplexer> LazyConnection;
            private static string connectionRedis = "localhost:6379";
    
            public RedisStore(string connectiontring)
            {
                connectionRedis = connectiontring ?? "localhost:6379";
                LazyConnection = new Lazy<ConnectionMultiplexer>(() => ConnectionMultiplexer.Connect(connectionRedis));
            }
            public static ConnectionMultiplexer Connection => LazyConnection.Value;
            public RedisDatabase RedisCache => new RedisDatabase(Connection);
    
        }
    
        public class RedisDatabase
        {
            private Dictionary<int, IDatabase> DataBases = new Dictionary<int, IDatabase>();
            
            public ConnectionMultiplexer RedisConnection { get; }
    
            public RedisDatabase(ConnectionMultiplexer Connection)
            {
                DataBases = new Dictionary<int, IDatabase>{ };
                for(var i=0;i<16;i++)
                {
                    DataBases.Add(i, Connection.GetDatabase(i));
                }
                
                RedisConnection = Connection;
            }
    
            public IDatabase this[int index]
            {
                get
                {
                    if (DataBases.ContainsKey(index))
                        return DataBases[index];
                    else
                       return DataBases[0];
                }
            }
        }
    RedisCache

    Microsoft.Extensions.Caching.StackExchangeRedis

        从nuget doc可知,该组件库依赖于 StackExchange.Redis 客户端; 是.NetCore针对分布式缓存提供的客户端,侧重点在 Redis的缓存特性

    该库是基于 IDistributedCache 接口实现的,该接口为实现分布式缓存的通用性,缓存内容将以byte[] 形式读写
    另外能使用的函数签名也更倾向于【通用的 增、查操作】
    复制代码
    // add Redis cache service 
    services.AddStackExchangeRedisCache(options =>
    {
      options.Configuration = Configuration.GetConnectionString("redis");
      options.InstanceName = "SampleInstance";
    });
    
    // Set Cache Item (by byte[])
     lifetime.ApplicationStarted.Register(() =>
    {
          var currentTimeUTC = DateTime.UtcNow.ToString();
          byte[] encodedCurrentTimeUTC = Encoding.UTF8.GetBytes(currentTimeUTC);
          var options = new DistributedCacheEntryOptions()
                .SetSlidingExpiration(TimeSpan.FromMinutes(20));
          cache.Set("cachedTimeUTC", encodedCurrentTimeUTC, options);
    });  
    
    // Retrieve Cache Item
    [HttpGet]
    [Route("CacheRedis")]
    public async Task<string> GetAsync()
    {
      var ret = "";
      var bytes = await _cache.GetAsync("cachedTimeUTC");
       if (bytes != null)
       {
          ret = Encoding.UTF8.GetString(bytes);
          _logger.LogInformation(ret);
       }
       return  await Task.FromResult(ret);
    }
    复制代码

    ① 很明显,该Cache组件并不能做到自由切换 Redis DB, 目前可在redis连接字符串一次性配置项目要使用哪个Redis DB

    ② 会在指定DB(默认为0)生成key = SampleInstancecachedTimeUTC 的redis缓存项

    ③ Redis并不支持bytes[] 形式的存储值,以上byte[] 实际是以Hash的形式存储

    CSRedisCore

    该组件是基于连接池模型,默认配置会预热50个redis连接。 功能更好灵活,针对实际Redis应用场景有更多玩法

    - 普通模式

    - 官方集群模式 redis cluster

    - 分区模式(作者实现)

    普通模式使用方法极其简单,这里要提示的是: 该客户端也不支持 随意切换 Redis DB, 但是原作者给出一种缓解的方式:构造多客户端。
    复制代码
    var redisDB = new CSRedisClient[16];                        // 多客户端
    for (var a = 0; a < redisDB.Length; a++)
      redisDB[a] = new CSRedisClient(Configuration.GetConnectionString("redis") + ",defaultDatabase=" + a);
    services.AddSingleton(redisDB);
    // ----------------------------
    _redisDB[0].IncrByAsync("ProfileUsageCap", -1)
    _redisDB[0].HGetAsync(profileUsage, eqidPair.ProfileId.ToString())
    _redisDB[0].HIncrByAsync(profileUsage, eqidPair.ProfileId.ToString(), -1);
    复制代码

     内置的静态操作类RedisHelper, 与Redis-Cli 命令完全一致, 故他能原生支持”blocking pops”。

    // 实现后台服务,持续消费MQ消息
    public class BackgroundJob : BackgroundService
        {
            private readonly CSRedisClient[] _redisDB;
            private readonly IConfiguration _conf;
            private readonly ILogger _logger;
            public BackgroundJob(CSRedisClient[] csRedisClients,IConfiguration conf,ILoggerFactory loggerFactory)
            {
                _redisDB = csRedisClients;
                _conf = conf;
                _logger = loggerFactory.CreateLogger(nameof(BackgroundJob));
            }
            
            //  Background 需要实现的后台任务
            protected override async Task ExecuteAsync(CancellationToken stoppingToken)
            {
                _redisDB[0] = new CSRedisClient(_conf.GetConnectionString("redis") + ",defualtDatabase=" + 0);
                RedisHelper.Initialization(_redisDB[0]);
    
                while (!stoppingToken.IsCancellationRequested)
                {
                    var key = $"eqidpair:{DateTime.Now.ToString("yyyyMMdd")}";
                    // 阻塞式从右侧读取List首消息 
                    var eqidpair = RedisHelper.BRPop(5, key);
                    // TODO Handler Message
                    else
                        await Task.Delay(1000, stoppingToken);
                }
            }
        }
    
    -----RedisMQ 生产者---
    //  将一个或多个msg插入List头部
    RedisHelper.LPush(redisKey, eqidPairs.ToArray());
    简单有效RedisMQ

    Redis的一点小经验:

    • 对自己要使用的Redis API 的时间复杂度心里要有数,尽量不要使用长时间运行的命令如keys *,可通过redis.io SlowLog命令观测 哪些命令耗费较长时间

    • Redis Key可按照“:”分隔定义成有业务意义的字符串,如NewUsers:201909:666666(某些Redis UI可直观友好查看该业务)

    • 合适确定Key-Value的大小:Redis对于small value更友好, 如果值很大,考虑划分到多个key

    • 关于缓存穿透,面试的时候会问,自行搜索布隆过滤器。

    • redis虽然有持久化机制,但在实际中会将key-value 持久化到关系型数据库,因为对于某些结构化查询,SQL更为有效。

    ----- 20190829 多说两句-------

    以上三大客户端,Microsoft.Extensions.Caching.StackExchangeRedis 与其他两者的定位还是有很大差距的,单纯使用Redis 缓存特性, 有微软出品,必属精品情结的可使用此客户端;

    StackExchange.Redis、CSRedisCore 对于Redis全功能特性支持的比较全,但是我也始终没有解决StackExchange.Redis : RedisTimeoutException 超时的问题,换成CSRedisCore 确实没有出现Redis相关异常。

    ---- 2019-09-25 update--------

    CSRedisCore 挖坑填坑经历

    - 受到公号一些网友的启发,再次使用StackExchange.Redis , 并在redis配置中增加 abortConnect= false(缺省为true,表示不尝试重连),超时问题不再出现。

    转载于:https://www.cnblogs.com/JulianHuang/p/11418881.html

    Node.js的文章,与其相关的
  • 相关阅读:
    poj 1328 Radar Installation (贪心)
    hdu 2037 今年暑假不AC (贪心)
    poj 2965 The Pilots Brothers' refrigerator (dfs)
    poj 1753 Flip Game (dfs)
    hdu 2838 Cow Sorting (树状数组)
    hdu 1058 Humble Numbers (DP)
    hdu 1069 Monkey and Banana (DP)
    hdu 1087 Super Jumping! Jumping! Jumping! (DP)
    必须知道的.NET FrameWork
    使用记事本+CSC编译程序
  • 原文地址:https://www.cnblogs.com/boyYu/p/12458030.html
Copyright © 2011-2022 走看看