有哪些习惯
坚持
LESS IS MORE,SIMPLER IS BETTER THAN MORE
你一定会有很大的收获
各种小问题?
如果你之前用过Redis的话,肯定会使用过StackExchange.Redis,我之前很久就用过,在.netfw的时候,当时并发还比较小,没有什么问题,后来我就迁移到Blog.Core里了,但是有很多小伙伴,反馈高并发下,使用同步的方法会有问题,比如超时的问题,偶尔还会出现什么内存的问题,一直被很多网友所诟病。
一直说国内有一个组件很不错,这个大家自己去使用吧,我也不多说什么,但是我想着StackExchange.Redis既然是官方推荐的不会这么菜吧,果然官方给的方案是,用异步的方式写,会解决超时的问题。
那具体应该怎么写呢,我还没有来得及思考,正好这两天研究微软的微服务案例eShopOnContainers,我发现他就是用的StackExchange.Redis实现的购物车缓存的子服务逻辑,研究了下,迁移到Blog.Core项目中,当然我这里简单测试了下,并发下没有问题,大家还是可以自行测试,当然还是那句话,有问题了我会继续修改,如果还是不行,那自己就换其他的组件吧。
设计异步方案
这个比较简单的,设计思路和之前的是一样的,只不过有一点,连接调制器的注入方式我做了调整(ConnectionMultiplexer),之前用的是双if夹lock的方式,实现的单例,现在直接使用依赖注入AddSingleton的方式,更专业些,也没那么幺蛾子:
/// <summary>
/// Redis缓存 启动服务
/// </summary>
public static class RedisCacheSetup
{public static void AddRedisCacheSetup(this IServiceCollection services){if (services == null) throw new ArgumentNullException(nameof(services));services.AddTransient<IRedisBasketRepository, RedisBasketRepository>();services.AddSingleton<ConnectionMultiplexer>(sp =>{//获取连接字符串string redisConfiguration = Appsettings.app(new string[] { "AppSettings", "RedisCachingAOP", "ConnectionString" });var configuration = ConfigurationOptions.Parse(redisConfiguration, true);configuration.ResolveDns = true;return ConnectionMultiplexer.Connect(configuration);});}
}
这里是Redis依赖注入的扩展方法,需要在Startup里配置上,别忘记了:
services.AddRedisCacheSetup();
然后就是设计接口和实现类了,也很简单,接口和之前的一样,只不过都换成了异步:
/// <summary>/// Redis缓存接口/// </summary>public interface IRedisBasketRepository{//获取 Reids 缓存值Task<string> GetValue(string key);//获取值,并序列化Task<TEntity> Get<TEntity>(string key);//保存Task Set(string key, object value, TimeSpan cacheTime);//判断是否存在Task<bool> Exist(string key);//移除某一个缓存值Task Remove(string key);//全部清除Task Clear();}
接口的名称我为了纪念eShop项目,保持一致了,虽然比较怪,自己酌情修改吧。
然后实现类也很简单,构造函数直接注入日志和连接调制器实例,不用写复杂的单例模式了:
public class RedisBasketRepository : IRedisBasketRepository{private readonly ILogger<RedisBasketRepository> _logger;private readonly ConnectionMultiplexer _redis;private readonly IDatabase _database;public RedisBasketRepository(ILogger<RedisBasketRepository> logger, ConnectionMultiplexer redis){_logger = logger;_redis = redis;_database = redis.GetDatabase();}private IServer GetServer(){var endpoint = _redis.GetEndPoints();return _redis.GetServer(endpoint.First());}public async Task Clear(){foreach (var endPoint in _redis.GetEndPoints()){var server = GetServer();foreach (var key in server.Keys()){await _database.KeyDeleteAsync(key);}}}public async Task<bool> Exist(string key){return await _database.KeyExistsAsync(key);}public async Task<string> GetValue(string key){return await _database.StringGetAsync(key);}public async Task Remove(string key){await _database.KeyDeleteAsync(key);}public async Task Set(string key, object value, TimeSpan cacheTime){if (value != null){//序列化,将object值生成RedisValueawait _database.StringSetAsync(key, SerializeHelper.Serialize(value), cacheTime);}}public async Task<TEntity> Get<TEntity>(string key){var value = await _database.StringGetAsync(key);if (value.HasValue){//需要用的反序列化,将Redis存储的Byte[],进行反序列化return SerializeHelper.Deserialize<TEntity>(value);}else{return default(TEntity);}}}
相应的逻辑也很简单,我详细基本都能看得懂。
最后就是在BlogRedisCacheAOP.cs中,也要修改下,毕竟改成了异步,还是要注意的:
写到最后
九月马上就要到了,新的一年也快结束了,准备好迎接125天的2021年了么!希望新的一年,一起都好起来,大家也要趁着时间好好学学知识,因为我相信到时候肯定会有很多岗位,机会总是会留给有准备的人,相信我,相信你自己。
生活の艺术