第十四节:基于CSRedisCore程序集调用redis各个功能详解
一. 整体介绍1. 说明CSRedis 是 redis.io 官方推荐库,支持 redis-trib集群、哨兵、私有分区与连接池管理技术,简易 RedisHelper 静态类, 它主要又两个程序集。(1).CSRedisCore:主库,实现对接redis各种功能(2).Caching.CSRedis:分布式缓存 CSRedisCore 实现 Microsoft.Extensions.Caching
一. 整体介绍
1. 说明
CSRedis
是 redis.io
官方推荐库,支持 redis-trib
集群、哨兵、私有分区与连接池管理技术,简易 RedisHelper
静态类, 它主要又两个程序集。
(1).CSRedisCore
:主库,实现对接redis
各种功能
(2).Caching.CSRedis
:分布式缓存 CSRedisCore
实现 Microsoft.Extensions.Caching
相关地址如下:
GitHub地址:https://github.com/2881099/csredis
Nuget地址:https://www.nuget.org/packages/CSRedisCore/
2. 主要特点
(1).调用方法的时候,可以使用CSRedisClient
实例化的对象,也可以使用全局类RedisHelper
(需要Initialization
初始化一下)
注:无论是CSRedisClient
实例化的对象还是RedisHelper
调用的方法和Redis
自身cli
指令名字完全相同,这一点非常好!!
(2).官方推荐配置:CSRedisClient
is singleton, RedisHelper
static class is recommended (CSRedisClient
推荐配置单例模式,RedisHelper
推荐静态)
(3).支持geo
类型(>=3.2)、stream
类型(>=5.0)
(4).支持主从、哨兵、cluster
二. 如何集成
1. 控制台中使用
前提:通过Nuget
安装程序集:CSRedisCore
(1).用法1:直接实例化CSRedisClient
进行使用
(2).用法2:初始化帮助类RedisHelper
帮助类进行使用
代码分享:
{
//用法1-CSRedisClient实例化的对象(生产环境中把CSRedisClient写成单例类)
var rds = new CSRedis.CSRedisClient("119.45.174.xx:6379,password=123456,defaultDatabase=0");
rds.Set("name1", "ypf");
var result1 = rds.Get("name1");
Console.WriteLine($"name1={result1}");
//用法2-RedisHelper帮助类
RedisHelper.Initialization(new CSRedis.CSRedisClient("119.45.174.xx:6379,password=123456,defaultDatabase=0"));
RedisHelper.Set("name2", "ypf2");
var result2 = RedisHelper.Get("name2");
Console.WriteLine($"name2={result2}");
}
ps:CSRedisClient
链接字符串的说明
2. Core MVC中使用
前提:通过Nuget
安装程序集:CSRedisCore
,同样有两种用法
(1).在ConfigureSevice
实例化CSRedisClient
,并注册成单例模式;然后实例化RedisHelper
帮助类
(2).在action
如果使用CSRedisClient
,需要注入后再使用
(3).再action
如果使用RedisHelper
帮助类,可以直接使用,不需要注册
代码分享:
配置文件
"RedisStr": "119.45.174.xx:6379,password=123456,defaultDatabase=0"
ConfigureService
:
public void ConfigureServices(IServiceCollection services)
{
//1. 集成Redis的两种方式
{
//用法1-CSRedisClient实例化的对象
var rds = new CSRedis.CSRedisClient(Configuration["RedisStr"]);
services.AddSingleton(rds); //注册成全局单例
//用法2-RedisHelper帮助类
RedisHelper.Initialization(rds);
}
services.AddControllersWithViews();
}
调用:
public class HomeController : Controller
{
private CSRedisClient _csredis;
public HomeController(CSRedisClient csredis)
{
this._csredis = csredis;
}
public IActionResult Index()
{
#region 01-如何集成
//{
// //1. 用法1,需要注入
// _csredis.Set("name1", "ypf11");
// var result1 = _csredis.Get("name1");
// Console.WriteLine($"name1={result1}");
// //2. 用法2,直接使用RedisHelp类即可
// RedisHelper.Set("name2", "ypf22");
// var result2 = RedisHelper.Get("name2");
// Console.WriteLine($"name2={result2}");
//}
#endregion
return View();
}
}
3. Core MVC缓存集成
前提:通过Nuget
安装程序集【Caching.CSRedis
】,注册方式和Core MVC的默认分布式缓存的方式有点区别,这里更加简单粗暴,直接注册IDistributedCache
对象即可
(1).在ConfigureService
中注册IDistributedCache
单例对象
(2).在控制器中注入进行使用即可,包含的方法默认修改的模式相同
代码分享
ConfigureService
//2. 注册基于Redis的分布式缓存
{
var csredis = new CSRedis.CSRedisClient(Configuration["RedisStr"]);
services.AddSingleton<IDistributedCache>(new Microsoft.Extensions.Caching.Redis.CSRedisCache(csredis));
//集群模式
// var csredis = new CSRedis.CSRedisClient(null,
// "127.0.0.1:6371,pass=123,defaultDatabase=11,poolsize=10,ssl=false,writeBuffer=10240,prefix=key前辍",
// "127.0.0.1:6372,pass=123,defaultDatabase=12,poolsize=11,ssl=false,writeBuffer=10240,prefix=key前辍",
// "127.0.0.1:6373,pass=123,defaultDatabase=13,poolsize=12,ssl=false,writeBuffer=10240,prefix=key前辍",
// "127.0.0.1:6374,pass=123,defaultDatabase=14,poolsize=13,ssl=false,writeBuffer=10240,prefix=key前辍");
// services.AddSingleton<IDistributedCache>(new Microsoft.Extensions.Caching.Redis.CSRedisCache(csredis));
}
调用
public class HomeController : Controller
{
private IDistributedCache _dCache;
public HomeController(IDistributedCache dCache)
{
this._dCache = dCache;
}
public IActionResult Index()
{
#region 02-缓存集成
{
_dCache.SetString("lmr", "1234567");
var data = _dCache.GetString("lmr");
}
#endregion
return View();
}
}
4.分享一种封装模式(推荐)
(1). 配置文件中声明缓存类型和链接字符串
(2). 新建CacheExtension
类进行不同类型的缓存初始化(redis
自身初始化和redis
缓存依赖初始化)
(3). 在Program
中进行UseCache
(4). 进行测试
代码分享:
配置文件:
//缓存类型
//有两种取值 (Redis:代表使用redis缓存, 并实例化redis相关对象. Memory:代表使用服务端缓存)
"CacheType": "Redis",
"RedisStr": "119.45.174.xxx:6379,password=123456,defaultDatabase=0"
策略类:
/// <summary>
/// 缓存扩展
/// </summary>
public static class CacheExtension
{
/// <summary>
/// 使用缓存
/// </summary>
/// <param name="hostBuilder">建造者</param>
/// <returns></returns>
public static IHostBuilder UseCache(this IHostBuilder hostBuilder)
{
hostBuilder.ConfigureServices((buidlerContext, services) =>
{
var CacheType = buidlerContext.Configuration["CacheType"].ToString();
switch (CacheType)
{
case "Memory": services.AddDistributedMemoryCache(); break;
case "Redis":
{
//初始化redis的两种使用方式
var csredis = new CSRedisClient(buidlerContext.Configuration["RedisStr"].ToString());
services.AddSingleton(csredis);
RedisHelper.Initialization(csredis);
//初始化缓存基于redis
services.AddSingleton<IDistributedCache>(new CSRedisCache(csredis));
}; break;
default: throw new Exception("缓存类型无效");
}
});
return hostBuilder;
}
}
Program:
调用:同上面的调用类似
三. 通用指令、数据类型测试
1. 通用指令
{
Console.WriteLine("通用指令测试");
//1.获取所有key
var d1 = RedisHelper.Keys("*");
//2. 获取部分key
var d2 = RedisHelper.Scan(0, "*", 2);
//3. 获取过期时间
var d3 = RedisHelper.Ttl("name1");
//4. 删除key
var d4 = RedisHelper.Del("name1");
//5. 判断key是否存在
var d5 = RedisHelper.Exists("name1");
//6. 设置过期时间
var d6 = RedisHelper.Expire("name2", 60);
Console.WriteLine("执行完成");
}
2. String
类型
{
Console.WriteLine("String类型测试");
RedisHelper.Set("name1", "lmr2"); //默认是不过期的
RedisHelper.Set("name2", "lmr2", 30); //30s过期
RedisHelper.IncrBy("count1", 2); //每次自增2
RedisHelper.IncrBy("count2", -1); //每次自减1
Console.WriteLine("执行完成");
}
3. Hash
类型
{
Console.WriteLine("Hash类型测试");
RedisHelper.HSet("myhash", "userName", "ypf");
RedisHelper.HSet("myhash", "userAge", 20);
RedisHelper.HIncrBy("myhash", "count", 1); //每次自增1
//获取key下的所有内容
var data1 = RedisHelper.HGetAll("myhash");
//指定key-filed
var dataValue = RedisHelper.HGet<int>("myhash", "userAge");
Console.WriteLine("执行完成");
}
4. List
类型
{
Console.WriteLine("List类型测试");
//左侧插入
RedisHelper.LPush("myList", "001");
RedisHelper.LPush("myList", "002");
RedisHelper.LPush("myList", "003");
//右侧插入
RedisHelper.RPush("myList", "004");
RedisHelper.RPush("myList", "005");
RedisHelper.RPush("myList", "006");
//左侧删除并返回该元素
var d1 = RedisHelper.LPop("myList");
//右侧删除并返回该元素
var d2 = RedisHelper.RPop("myList");
//获取指定范围的元素
var data1 = RedisHelper.LRange("myList", 0, 2); //左侧开始,获取前三个元素
var data2 = RedisHelper.LRange("myList", 0, -1); //左侧开始,获取所有
Console.WriteLine("执行完成");
}
5. Set
类型
{
Console.WriteLine("Set类型测试");
//增加
RedisHelper.SAdd("school", "a");
RedisHelper.SAdd("school", "b");
RedisHelper.SAdd("school", "c");
RedisHelper.SAdd("school", "d");
RedisHelper.SAdd("school", "e");
//删除
RedisHelper.SIsMember("school", "b");
//获取
var data1 = RedisHelper.SMembers("school");
//随机获取(不删除)
var data2 = RedisHelper.SRandMember("school");
Console.WriteLine("执行完成");
}
6. SortedSet
类型
{
Console.WriteLine("SortedSet类型测试");
//1. 增加
RedisHelper.ZAdd("mySort", (20, "s1"), (20, "s2"), (20, "s3"), (50, "s4"), (80, "s8"));
//2. 自增/自减
RedisHelper.ZIncrBy("mySort", "s1", 1);
RedisHelper.ZIncrBy("mySort", "s2", -2);
RedisHelper.ZIncrBy("mySort", "s3", -3);
RedisHelper.ZIncrBy("mySort", "s4", 10);
//3. 获取
var d1 = RedisHelper.ZRange("mySort", 0, 2);
var d2 = RedisHelper.ZRange("mySort", 0, -1); //所有元素
//4.按照score排序获取
var s1 = RedisHelper.ZRevRange("mySort", 0, -1); //从高到低排序
//5. 删除
RedisHelper.ZRem("mySort", "s1");
Console.WriteLine("执行完成");
}
7. Geo
类型
{
Console.WriteLine("Geo类型测试");
//1. 添加地点经纬度
RedisHelper.GeoAdd("myLocation", Convert.ToDecimal(116.20), Convert.ToDecimal(39.56), "北京");
RedisHelper.GeoAdd("myLocation", Convert.ToDecimal(120.51), Convert.ToDecimal(30.40), "上海");
//2. 求两点之间的距离
var d1 = RedisHelper.GeoDist("myLocation", "北京", "上海", GeoUnit.km);
Console.WriteLine("执行完成");
}
四. 集群测试
1. 主从
2. 哨兵
下面地址是哨兵的地址
var csredis = new CSRedis.CSRedisClient("mymaster,password=123,prefix=my_", new [] { "192.169.1.10:26379", "192.169.1.11:26379", "192.169.1.12:26379" });
3. Redis Cluster
(1).环境准备
6个redis
实例,建立redis-cluster
,地址分别为:192.168.137.202:6379
(端口依次次6379-6384
),主从关系和节点槽位分配,见下图
(2).写法1
写任意个地址即可,其他节点在运行过程中自动增加,确保每个节点密码一致。(原理:它会根据 redis-server
返回的 MOVED | ASK
错误记录slot
,自动增加节点 Nodes
属性。)
如下测试:name1
和name2
位于不同redis
服务器,写入成功。
{
Console.WriteLine("集群测试");
RedisHelper.Initialization(new CSRedis.CSRedisClient("192.168.137.202:6379,password=123456,defaultDatabase=0"));
RedisHelper.Set("name1", "ypf1"); //对应的槽位在6384端口上
RedisHelper.Set("name2", "ypf2"); //对应的槽位在6379端口上
var data1 = RedisHelper.Get<String>("name1");
var data2 = RedisHelper.Get<String>("name2");
Console.WriteLine($"data1={data1}");
Console.WriteLine($"data2={data2}");
Console.WriteLine("执行完毕");
}
注意:这个写法与【分区模式】同时使用时,切记不可设置“prefix=key前辍
”(或者全部设置成一样),否则会导致 keySlot
计算结果与服务端不匹配,无法记录 slotCache
。
(3).写法2
把所有地址都列进去。
如下测试:name1
和name2
位于不同redis
服务器,写入成功
{
Console.WriteLine("集群测试");
var csredis = new CSRedis.CSRedisClient(null,
"192.168.137.202:6379,password=123456,defaultDatabase=0",
"192.168.137.202:6380,password=123456,defaultDatabase=0",
"192.168.137.202:6381,password=123456,defaultDatabase=0",
"192.168.137.202:6382,password=123456,defaultDatabase=0",
"192.168.137.202:6383,password=123456,defaultDatabase=0",
"192.168.137.202:6384,password=123456,defaultDatabase=0");
RedisHelper.Initialization(csredis);
RedisHelper.Set("name1", "ypf1"); //对应的槽位在6384端口上
RedisHelper.Set("name2", "ypf2"); //对应的槽位在6379端口上
var data1 = RedisHelper.Get<String>("name1");
var data2 = RedisHelper.Get<String>("name2");
Console.WriteLine($"data1={data1}");
Console.WriteLine($"data2={data2}");
Console.WriteLine("执行完毕");
}
特别注意:官方集群(redis-cluster
)不支持多 keys
的命令、【管道】、Eval(脚本)等众多杀手级功能。
五. Lua
测试
lua
相关介绍参考:https://www.cnblogs.com/yaopengfei/p/13941841.html
https://www.cnblogs.com/yaopengfei/p/13826478.html
- 在客户端测试
A. 把Test1.lua
文件改为“始终复制”.
B. 方案1:将lua
脚本通过 ScriptLoad
转换成sha
, 然后在通过 EvalSHA
调用.
C. 方案2:直接通过Eval
调用lua
脚本
PS:方案1中sha
会存放到redis
的缓存中,客户端多次调用相比每次都发送lua
脚本给redis
来说节省带宽。
代码分享:
Lua脚本:
--[[
一. 方法声明
]]--
--1. 方法幂等(防止网络延迟多次下单)
local function recordOrderSn()
--(1).获取相关参数
local requestId = ARGV[1]; --请求ID
--(2).执行判断业务
local requestIdNum = redis.call('INCR',requestId);
--表示第一次请求
if (requestIdNum==1)
then
redis.call('expire',requestId,600) --10min过期
return 1; --成功
end;
--第二次及第二次以后的请求
if (requestIdNum>1)
then
return 0; --失败
end;
end; --对应的是整个代码块的结束
--[[
二. 方法调用 返回值1代表成功,返回:0代表失败
]]--
--1. 方法幂等
local status3 = recordOrderSn();
if status3 == 0 then
return 0; --失败
end
return 1; --成功
调用:
{
Console.WriteLine("Lua客户端测试");
RedisHelper.Initialization(new CSRedis.CSRedisClient("119.45.174.249:6379,password=123456,defaultDatabase=0"));
FileStream fileStream1 = new FileStream(@"Luas/Test1.lua", FileMode.Open);
string script1 = "";
string luaSha1 = "";
using (StreamReader reader = new StreamReader(fileStream1))
{
script1 = reader.ReadToEnd();
//将lua脚本转换成sha,同时redis会将其存到脚本缓存中
luaSha1 = RedisHelper.ScriptLoad(script1);
}
//方案1:调用lua对应sha值(这里testKey1没有特别作用)----推荐
var d1 = RedisHelper.EvalSHA(luaSha1, "testkey1", "12345-1");
Console.WriteLine($"d1={d1}");
//方案2:直接调用lua脚本(每次都发脚本给redis,浪费带宽,推荐上面的sha模式)
var d2 = RedisHelper.Eval(script1, "testkey2", "12345-2");
Console.WriteLine($"d2={d2}");
//清空缓存中的所有脚本
//RedisHelper.ScriptFlush();
//杀死正在运行的脚本
//RedisHelper.ScriptKill();
Console.WriteLine("执行完成");
}
- 在CoreMvc中测试
直接演示sha
的那种模式,调用方式完全类似,但在web
程序中,我们可以在项目启动的时候就把所有的脚本都事先转换成lua
存放到服务器缓存中,后续直接调用即可.
后台任务代码:
/// <summary>
/// 后台任务,初始化lua文件到服务器缓存中
/// </summary>
public class LuasLoadService : BackgroundService
{
private IMemoryCache _cache;
public LuasLoadService(IMemoryCache cache)
{
_cache = cache;
}
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
FileStream fileStream1 = new FileStream(@"Luas/Test1.lua", FileMode.Open);
using (StreamReader reader = new StreamReader(fileStream1))
{
string line = reader.ReadToEnd();
string luaSha = RedisHelper.ScriptLoad(line);
//保存到缓存中
_cache.Set<string>("Test1Lua", luaSha);
}
return Task.CompletedTask;
}
}
注入和调用:
{
services.AddHostedService<LuasLoadService>();
}
六. 其他
1.多个db
的用法
(1).多个CSRedisClient
实例
//1.多个CSRedisClient实例
var connectionString = "119.45.174.xx:6379,password=123456";
var redis = new CSRedisClient[14]; //生产中设置成Singleton
for (var a = 0; a < redis.Length; a++)
{
redis[a] = new CSRedisClient(connectionString + ",defaultDatabase=" + a);
};
redis[1].Set("cs1", "111");
redis[2].Set("cs1", "111");
(2).多个RedisHelper
子类
public class MyHelper1 : RedisHelper<MyHelper1>
{
}
public class MyHelper2 : RedisHelper<MyHelper2>
{
}
调用:
MyHelper1.Initialization(new CSRedisClient("119.45.174.249:6379,password=123456,defaultDatabase=1"));
MyHelper2.Initialization(new CSRedisClient("119.45.174.249:6379,password=123456,defaultDatabase=2"));
MyHelper1.Set("cs2", "222");
MyHelper2.Set("cs2", "222");
- 发布订阅
简单了解即可(可参考:https://www.cnblogs.com/kellynic/p/9952386.html), 专业场景推荐使用RabbitMQ
(1). 普通的发布订阅
利用Subscribe
和Publish
方法
{
Console.WriteLine("发布订阅");
RedisHelper.Initialization(new CSRedis.CSRedisClient("119.45.174.249:6379,password=123456,defaultDatabase=0"));
//程序1:使用代码实现订阅端
var sub = RedisHelper.Subscribe(("chan1", msg => Console.WriteLine(msg.Body)));
//sub.Disponse(); //停止订阅
//程序2:使用代码实现发布端
RedisHelper.Publish("chan1", "111");
//下面了解即可
//{ //sub1, sub2 争抢订阅(只可一端收到消息)
// var sub1 = RedisHelper.SubscribeList("list1", msg => Console.WriteLine($"sub1 -> list1 : {msg}"));
// var sub2 = RedisHelper.SubscribeList("list1", msg => Console.WriteLine($"sub2 -> list1 : {msg}"));
// //sub3, sub4, sub5 非争抢订阅(多端都可收到消息)
// var sub3 = RedisHelper.SubscribeListBroadcast("list2", "sub3", msg => Console.WriteLine($"sub3 -> list2 : {msg}"));
// var sub4 = RedisHelper.SubscribeListBroadcast("list2", "sub4", msg => Console.WriteLine($"sub4 -> list2 : {msg}"));
// var sub5 = RedisHelper.SubscribeListBroadcast("list2", "sub5", msg => Console.WriteLine($"sub5 -> list2 : {msg}"));
//}
}
(2). 利用BLPOP+LPUSH
实现
(详细可参考上面文档)
- 缓存相关业务简化用法
详见代码
{
Console.WriteLine("缓存相关业务简化封装");
RedisHelper.Initialization(new CSRedis.CSRedisClient("119.45.174.249:6379,password=123456,defaultDatabase=0"));
List<string> strList = new List<string>() { "ypf1", "ypf2", "ypf3" }; //模拟db
//常规写法:先去缓存中找,有数据直接返回,没有数据去查db,然后给缓存赋值,最后返回
//{
// var data = RedisHelper.Get<string>("userName");
// if (string.IsNullOrEmpty(data))
// {
// //从db中查找
// data = strList.FirstOrDefault();
// RedisHelper.Set("userName", data, 100); //100s过期
// Console.WriteLine($"data={data}");
// }
// else
// {
// //直接将缓存中的数据返回
// Console.WriteLine($"data={data}");
// }
//}
//封装的写法
{
//下面两行等价于上面的一坨代码
var data = RedisHelper.CacheShell("userName", 100, () => strList.FirstOrDefault()); //100s过期
Console.WriteLine($"data={data}");
//另外还支持hash类型
//var t2 = RedisHelper.CacheShell("test1", "1", 100, () => strList.FirstOrDefault());
//var t3 = RedisHelper.CacheShell("test2", new[] { "1", "2" }, 10, notCacheFields => new[] {
// ("1", strList.FirstOrDefault()),
// ("2", strList.FirstOrDefault())
//});
}
}
PipeLine
管道
Redis
管道技术可以在服务端未响应时,客户端可以继续向服务端发送请求,并最终一次性读取所有服务端的响应。
{
RedisHelper.Initialization(new CSRedis.CSRedisClient("119.45.174.249:6379,password=123456,defaultDatabase=0"));
//一次性返回所有请求结果
var data = RedisHelper.StartPipe(p => p.Set("userName1", "ypf1").Get("userName1").Set("userName2", "ypf2").Get("userName2"));
}
Benchmark
性能测试
见官网测试记录
https://www.cnblogs.com/yaopengfei/p/14211883.html
更多推荐
所有评论(0)