通过Dapr实现一个简单的基于.net的微服务电商系统(十八)——服务保护之多级缓存

  很久没有更新dapr系列了。今天带来的是一个小的组件集成,通过多级缓存框架来实现对服务的缓存保护,依旧是一个简易的演示以及对其设计原理思路的讲解,欢迎大家转发留言和star

目录:
一、通过Dapr实现一个简单的基于.net的微服务电商系统

二、通过Dapr实现一个简单的基于.net的微服务电商系统(二)——通讯框架讲解

三、通过Dapr实现一个简单的基于.net的微服务电商系统(三)——一步一步教你如何撸Dapr

四、通过Dapr实现一个简单的基于.net的微服务电商系统(四)——一步一步教你如何撸Dapr之订阅发布

五、通过Dapr实现一个简单的基于.net的微服务电商系统(五)——一步一步教你如何撸Dapr之状态管理

六、通过Dapr实现一个简单的基于.net的微服务电商系统(六)——一步一步教你如何撸Dapr之Actor服务

七、通过Dapr实现一个简单的基于.net的微服务电商系统(七)——一步一步教你如何撸Dapr之服务限流

八、通过Dapr实现一个简单的基于.net的微服务电商系统(八)——一步一步教你如何撸Dapr之链路追踪

九、通过Dapr实现一个简单的基于.net的微服务电商系统(九)——一步一步教你如何撸Dapr之OAuth2授权 && 百度版Oauth2

十、通过Dapr实现一个简单的基于.net的微服务电商系统(十)——一步一步教你如何撸Dapr之绑定

十一、通过Dapr实现一个简单的基于.net的微服务电商系统(十一)——一步一步教你如何撸Dapr之自动扩/缩容

十二、通过Dapr实现一个简单的基于.net的微服务电商系统(十二)——istio+dapr构建多运行时服务网格

十三、通过Dapr实现一个简单的基于.net的微服务电商系统(十三)——istio+dapr构建多运行时服务网格之生产环境部署

十四、通过Dapr实现一个简单的基于.net的微服务电商系统(十四)——开发环境容器调试小技巧

十五、通过Dapr实现一个简单的基于.net的微服务电商系统(十五)——集中式接口文档实现

十六、通过Dapr实现一个简单的基于.net的微服务电商系统(十六)——dapr+sentinel中间件实现服务保护

十七、通过Dapr实现一个简单的基于.net的微服务电商系统(十七)——服务保护之动态配置与热重载

十八、通过Dapr实现一个简单的基于.net的微服务电商系统(十八)——服务保护之多级缓存

十九、通过Dapr实现一个简单的基于.net的微服务电商系统(十九)——分布式事务之Saga模式

二十、通过Dapr实现一个简单的基于.net的微服务电商系统(二十)——Saga框架实现思路分享


附录:(如果你觉得对你有用,请给个star)
一、电商Demo地址

二、通讯框架地址

  今天我们演示一下,在创建订单的时候,订单服务会通过rpc拉取用户服务获取一个随机用户来模拟下订单。这个随机用户的接口接下来我会尝试使用多级缓存来保护。首先我们需要在AccountService的Infrastructure层通过nuget引入多级缓存的包:

