.net使用CSRedis操做Redis緩存的簡單筆記(新手教程)

0.介紹

.NET Core or .NET Framework 4.0+ client for Redis and Redis Sentinel (2.8) and Cluster. Includes both synchronous and asynchronous clients.html

本文記錄CSRedis在開發過程當中的簡單使用,能夠直接調試樣例源碼。git

1. 參考資料

github https://github.com/2881099/csredisgithub

做者博客 http://www.javashuo.com/article/p-ykiugmwg-dy.htmlredis

Redis Runoob教程 https://www.runoob.com/redis/redis-install.html緩存

2.核心內容

  • 使用心得

1.科學使用緩存併發

  • 從Redis中讀取數據

從Redis中讀取數據須要考慮"數據存在,可是Redis中過時或者未寫入的狀況"這時候就須要根據指定Key先獲取數據再寫入Redis中。dom

  • 將數據寫入Redis

寫入Redis須要增長過時時。增長過時時間的時候能夠將時間隨機,這樣能夠避免緩存在相同時間過時而引起緩存雪崩。async

在高併發的狀況下,若是根據key獲取的數據不存在,也將null保存至Redis中,而非抽象類(AbstractRedisService)中作刪除動做,這樣可避免緩存穿透。ide

我對高併發場景下的緩存使用理解不深,AbstractRedisService抽象類在更新失效值的時候使用了lock,使用lock寫法容易形成堵塞,可是若是不使用lock的話,就會出現重複讀取再寫入Redis的狀況,而且在當前狀況下如何處理緩存擊穿也不是很清楚,但願你們能不吝賜教~高併發

2.在使用CSRedis的時候遇到了多個業務模塊都有相識的代碼,因而抽取了抽象類AbstractRedisService,在業務模塊實現的時候只須要實現RedisGroup 屬性與GetKeyByRedisInputKey、GetValueByKey兩個方法。

