一文了解什么是EasyCaching(下)

发表于:2021-11-10 09:53

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:catcherwong    来源:掘金

  多实例支持
  可能有人会问多实例是什么意思,这里的多实例主要是指,在同一个项目中,同时使用多个provider,包括多个同一类型的provider或着是不同类型的provider。
  这样说可能不太清晰,再来举一个虚构的小例子,可能大家就会更清晰了。
  现在我们的商品缓存在redis集群一中,用户信息在redis集群二中,商品评论缓存在mecached集群中,一些简单的配置信息在应用服务器的本地缓存中。
  在这种情况下,我们想简单的通过IEasyCachingProvider来直接操作这么多不同的缓存,显然是没办法做到的!
  这个时候想同时操作这么多不同的缓存,就要借助IEasyCachingProviderFactory来指定使用那个provider。
  这个工厂是通过provider的名字来获取要使用的provider。
  下面来看个例子。
  我们先添加两个不同名字的InMemory缓存:
services.AddEasyCaching(option =>
{
    // 指定当前provider的名字为m1
    option.UseInMemory("m1");
    
    // 指定当前provider的名字为m2
    config.UseInMemory(options => 
    {
        options.DBConfig = new InMemoryCachingOptions
        {
            SizeLimit = 100 
        };
    }, "m2");
});

  使用的时候:
[Route("api/[controller]")]  
public class ValuesController : Controller  
{  
    private readonly IEasyCachingProviderFactory _factory;  
  
    public ValuesController(IEasyCachingProviderFactory factory)  
    {  
        this._factory = factory;  
    }  
  
    // GET api/values
    [HttpGet]  
    [Route("")]  
    public string Get()  
    {  
        // 获取名字为m1的provider
        var provider_1 = _factory.GetCachingProvider("m1");  
        // 获取名字为m2的provider
        var provider_2 = _factory.GetCachingProvider("m2");
        
        // provider_1.xxx
        // provider_2.xxx
    
        return $"multi instances";                 
    }  
}  

  上面这个例子中,provider_1和provider_2是不会互相干扰对方的,因为它们是不同的provider!
  直观感觉,有点类似区域(region)的概念,可以这样去理解,但是严格意义上它并不是区域。

  缓存的AOP操作
  说起AOP,可能大家第一印象会是记录日志操作,把参数打一下,结果打一下。
  其实这个在缓存操作中同样有简化的作用。
  一般情况下,我们可能是这样操作缓存的。
public async Task<Product> GetProductAsync(int id)  
{  
    string cacheKey = $"product:{id}";  
      
    var val = await _cache.GetAsync<Product>(cacheKey);  
      
    if(val.HasValue)  
        return val.Value;  
      
    var product = await _db.GetProductAsync(id);  
      
    if(product != null)  
        _cache.Set<Product>(cacheKey, product, expiration);  
          
    return val;  
}  

  如果使用缓存的地方很多,那么我们可能就会觉得烦锁。
  我们同样可以使用AOP来简化这一操作。
public interface IProductService 
{
    [EasyCachingAble(Expiration = 10)]
    Task<Product> GetProductAsync(int id);
}

public class ProductService : IProductService
{
    public Task<Product> GetProductAsync(int id)
    {
        return Task.FromResult(new Product { ... });   
    }
}

  可以看到,我们只要在接口的定义上面加上一个Attribute标识一下就可以了。
  当然,只加Attribute,不加配置,它也是不会生效的。下面以EasyCaching.Interceptor.AspectCore为例,添加相应的配置。
