第十四節:基於CSRedisCore程序集調用redis各個功能詳解


一. 整體介紹

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. CoreMVC中使用

 前提:通過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. CoreMVC緩存集成

 前提:通過Nuget安裝程序集【Caching.CSRedis】,注冊方式和CoreMVC的默認分布式緩存的方式有點區別,這里更加簡單粗暴,直接注冊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));
            }
View Code

調用

  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

1. 在客戶端測試

 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;    --成功
View Code

調用:

            {

                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("執行完成");

            }

2. 在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");

2. 發布訂閱

簡單了解即可(可參考: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實現

(詳細可參考上面文檔)

3. 緩存相關業務簡化用法

詳見代碼

            {
                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())
                    //});

                }
            }

4. 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"));
            }

 

5. Benchmark性能測試

見官網測試記錄

 

 

 

 

 

 

 

 

 

 

 

 

!

  • 作       者 : Yaopengfei(姚鵬飛)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 聲     明1 : 如有錯誤,歡迎討論,請勿謾罵^_^。
  • 聲     明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM