基础数据与进程内缓存

前言

就目前的大环境来讲,说到缓存,可能大部分小伙伴第一反应就会是redis。不少人每每忽视了进程内缓存这一利器。git

分布式缓存和进程内缓存的优点和劣势不用多说,你们都略知一二。github

我我的仍是更倾向于将基础数据,这些变更少的东西,丢到进程内缓存而不是放到redis中,而后定时去更新。redis

碰到的一些业务情景对这些基础数据的实时要求并不会过高,能够容忍20〜30分钟的延迟,即容许这一小段时间内的脏读,而不影响系统的总体结果。针对不一样的业务,就要视状况而定了。数据库

目前的作法是基于.NET Core的HostedService,在程序启动的时候先把数据加载到缓存中,同时有个定时器,每隔一个时间刷新一次。api

具体实现

首先是刷新缓存的后台任务缓存

public class RefreshCachingBgTask : IHostedService, IDisposable
{
    private readonly ILogger _logger;
    private readonly IEasyCachingProviderFactory _providerFactory;        
    private Timer _timer;
    private bool _refreshing;
    
    public RefreshCachingBgTask(ILoggerFactory loggerFactory, IEasyCachingProviderFactory providerFactory)
    {            
        this._logger = loggerFactory.CreateLogger<RefreshCachingBgTask>();
        this._providerFactory = providerFactory;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation($"Refresh caching backgroud taks begin ...");
        
        _timer = new Timer(async x =>
        {
            if (_refreshing)
            {
                _logger.LogInformation($"Latest manipulation is still working ...");
                return;
            }
            _refreshing = true;
            await RefreshAsync();
            _refreshing = false;
        }, null, TimeSpan.Zero, TimeSpan.FromSeconds(20));

        return Task.CompletedTask;
    }

    private async Task RefreshAsync()
    {
        _logger.LogInformation($"Refresh caching begin at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");

        try
        {
            var cachingProvider = _providerFactory.GetCachingProvider("m1");

            // mock query data from database or others 
            var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            var random = new Random().NextDouble();

            // only once
            var dict = new Dictionary<string, string>()
            {
                { ConstValue.Time_Cache_Key, time },
                { ConstValue.Random_Cache_Key, random.ToString() }
            };
            await cachingProvider.SetAllAsync(dict, TimeSpan.FromDays(30));

            //// one by one
            //await cachingProvider.SetAsync(Time_Cache_Key, time, TimeSpan.FromSeconds(10));
            //await cachingProvider.SetAsync(Random_Cache_Key, random.ToString(), TimeSpan.FromSeconds(10));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, $"Refresh caching error ...");                
        }

        _logger.LogInformation($"Refresh caching end at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation($"Refresh caching backgroud taks end ...");

        _timer?.Change(Timeout.Infinite, 0);

        return Task.CompletedTask;
    }

    public void Dispose()
    {
        _timer?.Dispose();
    }
}

注: 由于是演示,全部这里设置的时间比较短,正常来讲,这里是要设置一个超长的缓存时间,以便在获取这个缓存的时候,永远能取到值。dom

Startup中注册EasyCaching和刷新缓存的后台任务async

public void ConfigureServices(IServiceCollection services)
{
    services.AddEasyCaching(options=> 
    {
        options.UseInMemory("m1");
    });

    // register backgroud task
    services.AddHostedService<RefreshCachingBgTask>();

    // others ..
}

而后是控制器中的使用分布式

[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    private readonly IEasyCachingProviderFactory _providerFactory;  

    public ValuesController(IEasyCachingProviderFactory providerFactory)
    {
        this._providerFactory = providerFactory;
    }

    // GET api/values
    [HttpGet]
    public ActionResult<IEnumerable<string>> Get()
    {
        var provider = _providerFactory.GetCachingProvider("m1");

        var time = provider.Get<string>(ConstValue.Time_Cache_Key);

        // do something based on time ...

        var random = provider.Get<string>(ConstValue.Random_Cache_Key);

        // do something based on random ...

        return new string[] { time.Value, random.Value };            
    }        
}

效果大体以下:ide

固然,可能有人会提出问题,若是在程序启动的时候,缓存没能正确的写入,好比从数据库读数据的时候引起了异常,或者其余缘由致使没能写进去。

这里也给出下面几个解决方案:

  1. 引入Polly进行重试操做
  2. 在读的时候,若是失败,从新load一次数据,这里必定要加互斥锁,避免同一时间n个请求同时去load数据

文中示例代码:

RefreshCaching

相关文章
相关标签/搜索