很久沒有更新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