基礎數據與進程內緩存

前言

就目前的大環境來講,說到緩存,可能大部分小夥伴第一反應就會是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

相關文章
相關標籤/搜索