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

a1010發表於2022-01-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上的程式碼:https://github.com/sd797994/Oxygen-MultilevelCache

相關文章