public IServiceProvider ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IProductService, ProductService>();

    services.AddEasyCaching(options =>
    {
        options.UseInMemory("m1");
    });

    return services.ConfigureAspectCoreInterceptor(options =>
    {
        // 可以在这里指定你要用那个provider
        // 或者在Attribute上面指定
        options.CacheProviderName = "m1";
    });
}

  这两步就可以让你在调用方法的时候优先取缓存,没有缓存的时候会去执行方法。
  下面再来说一下三个Attritebute的一些参数。
  首先是三个通用配置:

  EasyCachingAble和EasyCachingPut还有一个同名和配置。

  EasyCachingEvict有两个特殊的配置。

  支持Diagnostics
  为了方便接入第三方的APM,提供了Diagnostics的支持,便于实现追踪。
  下图是我司接入Jaeger的一个案例。

  二级缓存
  二级缓存,多级缓存,其实在缓存的小世界中还算是一个比较重要的东西!
  一个最为头疼的问题就是不同级的缓存如何做到近似实时的同步。
  在EasyCaching中,二级缓存的实现逻辑大致就是下面的这张图。

  如果某个服务器上面的本地缓存被修改了,就会通过缓存总线去通知其他服务器把对应的本地缓存移除掉。
  下面来看一个简单的使用例子。
  首先是添加nuget包。
dotnet add package EasyCaching.InMemory
dotnet add package EasyCaching.Redis
dotnet add package EasyCaching.HybridCache
dotnet add package EasyCaching.Bus.Redis

  其次是添加配置。
services.AddEasyCaching(option =>
{
    // 添加两个基本的provider
    option.UseInMemory("m1");
    option.UseRedis(config =>
    {
        config.DBConfig.Endpoints.Add(new Core.Configurations.ServerEndPoint("127.0.0.1", 6379));
        config.DBConfig.Database = 5;
    }, "myredis");

    //  使用hybird
    option.UseHybrid(config =>
    {
        config.EnableLogging = false;
        // 缓存总线的订阅主题
        config.TopicName = "test_topic";
        // 本地缓存的名字
        config.LocalCacheProviderName = "m1";
        // 分布式缓存的名字
        config.DistributedCacheProviderName = "myredis";
    });

    // 使用redis作为缓存总线
    option.WithRedisBus(config =>
    {
        config.Endpoints.Add(new Core.Configurations.ServerEndPoint("127.0.0.1", 6379));
        config.Database = 6;
    });
});

  最后就是使用了。
[Route("api/[controller]")]  
public class ValuesController : Controller  
{  
    private readonly IHybridCachingProvider _provider;  
  
    public ValuesController(IHybridCachingProvider provider)  
    {  
        this._provider = provider;  
    }  
  
    // GET api/values
    [HttpGet]  
    [Route("")]  
    public string Get()  
    {  
        _provider.Set(cacheKey, "val", TimeSpan.FromSeconds(30));
    
        return $"hybrid";                 
    }  

  Redis的特殊Provider
  大家都知道redis支持多种数据结构,还有一些原子递增递减的操作等等。为了支持这些操作,EasyCaching提供了一个独立的接口,IRedisCachingProvider。
  这个接口,目前也只支持了百分之六七十常用的一些操作,还有一些可能用的少的就没加进去。
  同样的,这个接口也是支持多实例的,也可以通过IEasyCachingProviderFactory来获取不同的provider实例。
  在注入的时候,不需要额外的操作,和添加Redis是一样的。不同的是,在使用的时候,不再是用IEasyCachingProvider,而是要用IRedisCachingProvider。
  下面是一个简单的使用例子。
[Route("api/mredis")]
public class MultiRedisController : Controller
{
    private readonly IRedisCachingProvider _redis1;
    private readonly IRedisCachingProvider _redis2;

    public MultiRedisController(IEasyCachingProviderFactory factory)
    {
        this._redis1 = factory.GetRedisProvider("redis1");
        this._redis2 = factory.GetRedisProvider("redis2");
    }

    // GET api/mredis
    [HttpGet]
    public string Get()
    {
        _redis1.StringSet("keyredis1", "val");

        var res1 = _redis1.StringGet("keyredis1");
        var res2 = _redis2.StringGet("keyredis1");

        return $"redis1 cached value: {res1}, redis2 cached value : {res2}";
    }             
}

  除了这些基础功能,还有一些扩展性的功能,在这里要非常感谢yrinleung,他把EasyCaching和WebApiClient,CAP等项目结合起来了。

  写在最后
  以上就是EasyCaching目前支持的一些功能特性,如果大家在使用的过程中有遇到问题的话,希望可以积极的反馈,帮助EasyCaching变得越来越好。

  本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号