多实例支持
可能有人会问多实例是什么意思,这里的多实例主要是指,在同一个项目中,同时使用多个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),我们将立即处理