3.在實際應用中,有可能會出現程序與Redis服務鏈接不穩定的狀況,若是Redis服務沒有發現問題的話,能夠嘗試使用下面三種方式解決(參考 https://github.com/2881099/csredis/issues)

  • connectTimeout=30000 設置鏈接超時時間

  • tryit=3 設置重試次數

  • preheat=100 預熱鏈接數

  • 初始化RedisHelper

// 初始化RedisHelper
    RedisHelper.Initialization(CSRedisInstance.GetRedis());

    /// <summary>
    /// CSRedisClient 單例
    /// </summary>
    internal class CSRedisInstance
    {
        private static readonly object _lock = new object();
        private static CSRedisClient _csRedis = null;

        private const string ip = "127.0.0.1";
        private const string port = "6379";
        private const string preheat = "100"; // 設置預熱鏈接數
        private const string connectTimeout = "100"; // 設置鏈接超時時間
        private const string tryit = "1"; // 設置重試次數
        private const string prefix = "CSRedisTest."; // 設置前綴
        private static readonly string _connectString = $"{ip}:{port}," +
          $"preheat={preheat},connectTimeout={connectTimeout},tryit={tryit},prefix={prefix}";

        internal static CSRedisClient GetRedis()
        {
            if (_csRedis == null)
            {
                lock (_lock)
                {
                    if (_csRedis == null)
                    {
                        _csRedis = GetCSRedisClient();
                    }
                }
            }

            return _csRedis;
        }

        private static CSRedisClient GetCSRedisClient()
        {
            return new CSRedisClient(_connectString);
        }
    }
  • 業務應用 - 抽象類分享

AbstractRedisService抽象類
/// <summary>
    /// Redis抽象類 - 緩存內容根據Key指定刷新
    /// </summary>
    /// <typeparam name="RedisInputKey">輸入Key值</typeparam>
    /// <typeparam name="RedisValue">Redis保存的Value值</typeparam>
    public abstract class AbstractRedisService<RedisInputKey, RedisValue>
    {
        private readonly static object _lock = new object();
        private const int _expireTime = 3600;

        /// <summary>
        /// 緩存模塊
        /// </summary>
        protected abstract RedisGroup CacheGroup { get; }

        /// <summary>
        /// 根據輸入Key值,返回真正RedisKey
        /// </summary>
        protected abstract string GetKeyByRedisInputKey(RedisInputKey redisInputKey);

        /// <summary>
        /// 根據輸入Key值,獲取對應Value
        /// </summary>
        protected abstract RedisValue GetValueByKey(RedisInputKey redisInputKey);

        public RedisValue GetRedisByRedisInputKey(RedisInputKey redisInputKey)
        {
            if (!RedisControl.UseRedis())
                return default(RedisValue);

            var result = GetRedisValue(redisInputKey);
            // 刷新Redis以後還沒法獲取正確的值,則記錄緣由
            if (result == null)
            {
                // 日誌輸出
            };

            return result;
        }

        public void NoticeRedisUpdateByKey(RedisInputKey redisInputKey)
        {
            try
            {
                UpdateByKey(redisInputKey);
            }
            catch (Exception e)
            {
                // 日誌輸出
            }
        }

        /// <summary>
        /// 有可能沒有Redis服務,則將異常捕捉,並中止使用Redis緩存
        /// </summary>
        private RedisValue GetRedisValue(RedisInputKey redisInputKey)
        {
            RedisValue value = default(RedisValue);
            string key = GetKeyByRedisInputKey(redisInputKey);
            try
            {
                value = GetRedisValueByKey(key);
                if (value != null)
                    return value;

                lock (_lock)
                {
                    value = GetRedisValueByKey(key);
                    if (value == null)
                        UpdateByKey(redisInputKey);
                }

                value = GetRedisValueByKey(key);
            }
            catch (Exception e)
            {
                RedisControl.StopUseRedis();
                // 日誌輸出
            }

            return value;
        }

        private void UpdateByKey(RedisInputKey redisInputKey)
        {
            var key = GetKeyByRedisInputKey(redisInputKey);
            RedisValue value = GetValueByKey(redisInputKey);

            if (value == null) //刪除操做執行更新時,移除掉key
                RedisHelper.Del(key);
            else
                RedisHelper.Set(key, value, _expireTime + 200 * new Random().Next(1, 10));
        }

        private RedisValue GetRedisValueByKey(string key)
        {
            return RedisHelper.Get<RedisValue>(key);
        }
    }
1.StudentRedisService實現類
/// <summary>
    /// Redis Student實現類
    /// </summary>
    public class StudentRedisService : AbstractRedisService<string, Student>,IStudentRedisService
    {
        protected override RedisGroup CacheGroup => RedisGroup.Student;

        protected override string GetKeyByRedisInputKey(string redisInputKey) => redisInputKey;

        protected override Student GetValueByKey(string redisInputKey)
        {
            return Test.AllStudents.Where(v=>v.Name == redisInputKey).FirstOrDefault();
        }
    }
2.使用StudentRedisService
private static void AbstractRedisServiceTest()
{
    var studentRedisService = new StudentRedisService();

    // 一:經過key獲取
    var mark = studentRedisService.GetRedisByRedisInputKey("Mark"); // 有此數據,獲取的時候會寫入Redis
    var linda = studentRedisService.GetRedisByRedisInputKey("Linda"); // 無數據則返回null

    // 二:數據變動通知
    string markName = "Mark";
    // 更新數據
    int random = new Random().Next();
    Test.AllStudents.Where(v => v.Name == markName).First().Age = random;

    var mark2 = studentRedisService.GetRedisByRedisInputKey(markName); // 舊值
    studentRedisService.NoticeRedisUpdateByKey(markName);// 更新Redis
    mark2 = studentRedisService.GetRedisByRedisInputKey(markName);// 新值
}

3.樣例源碼地址

調試Demo能夠先參考Redis Runoob安裝教程,部署Redis服務,再進行調試

https://github.com/Impartsoft/Bins/tree/main/CSRedisDemo/CSRedisDemo

相關文章
相關標籤/搜索