zoukankan      html  css  js  c++  java
  • 基础数据与进程内缓存

    前言

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

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

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

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

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

    具体实现

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

    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();
        }
    }
    

    注: 因为是演示,所有这里设置的时间比较短,正常来说,这里是要设置一个超长的缓存时间,以便在获取这个缓存的时候,永远能取到值。

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

    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 };            
        }        
    }
    

    效果大致如下:

    当然,可能有人会提出问题,如果在程序启动的时候,缓存没能正确的写入,比如从数据库读数据的时候引发了异常,或者其他原因导致没能写进去。

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

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

    文中示例代码:

    RefreshCaching

  • 相关阅读:
    block iOS 块
    面试有感
    Could not automatically select an Xcode project. Specify one in your Podfile like so
    xcrun: error: active developer path
    NSDictionary
    CSS3魔法堂:CSS3滤镜及Canvas、SVG和IE滤镜替代方案详解
    转:CSS盒模型
    转:手机端html5触屏事件(touch事件)
    转: div:给div加滚动条 div的滚动条设置
    转:什么时候用阻止事件冒泡
  • 原文地址:https://www.cnblogs.com/catcher1994/p/10598021.html
Copyright © 2011-2022 走看看