.Net Core緩衝組件(Redis)源碼解析

來源:互聯網
上載者:User

  上一篇文章已經介紹了MemoryCache,MemoryCache儲存的資料類型是Object,也說了Redis支援五中資料類型的儲存,但是微軟的Redis緩衝組件只實現了Hash類型的儲存。在分析源碼之前,先學幾個關於Redis操作的命令。

一、Redis命令

  Redis所有的命令在http://doc.redisfans.com/上有詳細介紹。下面介紹幾個常用的關於Hash類型的命令。

  HSET:用於添加緩衝

    用法:HSET key field value 。

    傳回值:如果 field 是雜湊表中的一個建立域,並且值設定成功,返回 1 。

        如果雜湊表中域 field 已經存在且舊值已被新值覆蓋,返回 0 。    例如:HSET user Name Microheart       HSET user Age 18  HMSET:用於同時添加多個

    用法:HMSET key [field value field1 value1 ...]

    傳回值:如果命令執行成功,返回 OK 。

        當 key 不是雜湊表(hash)類型時,返回一個錯誤。    例如:HMSET user1 Name Microheart Age 18      HGET:擷取欄位值

    用法:HGET key field  

    傳回值:給定域的值。

        當給定域不存在或是給定 key 不存在時,返回 nil 

    例如:HGET user Name (注意 Redis區分大小寫)

    

  HMGET:擷取多個欄位的值

    用法:HMGET key [field1,field2]

    傳回值:一個包含多個給定域的關聯值的表,表值的排列順序和給定域參數的請求順序一樣。

    例如:HMGET user Name Age

    

  EXPIRE:設定緩衝的到期時間

    用法:EXPIRE key seconds

    傳回值:設定成功返回 1 。

        當 key 不存在或者不能為 key 設定存留時間時(比如在低於 2.1.3 版本的 Redis 中你嘗試更新 key 的存留時間),返回 0 。    例如:EXPIRE user 60      TTL:表示剩餘存留時間。57表示還有57秒這個緩衝到期。到期後,Redis會自動刪除。在 Redis 2.4 版本中,到期時間的延遲在 1 秒鐘之內 —— 也即是,就算 key 已經到期,但它還是可能在到期之後一秒鐘之內被訪問到,而在新的 Redis 2.6 版本中,延遲被降低到 1 毫秒之內。二、在.Net Core中使用Redis組件

  首先在Startup類中添加Redis緩衝功能。配置的Option中設定的InstanceName的值會作為key的一部分。比如設定的InstanceName為test,代碼中設定一個緩衝key為user,儲存到Redis中的實際key為testuser。

