通過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的微服務電商系統(十八)——服務保護之多級緩存
附錄:(如果你覺得對你有用,請給個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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章