前言

  稍微复杂一点的互联网项目,技术选型都可能会涉及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))
// 对应redis api: HGET KEY field1
_redisDB0.HashGetAsync(profileUsage, eqidPair.ProfileId))       
// 对应redis哈希自增api:  HINCRBY myhash field -1
_redisDB0.HashDecrementAsync(profileUsage, eqidPair.ProfileId, )
  • 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=;i<;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[];
}
}
}

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());
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[];                        // 多客户端
for (var a = 0; a < redisDB.Length; a++)
  redisDB[a] = new CSRedisClient(Configuration.GetConnectionString("redis") + ",defaultDatabase=" + a);
services.AddSingleton(redisDB);
// ----------------------------
_redisDB[].IncrByAsync("ProfileUsageCap", -)
_redisDB[].HGetAsync(profileUsage, eqidPair.ProfileId.ToString())
_redisDB[].HIncrByAsync(profileUsage, eqidPair.ProfileId.ToString(), -);

内置的静态操作类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[] = new CSRedisClient(_conf.GetConnectionString("redis") + ",defualtDatabase=" + );
RedisHelper.Initialization(_redisDB[]); while (!stoppingToken.IsCancellationRequested)
{
var key = $"eqidpair:{DateTime.Now.ToString("yyyyMMdd")}";
// 阻塞式从右侧读取List首消息
var eqidpair = RedisHelper.BRPop(, key);
// TODO Handler Message
else
await Task.Delay(, 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,表示不尝试重连),超时问题不再出现。

.Net Core 三大Redis客户端对比和使用心得的更多相关文章

  1. ASP.NET Core 使用 Redis 客户端

    Mac OS 安装 Redis(用于连 Redis 服务器,方便查看数据):https://redis.io/topics/quickstart wget http://download.redis. ...

  2. Redis 客户端 Jedis、lettuce 和 Redisson 对比

    Redis 支持多种语言的客户端,下面列举了部分 Redis 支持的客户端语言,大家可以通过官网查看 Redis 支持的客户端详情. C语言 C++ C# Java Python Node.js PH ...

  3. Redis客户端管理

    1.客户端管理 Redis提供了客户端相关API对其状态进行监控和管理,本节将深入介绍各个API的使用方法以及在开发运维中可能遇到的问题. 1.1 客户端API 1.client list clien ...

  4. 初探 Redis 客户端 Lettuce:真香!

    一.Lettuce 是啥? 一次技术讨论会上,大家说起 Redis 的 Java 客户端哪家强,我第一时间毫不犹豫地喊出 "Jedis, YES!" "Jedis 可是官 ...

  5. Ignite性能测试以及对redis的对比

    测试方法 为了对Ignite做一个基本了解,做了一个性能测试,测试方法也比较简单主要是针对client模式,因为这种方法和使用redis的方式特别像.测试方法很简单主要是下面几点: 不作参数优化,默认 ...

  6. Redis客户端Java服务接口封装

    最近在学习Redis并集成到Spring中去,发现Spring的RedisTemplate并不好用,还没有MongoTemplate好用. 而且发现Jedis和ShardedJedis的方法非常多,覆 ...

  7. "Redis客户端连接数一直降不下来"的有关问题解决

    [线上问题] "Redis客户端连接数一直降不下来"的问题解决 前段时间,上线了新的 Redis缓存(Cache)服务,准备替换掉 Memcached. 为什么要将 Memcach ...

  8. .net core 使用Redis的发布订阅

    Redis是一个性能非常强劲的内存数据库,它一般是作为缓存来使用,但是他不仅仅可以用来作为缓存,比如著名的分布式框架dubbo就可以用Redis来做服务注册中心.接下来介绍一下.net core 使用 ...

  9. Redis客户端——Jedis的使用

    本文介绍基于Java语言的Redis客户端——Jedis的使用,包括Jedis简介.获取Jedis.Jedis直连.Jedis连接池以及二者的对比的选择. Jedis简介 Jedis 是 Redis  ...

随机推荐

  1. Windows 设置自启动计划任务(非登录启动)

    原因:服务器会不定期重启,且重启后无人看管,不会有人去登录系统.导致我们做的一些开机启动程序失效,进而系统瘫痪. 解决方法: 自己理解,想要达到目的有两种方式:系统服务 & 计划任务配置. 计 ...

  2. python列表、元组、字典练习题

    1.元素分类 有如下值集合[11,22,33,44,55,66,77,88,99,90], 将所有大于66的值保存至字典的第一个key中,将小于66值保存至第二个key的值中. li = [11,22 ...

  3. swift对象存储

    swift对象存储 简介 OpenStack Object Storage(Swift)是OpenStack开源云计算项目的子项目之一,被称为对象存储,提供了强大的扩展性.冗余和持久性.对象存储,用于 ...

  4. HTTP,TCP,IP详解

    互联网协议包含了上百种协议标准,但是最重要的两个协议是TCP和IP协议,所以,大家把互联网的协议简称TCP/IP协议. 通信的时候,双方必须知道对方的标识,好比发邮件必须知道对方的邮件地址.互联网上每 ...

  5. Spring Boot 面试的十个问题

    用下面这些常见的面试问题为下一次 Spring Boot 面试做准备. 在本文中,我们将讨论 Spring boot 中最常见的10个面试问题.现在,在就业市场上,这些问题有点棘手,而且趋势日益严重. ...

  6. 温故而知新,重温 Java 7 的那些“新”特性

    2009 年 4 月 20 日,Java 的亲生父亲 Sun 被养父 Oracle 以 74 亿美元收购,这在当时可是一件天大的事.有不少同学都担心 Java 的前途,我当时傻不啦叽地也很担心:自己刚 ...

  7. R语言学习笔记——C#中如何使用R语言setwd()函数

    在R语言编译器中,设置当前工作文件夹可以用setwd()函数. > setwd("e://桌面//")> setwd("e:\桌面\")> s ...

  8. vscode vue开发环境搭建

    以前仅了解过VUE但没有真正上手过,现在因为工作需要准备再近几个月里系统的学习一下这款超火的前端框架,希望大佬们指教. ---------------------------------------- ...

  9. 【Docker】unauthorized: incorrect username or password

    昨天朋友推荐玩玩 Docker.虽然之前就听过,但一直不清楚干嘛的,也没去搞过,虽然前段时间就装了,但一直没打开.这两天刚开始熟悉,就遇到了点小问题.一番 Google 之后解决了,记录一下. CLI ...

  10. .NET Core on K8S学习实践系列文章索引(Draft版)

    一.关于这个系列 自从去年(2018年)底离开工作了3年的M公司加入X公司之后,开始了ASP.NET Core的实践,包括微服务架构与容器化等等.我们的实践是渐进的,当我们的微服务数量到了一定值时,发 ...