通過Dapr實現一個簡單的基於.net的微服務電商系統(十八)——服務保護之多級快取

  • 2022 年 1 月 25 日
  • 筆記

  很久沒有更新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上的程式碼://github.com/sd797994/Oxygen-MultilevelCache