• .Net Core 缓存方式(二)分布式缓存的扩展方法的实现(4)


    .Net Core 缓存方式(二)分布式缓存的扩展方法的实现(4)

    IDistributedCache 接口

    看过 IDistributedCache.cs 了解到,所有分布式缓存都是通过实现 IDistributedCache,实现使用不同的缓存服务器,

    IDistributedCache.cs

    // Licensed to the .NET Foundation under one or more agreements.
    // The .NET Foundation licenses this file to you under the MIT license.
    
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace Microsoft.Extensions.Caching.Distributed
    {
        /// <summary>
        /// Represents a distributed cache of serialized values.
        /// </summary>
        public interface IDistributedCache
        {
            /// <summary>
            /// Gets a value with the given key.
            /// </summary>
            /// <param name="key">A string identifying the requested value.</param>
            /// <returns>The located value or null.</returns>
            byte[] Get(string key);
    
            /// <summary>
            /// Gets a value with the given key.
            /// </summary>
            /// <param name="key">A string identifying the requested value.</param>
            /// <param name="token">Optional. The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the located value or null.</returns>
            Task<byte[]> GetAsync(string key, CancellationToken token = default(CancellationToken));
    
            /// <summary>
            /// Sets a value with the given key.
            /// </summary>
            /// <param name="key">A string identifying the requested value.</param>
            /// <param name="value">The value to set in the cache.</param>
            /// <param name="options">The cache options for the value.</param>
            void Set(string key, byte[] value, DistributedCacheEntryOptions options);
    
            /// <summary>
            /// Sets the value with the given key.
            /// </summary>
            /// <param name="key">A string identifying the requested value.</param>
            /// <param name="value">The value to set in the cache.</param>
            /// <param name="options">The cache options for the value.</param>
            /// <param name="token">Optional. The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
            Task SetAsync(string key, byte[] value, DistributedCacheEntryOptions options, CancellationToken token = default(CancellationToken));
    
            /// <summary>
            /// Refreshes a value in the cache based on its key, resetting its sliding expiration timeout (if any).
            /// </summary>
            /// <param name="key">A string identifying the requested calue.</param>
            void Refresh(string key);
    
            /// <summary>
            /// Refreshes a value in the cache based on its key, resetting its sliding expiration timeout (if any).
            /// </summary>
            /// <param name="key">A string identifying the requested value.</param>
            /// <param name="token">Optional. The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
            Task RefreshAsync(string key, CancellationToken token = default(CancellationToken));
    
            /// <summary>
            /// Removes the value with the given key.
            /// </summary>
            /// <param name="key">A string identifying the requested value.</param>
            void Remove(string key);
    
            /// <summary>
            /// Removes the value with the given key.
            /// </summary>
            /// <param name="key">A string identifying the requested value.</param>
            /// <param name="token">Optional. The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
            /// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
            Task RemoveAsync(string key, CancellationToken token = default(CancellationToken));
        }
    }
    

    默认的IDistributedCache只实现了一些很简单的方法,这些在实际开发中使用中并不常用,例如:

                    var currentTimeUTC = DateTime.UtcNow.ToString();
                    byte[] encodedCurrentTimeUTC = Encoding.UTF8.GetBytes(currentTimeUTC);
                    var options = new DistributedCacheEntryOptions()
                        .SetSlidingExpiration(TimeSpan.FromSeconds(20));
                    cache.Set("cachedTimeUTC", encodedCurrentTimeUTC, options);
    

    使用起来很麻烦,但是实际使用有类似 cache.SetString就再好不过了

    IDistributedCache 的扩展方法 DistributedCacheExtensions.cs

    微软官方提供了一些扩展方法

    DistributedCacheExtensions.cs

    // Licensed to the .NET Foundation under one or more agreements.
    // The .NET Foundation licenses this file to you under the MIT license.
    
    using System;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace Microsoft.Extensions.Caching.Distributed
    {
        /// <summary>
        /// Extension methods for setting data in an <see cref="IDistributedCache" />.
        /// </summary>
        public static class DistributedCacheExtensions
        {
            /// <summary>
            /// Sets a sequence of bytes in the specified cache with the specified key.
            /// </summary>
            /// <param name="cache">The cache in which to store the data.</param>
            /// <param name="key">The key to store the data in.</param>
            /// <param name="value">The data to store in the cache.</param>
            /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="key"/> or <paramref name="value"/> is null.</exception>
            public static void Set(this IDistributedCache cache, string key, byte[] value)
            {
                if (key == null)
                {
                    throw new ArgumentNullException(nameof(key));
                }
                if (value == null)
                {
                    throw new ArgumentNullException(nameof(value));
                }
    
                cache.Set(key, value, new DistributedCacheEntryOptions());
            }
    
            /// <summary>
            /// Asynchronously sets a sequence of bytes in the specified cache with the specified key.
            /// </summary>
            /// <param name="cache">The cache in which to store the data.</param>
            /// <param name="key">The key to store the data in.</param>
            /// <param name="value">The data to store in the cache.</param>
            /// <param name="token">Optional. A <see cref="CancellationToken" /> to cancel the operation.</param>
            /// <returns>A task that represents the asynchronous set operation.</returns>
            /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="key"/> or <paramref name="value"/> is null.</exception>
            public static Task SetAsync(this IDistributedCache cache, string key, byte[] value, CancellationToken token = default(CancellationToken))
            {
                if (key == null)
                {
                    throw new ArgumentNullException(nameof(key));
                }
                if (value == null)
                {
                    throw new ArgumentNullException(nameof(value));
                }
    
                return cache.SetAsync(key, value, new DistributedCacheEntryOptions(), token);
            }
    
            /// <summary>
            /// Sets a string in the specified cache with the specified key.
            /// </summary>
            /// <param name="cache">The cache in which to store the data.</param>
            /// <param name="key">The key to store the data in.</param>
            /// <param name="value">The data to store in the cache.</param>
            /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="key"/> or <paramref name="value"/> is null.</exception>
            public static void SetString(this IDistributedCache cache, string key, string value)
            {
                cache.SetString(key, value, new DistributedCacheEntryOptions());
            }
    
            /// <summary>
            /// Sets a string in the specified cache with the specified key.
            /// </summary>
            /// <param name="cache">The cache in which to store the data.</param>
            /// <param name="key">The key to store the data in.</param>
            /// <param name="value">The data to store in the cache.</param>
            /// <param name="options">The cache options for the entry.</param>
            /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="key"/> or <paramref name="value"/> is null.</exception>
            public static void SetString(this IDistributedCache cache, string key, string value, DistributedCacheEntryOptions options)
            {
                if (key == null)
                {
                    throw new ArgumentNullException(nameof(key));
                }
                if (value == null)
                {
                    throw new ArgumentNullException(nameof(value));
                }
                cache.Set(key, Encoding.UTF8.GetBytes(value), options);
            }
    
            /// <summary>
            /// Asynchronously sets a string in the specified cache with the specified key.
            /// </summary>
            /// <param name="cache">The cache in which to store the data.</param>
            /// <param name="key">The key to store the data in.</param>
            /// <param name="value">The data to store in the cache.</param>
            /// <param name="token">Optional. A <see cref="CancellationToken" /> to cancel the operation.</param>
            /// <returns>A task that represents the asynchronous set operation.</returns>
            /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="key"/> or <paramref name="value"/> is null.</exception>
            public static Task SetStringAsync(this IDistributedCache cache, string key, string value, CancellationToken token = default(CancellationToken))
            {
                return cache.SetStringAsync(key, value, new DistributedCacheEntryOptions(), token);
            }
    
            /// <summary>
            /// Asynchronously sets a string in the specified cache with the specified key.
            /// </summary>
            /// <param name="cache">The cache in which to store the data.</param>
            /// <param name="key">The key to store the data in.</param>
            /// <param name="value">The data to store in the cache.</param>
            /// <param name="options">The cache options for the entry.</param>
            /// <param name="token">Optional. A <see cref="CancellationToken" /> to cancel the operation.</param>
            /// <returns>A task that represents the asynchronous set operation.</returns>
            /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="key"/> or <paramref name="value"/> is null.</exception>
            public static Task SetStringAsync(this IDistributedCache cache, string key, string value, DistributedCacheEntryOptions options, CancellationToken token = default(CancellationToken))
            {
                if (key == null)
                {
                    throw new ArgumentNullException(nameof(key));
                }
                if (value == null)
                {
                    throw new ArgumentNullException(nameof(value));
                }
                return cache.SetAsync(key, Encoding.UTF8.GetBytes(value), options, token);
            }
    
            /// <summary>
            /// Gets a string from the specified cache with the specified key.
            /// </summary>
            /// <param name="cache">The cache in which to store the data.</param>
            /// <param name="key">The key to get the stored data for.</param>
            /// <returns>The string value from the stored cache key.</returns>
            public static string GetString(this IDistributedCache cache, string key)
            {
                byte[] data = cache.Get(key);
                if (data == null)
                {
                    return null;
                }
                return Encoding.UTF8.GetString(data, 0, data.Length);
            }
    
            /// <summary>
            /// Asynchronously gets a string from the specified cache with the specified key.
            /// </summary>
            /// <param name="cache">The cache in which to store the data.</param>
            /// <param name="key">The key to get the stored data for.</param>
            /// <param name="token">Optional. A <see cref="CancellationToken" /> to cancel the operation.</param>
            /// <returns>A task that gets the string value from the stored cache key.</returns>
            public static async Task<string> GetStringAsync(this IDistributedCache cache, string key, CancellationToken token = default(CancellationToken))
            {
                byte[] data = await cache.GetAsync(key, token).ConfigureAwait(false);
                if (data == null)
                {
                    return null;
                }
                return Encoding.UTF8.GetString(data, 0, data.Length);
            }
        }
    }
    

    通过DistributedCacheExtensions.cs 可以实现 cache.GetString(key) 这种写法

    DistributedCacheEntryOptions.cs 实现

    Microsoft.Extensions.Caching.Abstractions/src/DistributedCacheEntryOptions.cs

    https://github.com/dotnet/runtime/blob/master/src/libraries/Microsoft.Extensions.Caching.Abstractions/src/DistributedCacheEntryOptions.cs

        /// <summary>
        /// Provides the cache options for an entry in <see cref="IDistributedCache"/>.
        /// </summary>
        public class DistributedCacheEntryOptions
        {
            private DateTimeOffset? _absoluteExpiration;
            private TimeSpan? _absoluteExpirationRelativeToNow;
            private TimeSpan? _slidingExpiration;
    
    获取或设置缓存的绝对到期日期
            /// <summary>
            /// Gets or sets an absolute expiration date for the cache entry.
            /// </summary>
            public DateTimeOffset? AbsoluteExpiration
            {
                get
                {
                    return _absoluteExpiration;
                }
                set
                {
                    _absoluteExpiration = value;
                }
            }
    
    获取或设置相对于现在的绝对到期时间
            /// <summary>
            /// Gets or sets an absolute expiration time, relative to now.
            /// </summary>
            public TimeSpan? AbsoluteExpirationRelativeToNow
            {
                get
                {
                    return _absoluteExpirationRelativeToNow;
                }
                set
                {
                    if (value <= TimeSpan.Zero)
                    {
                        throw new ArgumentOutOfRangeException(
                            nameof(AbsoluteExpirationRelativeToNow),
                            value,
                            "The relative expiration value must be positive.");
                    }
    
                    _absoluteExpirationRelativeToNow = value;
                }
            }
    
    获取或设置缓存条目在被删除之前可以处于非活动状态(例如,未访问)的时间。这不会使条目的生存期超过绝对到期时间(如果已设置)。
            /// <summary>
            /// Gets or sets how long a cache entry can be inactive (e.g. not accessed) before it will be removed.
            /// This will not extend the entry lifetime beyond the absolute expiration (if set).
            /// </summary>
            public TimeSpan? SlidingExpiration
            {
                get
                {
                    return _slidingExpiration;
                }
                set
                {
                    if (value <= TimeSpan.Zero)
                    {
                        throw new ArgumentOutOfRangeException(
                            nameof(SlidingExpiration),
                            value,
                            "The sliding expiration value must be positive.");
                    }
                    _slidingExpiration = value;
                }
            }
        }
    
    

    Microsoft.Extensions.Caching.Abstractions/src/DistributedCacheEntryExtensions.cs

    https://github.com/dotnet/runtime/blob/master/src/libraries/Microsoft.Extensions.Caching.Abstractions/src/DistributedCacheEntryExtensions.cs

        public static class DistributedCacheEntryExtensions
        {
            /// <summary>
            /// Sets an absolute expiration time, relative to now.
            /// </summary>
            /// <param name="options">The options to be operated on.</param>
            /// <param name="relative">The expiration time, relative to now.</param>
            public static DistributedCacheEntryOptions SetAbsoluteExpiration(
                this DistributedCacheEntryOptions options,
                TimeSpan relative)
            {
                options.AbsoluteExpirationRelativeToNow = relative;
                return options;
            }
    
            /// <summary>
            /// Sets an absolute expiration date for the cache entry.
            /// </summary>
            /// <param name="options">The options to be operated on.</param>
            /// <param name="absolute">The expiration time, in absolute terms.</param>
            public static DistributedCacheEntryOptions SetAbsoluteExpiration(
                this DistributedCacheEntryOptions options,
                DateTimeOffset absolute)
            {
                options.AbsoluteExpiration = absolute;
                return options;
            }
    
            /// <summary>
            /// Sets how long the cache entry can be inactive (e.g. not accessed) before it will be removed.
            /// This will not extend the entry lifetime beyond the absolute expiration (if set).
            /// </summary>
            /// <param name="options">The options to be operated on.</param>
            /// <param name="offset">The sliding expiration time.</param>
            public static DistributedCacheEntryOptions SetSlidingExpiration(
                this DistributedCacheEntryOptions options,
                TimeSpan offset)
            {
                options.SlidingExpiration = offset;
                return options;
            }
        }
    

    new DistributedCacheEntryOptions() 后 DateTimeOffset? _absoluteExpiration TimeSpan? _absoluteExpirationRelativeToNow TimeSpan? _slidingExpiration均为null

    过期时间判断

    1. void Set(string key, byte[] value, DistributedCacheEntryOptions options)
    
                var creationTime = DateTimeOffset.UtcNow;
    
                var absoluteExpiration = GetAbsoluteExpiration(creationTime, options);
    
                var result = _cache.ScriptEvaluate(SetScript, new RedisKey[] { _instance + key },
                    new RedisValue[]
                    {
                            absoluteExpiration?.Ticks ?? NotPresent,
                            options.SlidingExpiration?.Ticks ?? NotPresent,
                            GetExpirationInSeconds(creationTime, absoluteExpiration, options) ?? NotPresent,
                            value
                    });
    
    2.GetAbsoluteExpiration(DateTimeOffset creationTime, DistributedCacheEntryOptions options)
    
            private static DateTimeOffset? GetAbsoluteExpiration(DateTimeOffset creationTime, DistributedCacheEntryOptions options)
            {
                if (options.AbsoluteExpiration.HasValue && options.AbsoluteExpiration <= creationTime)
                {
                    throw new ArgumentOutOfRangeException(
                        nameof(DistributedCacheEntryOptions.AbsoluteExpiration),
                        options.AbsoluteExpiration.Value,
                        "The absolute expiration value must be in the future.");
                }
                var absoluteExpiration = options.AbsoluteExpiration;
                if (options.AbsoluteExpirationRelativeToNow.HasValue)
                {
                    absoluteExpiration = creationTime + options.AbsoluteExpirationRelativeToNow;
                }
    
                return absoluteExpiration;
            }
    
            private static long? GetExpirationInSeconds(DateTimeOffset creationTime, DateTimeOffset? absoluteExpiration, DistributedCacheEntryOptions options)
            {
                if (absoluteExpiration.HasValue && options.SlidingExpiration.HasValue)
                {
                    return (long)Math.Min(
                        (absoluteExpiration.Value - creationTime).TotalSeconds,
                        options.SlidingExpiration.Value.TotalSeconds);
                }
                else if (absoluteExpiration.HasValue)
                {
                    return (long)(absoluteExpiration.Value - creationTime).TotalSeconds;
                }
                else if (options.SlidingExpiration.HasValue)
                {
                    return (long)options.SlidingExpiration.Value.TotalSeconds;
                }
                return null;
            }
    
    3.const string SetScript
    
            private const string SetScript = (@"
                    redis.call('HMSET', KEYS[1], 'absexp', ARGV[1], 'sldexp', ARGV[2], 'data', ARGV[4])
                    if ARGV[3] ~= '-1' then
                      redis.call('EXPIRE', KEYS[1], ARGV[3])
                    end
                    return 1");
    
    4.StackExchange.Redis Scripting
    

    db.ScriptEvaluate

    	const string Script = "redis.call('set', @key, @value)";
    
    	using (ConnectionMultiplexer conn = /* init code */)
    	{
    		var db = conn.GetDatabase(0);
    
    		var prepared = LuaScript.Prepare(Script);
    		db.ScriptEvaluate(prepared, new { key = (RedisKey)"mykey", value = 123 });
    	}
    
  • 相关阅读:
    jvm调试相关:jmap失效下找到alternatives神器
    作业2:java内存模型图示
    Python脚本:Linux自动化执行Python脚本
    Nodejs:单线程为什么能支持高并发?
    作业1:java虚拟机内存模型图示
    SpringBoot中获取spring.profiles.active的值
    线程中使用注解出现空指针如何解决?
    java7:核心技术与最佳实践读书笔记——对象生命周期
    java7:核心技术与最佳实践读书笔记——类加载
    虚拟机上不了网啦
  • 原文地址:https://www.cnblogs.com/WNpursue/p/13566939.html
Copyright © 2020-2023  润新知