Install-Package Oxygen-MultilevelCache

  接着我们需要注入两个具体的多级缓存实现,这里我们的一级缓存采用.netcore自带的memcache,二级缓存我们选用dapr的statemanager来实现,当然这两种实现你都可以替换成任意其他你熟知的缓存实现,并不影响最终效果。代码如下:

  一级缓存实现:

    public class L1Cache : IL1CacheServiceFactory
    {
        private readonly IMemoryCache memoryCache;
        public L1Cache(IMemoryCache memoryCache)
        {
            this.memoryCache = memoryCache;
        }
        public T Get<T>(string key)
        {
            Console.WriteLine($"L1缓存被调用,KEY={key},value{(memoryCache.Get<T>(key) == null ? "不存在" : "存在")}");
            return memoryCache.Get<T>(key);
        }

        public bool Set<T>(string key, T value, int expireTimeSecond = 0)
        {
            return memoryCache.Set(key, value, DateTimeOffset.Now.AddSeconds(expireTimeSecond)) != null;
        }
    }

  二级缓存实现:

    public class L2Cache : IL2CacheServiceFactory
    {
        private readonly IStateManager stateManager;
        public L2Cache(IStateManager stateManager)
        {
            this.stateManager = stateManager;
        }
        public async Task<T> GetAsync<T>(string key)
        {
            var cache = await stateManager.GetState(new L2CacheStore(key),typeof(T));
            Console.WriteLine($"L2缓存被调用,KEY={key},value{(cache == null ? "不存在" : "存在")}");
            if (cache != null)
                return (T)cache;
            return default(T);
        }

        public async Task<bool> SetAsync<T>(string key, T value, int expireTimeSecond = 0)
        {
            var resp = await stateManager.SetState(new L2CacheStore(key, value, expireTimeSecond));
            return resp != null;
        }
    }
    internal class L2CacheStore : StateStore
    {
        public L2CacheStore(string key, object data, int expireTimeSecond = 0)
        {
            Key = $"DarpEshopL2CacheStore_{key}";
            this.Data = data;
            this.TtlInSeconds = expireTimeSecond;
        }
        public L2CacheStore(string key)
        {
            Key = $"DarpEshopL2CacheStore_{key}";
        }
        public override string Key { get; set; }
        public override object Data { get; set; }
    }

  接着我们将这两个实现注入到我们的webapplication里并在middleware里通过use启动它:

builder.Services.AddMemoryCache();
builder.Services.InjectionCached<L1Cache, L2Cache>();
//......
var app = builder.Build();
app.UseCached();
//......
await app.RunAsync();

  最后我们在AccountQueryService.cs里对GetMockAccount添加对应的缓存注解:

        [SystemCached]
        public async Task<ApiResult> GetMockAccount()
        {
            Console.WriteLine("GetMockAccount被调用");
            //......
        }

  *默认注解参数为int expireSecond = 60, int timeOutMillisecond = 5000, SystemCachedType cachedType = SystemCachedType.Method。其中expireSecond代表你想要的缓存的时间,单位为秒,timeOutMillisecond是指在框架尝试请求二级缓存时的超时等待时长,单位毫秒。cachedType代表缓存类别,SystemCachedType.Method代表仅使用方法作为缓存key,SystemCachedType.MethodAndParams代表会根据方法名+参数来实现更加细化的缓存key

  最后启动我们的项目,现在通过postman来访问这个接口,打印日志如下:

 

   可以看到首先会尝试访问L1缓存实现,如果没有找到对应的key会尝试访问L2缓存实现,最终会访问原始服务并将缓存结果进行多级缓存,所以当再次请求这个接口时将不再产生对原始服务的调用:

 

   接下来我们停止掉这个pod,让k8s重启一个新的pod来模拟L1缓存失效时的情况,可以看到首次调用时L2缓存被调用并且会重新覆写到L1,后面再次调用都会命中L1的缓存:

 

 

  演示很简单,大家可以下载最新版本的代码rebuild通过调用创建订单并log accountservice来观察多级缓存是否起作用。

  下面来讲解一下多级缓存的实现思路:

 

  从流程图里看起来很简单,其实就是一个AOP原理的实现,通过systemcached注解为对应的方法体创建一个proxy代理并注入到IOC容器中。当方法被调用时被proxy拦截到请求后依次串行调用L1、L2、realservice实现。

  具体有兴趣的朋友可以看看github上的代码:https://github.com/sd797994/Oxygen-MultilevelCache

posted @ 2022-01-25 16:31  a1010  阅读(1189)  评论(6编辑  收藏  举报