public void ConfigureServices(IServiceCollection services){      services.AddMvc();      services.AddDistributedRedisCache(option =>      {           option.Configuration = "121.41.55.55:6379";//連接字串           option.InstanceName = "test";      });}

然後在需要使用的地方注入IDistributedCache。如下面所示:

public class ValuesController : Controller{        private readonly IDistributedCache redisCache;        public ValuesController(IDistributedCache redisCache)        {            this.redisCache = redisCache;        }        [HttpGet]        public IEnumerable<string> Get()        {            redisCache.SetAsync("key1", Encoding.UTF8.GetBytes("value1"), new DistributedCacheEntryOptions()            {                AbsoluteExpiration = DateTime.Now.AddSeconds(10)//設定到期時間,時間一到緩衝立刻就被移除了            });            redisCache.SetString("key2", "value2");//沒有設定緩衝到期時間,表示是永久緩衝            return new string[] { "value1", "value2" };        }}
 三、源碼解析

  源碼在github.com/aspnet/Caching,Redis的源碼相對簡單,主要是因為很多都直接使用的StackExchange.Redis的API。

RedisCacheOptions類:主要是Redis配置相關。

  Configuration:設定Redis配置,如連接字串、逾時時間等,最終被裝換為StackExchange.Redis中的ConfigurationOptions

  InstanceName:執行個體名稱。會和代碼中設定的key拼接成為Redis中的key。

RedisCacheServiceCollectionExtensions類:跟服務注入相關。

  就一個方法AddDistributedRedisCache,依賴注入IDistributedCache的執行個體。

        public static IServiceCollection AddDistributedRedisCache(this IServiceCollection services, Action<RedisCacheOptions> setupAction)        {            if (services == null)            {                throw new ArgumentNullException(nameof(services));            }            if (setupAction == null)            {                throw new ArgumentNullException(nameof(setupAction));            }            services.AddOptions();            services.Configure(setupAction);            services.Add(ServiceDescriptor.Singleton<IDistributedCache, RedisCache>());//注入一個單例            return services;        }
RedisCache類:最主要的類,快取作業相關的類。

  其中插入、擷取資料的方法比較重要。

3.1 Set方法,插入資料。
public void Set(string key, byte[] value, DistributedCacheEntryOptions options)      
{  //省略一些邏輯判斷 Connect(); var creationTime = DateTimeOffset.UtcNow;       //對於一個緩衝可以設定為絕對到期時間,相對於現在時間的到期時間和滑動到期時間三種(上一篇文章有例子),其實前兩種時間類型可以相互轉換。
       //下面這一步就是 如果設定了絕對到期時間或者相對於現在時間的到期時間,裝換為絕對到期時間 var absoluteExpiration = GetAbsoluteExpiration(creationTime, options);       //調用了StackExchange.Redis的API 插入緩衝 var result = _cache.ScriptEvaluate(SetScript, new RedisKey[] { _instance + key },//這裡的key是執行個體名稱+key=Redis中的key,當然我們在尋找緩衝的時候,並不需要我們手動拼接,只需要傳我們複製的key,不需要執行個體名稱
       new RedisValue[] { absoluteExpiration?.Ticks ?? NotPresent, options.SlidingExpiration?.Ticks ?? NotPresent,
               //如果對於一個緩衝同時設定了絕對到期時間和滑動到期時間,則取即將到期的時間,也就是最小的那個時間。 GetExpirationInSeconds(creationTime, absoluteExpiration, options) ?? NotPresent, value }); }
上面的添加緩衝中,使用了指令碼插入。
private const string SetScript = (@" redis.call('HMSET', KEYS[1], 'absexp', ARGV[1], 'sldexp', ARGV[2], 'data', ARGV[4])//設定key、絕對到期時間、滑動到期時間、和value的值 if ARGV[3] ~= '-1' then redis.call('EXPIRE', KEYS[1], ARGV[3])//設定緩衝的時間 end return 1");

  如果absexp和sldexp都沒有設定值,預設為-1,表示永不到期,緩衝時間就是從設定的絕對到期時間和滑動到期時間中取,當時間到了,Redis自動刪除到期緩衝,這一點和MemoryCache不一樣,MemoryCahe是在對快取作業的時候,會掃描整個緩衝刪除,存在很大的延時,而Redis採用下面三種策略清理到期的key:

  1. 被動刪除:當讀/寫一個已經到期的key時,會觸發惰性刪除策略,直接刪除掉這個到期key
  2. 主動刪除:由於惰性刪除策略無法保證冷資料被及時刪掉,所以Redis會定期主動淘汰一批已到期的key
  3. 當前已用記憶體超過maxmemory限定時,觸發主動清理策略

這就保證了到期緩衝的及時清理。關於Redis清理到期key的策略可以看這篇文章。

  當插入一條Hash類型資料時,開啟RedisManager會看到下面這樣,absexp:絕對到期時間,sldexp:滑動到期時間,data:就是我們代碼中設定的value。

3.2 Get方法中,實現的主要擷取功能調用了下面代碼。
internal static class RedisExtensions    {        private const string HmGetScript = (@"return redis.call('HMGET', KEYS[1], unpack(ARGV))");//通過指令碼HMGET命令擷取key的值     //Get方法中調用此方法,memebers為固定值 data,也就是擷取欄位data的值         internal static RedisValue[] HashMemberGet(this IDatabase cache, string key, params string[] members)        {            var result = cache.ScriptEvaluate(                HmGetScript,                new RedisKey[] { key },                GetRedisMembers(members));            return (RedisValue[])result;        }        internal static async Task<RedisValue[]> HashMemberGetAsync(            this IDatabase cache,            string key,            params string[] members)        {            var result = await cache.ScriptEvaluateAsync(                HmGetScript,                new RedisKey[] { key },                GetRedisMembers(members));            // TODO: Error checking?            return (RedisValue[])result;        }        private static RedisValue[] GetRedisMembers(params string[] members)        {            var redisMembers = new RedisValue[members.Length];            for (int i = 0; i < members.Length; i++)            {                redisMembers[i] = (RedisValue)members[i];            }            return redisMembers;        }    }

Remove方法就直接調用了StackExchange的API,這裡就不做解釋。

  相比MemoryCache的代碼,Redis代碼相對簡單,主要是微軟的開發人員"偷工減料"吧(我自己感覺),很多重要的方法,比如Redis串連、添加、設定到期時間、都調用了StackExchange的API,沒有實現自己的連結池等等。更像是對StackExchangeAPI中的Hash類型的再次封裝。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.