分布式缓存的实现和使用

发布时间 2024-01-12 12:30:36作者: HenryBest

1.前言

什么是分布式缓存?

分布式缓存是指将缓存数据存储在多个节点(通常是不同的计算机或服务器)上,以提高系统性能和可伸缩性。这种缓存方式允许应用程序通过从缓存中读取数据而不是从数据库或其他存储中读取来提高读取操作的速度。分布式缓存通常用于大规模的分布式系统,其中需要快速的数据访问,而传统的数据库读写速度无法满足需求。

以下是分布式缓存的一些关键特性和优势:

  1. 高性能: 缓存在内存中,可以提供更快的读取操作,减少对数据库或其他后端存储的访问次数。

  2. 可伸缩性: 由于缓存数据存储在多个节点上,系统可以轻松地扩展以处理更多的请求。

  3. 负载均衡: 缓存系统通常可以通过在多个节点之间均匀分配负载来实现负载均衡,确保每个节点的负载相对平衡。

  4. 容错性: 分布式缓存通常具有容错机制,其中如果一个节点失败,系统可以从其他节点获取数据,确保服务的可用性。

  5. 降低数据库压力: 缓存可以减轻数据库负担,特别是对于读取频繁但很少更新的数据。

  6. 灵活性: 分布式缓存通常支持多种数据结构和查询模式,适用于不同类型的应用程序。

一些流行的分布式缓存系统包括Redis、Memcached、Apache Ignite等。这些系统提供了丰富的功能集,包括数据持久化、分区、复制、事务等,以满足各种应用场景的需求。在使用分布式缓存时,需要根据应用程序的具体需求和特性选择合适的缓存系统。

那么在C#中如何实现分布式缓存呢?

首先安装

 这个包

接下来去Program类中加入这两行代码

builder.Services.AddScoped<IDistributedCacheHelper, DistributedCacheHelper>();//这里是写的分布式缓存帮助接口和实现类
builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = "localhost";
    options.InstanceName = "xiaoxin";
});

接下来就去编写一下分布式缓存的帮助接口和它的实现类

首先是接口

using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BCP.Common
{
    public interface IDistributedCacheHelper
    {
        TResult? GetOrCreate<TResult>(string cacheKey, Func<DistributedCacheEntryOptions, TResult?> valueFactory, int expireSeconds = 60);

        Task<TResult?> GetOrCreateAsync<TResult>(string cacheKey, Func<DistributedCacheEntryOptions, Task<TResult?>> valueFactory, int expireSeconds = 60);

        void Remove(string cacheKey);
        Task RemoveAsync(string cacheKey);
    }
}

接下来是它的实现类

using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

namespace BCP.Common
{
    public class DistributedCacheHelper : IDistributedCacheHelper
    {
        private readonly IDistributedCache distCache;

        public DistributedCacheHelper(IDistributedCache distCache)
        {
            this.distCache = distCache;
        }

        private static DistributedCacheEntryOptions CreateOptions(int baseExpireSeconds)
        {
            //过期时间.Random.Shared 是.NET6新增的
            double sec = Random.Shared.Next(baseExpireSeconds, baseExpireSeconds * 2);
            TimeSpan expiration = TimeSpan.FromSeconds(sec);
            DistributedCacheEntryOptions options = new DistributedCacheEntryOptions();
            options.AbsoluteExpirationRelativeToNow = expiration;
            return options;
        }

        public TResult? GetOrCreate<TResult>(string cacheKey, Func<DistributedCacheEntryOptions, TResult?> valueFactory, int expireSeconds = 60)
        {
            string jsonStr = distCache.GetString(cacheKey);
            //缓存中不存在
            if (string.IsNullOrEmpty(jsonStr))
            {
                var options = CreateOptions(expireSeconds);
                TResult? result = valueFactory(options);//如果数据源中也没有查到,可能会返回null
                //null会被json序列化为字符串"null",所以可以防范“缓存穿透”
                string jsonOfResult = JsonSerializer.Serialize(result,
                    typeof(TResult));
                distCache.SetString(cacheKey, jsonOfResult, options);
                return result;
            }
            else
            {
                //"null"会被反序列化为null
                //TResult如果是引用类型,就有为null的可能性;如果TResult是值类型
                //在写入的时候肯定写入的是0、1之类的值,反序列化出来不会是null
                //所以如果obj这里为null,那么存进去的时候一定是引用类型
                distCache.Refresh(cacheKey);//刷新,以便于滑动过期时间延期
                return JsonSerializer.Deserialize<TResult>(jsonStr)!;
            }
        }

        public async Task<TResult?> GetOrCreateAsync<TResult>(string cacheKey, Func<DistributedCacheEntryOptions, Task<TResult?>> valueFactory, int expireSeconds = 60)
        {
            string jsonStr = await distCache.GetStringAsync(cacheKey);
            if (string.IsNullOrEmpty(jsonStr))
            {
                var options = CreateOptions(expireSeconds);
                TResult? result = await valueFactory(options);
                string jsonOfResult = JsonSerializer.Serialize(result,
                    typeof(TResult));
                await distCache.SetStringAsync(cacheKey, jsonOfResult, options);
                return result;
            }
            else
            {
                await distCache.RefreshAsync(cacheKey);
                return JsonSerializer.Deserialize<TResult>(jsonStr)!;
            }
        }

        public void Remove(string cacheKey)
        {
            distCache.Remove(cacheKey);
        }

        public Task RemoveAsync(string cacheKey)
        {
            return distCache.RemoveAsync(cacheKey);
        }
    }
}

以上就是创建成功了,那么如何使用呢?

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Distributed;
using Zack.ASPNETCore;

namespace BCP.Controllers
{
    [ApiController]
    [Route("[controller]/[action]")]
    public class Test1Controller : ControllerBase
    {
        private readonly IDistributedCache distCache;
        private readonly IDistributedCacheHelper helper;
        public Test1Controller(IDistributedCache distCache, IDistributedCacheHelper helper)
        {
            this.distCache = distCache;
            this.helper = helper;
        }
        [HttpGet]
        public string Now()
        {
            string s = distCache.GetString("Now");
            if (s == null)
            {
                s = DateTime.Now.ToString();
                var opt = new DistributedCacheEntryOptions();
                opt.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(30);
                distCache.SetString("Now", s, opt);
            }
            return s;
        }

        [HttpGet]
        public Task<string?> Now2()
        {
            return helper.GetOrCreateAsync<string>("Now2", async e => DateTime.Now.ToString());
        }

    }
}

以上就是实现和使用分布式缓存的过程