狀態管理和上一章的訂閱釋出都算是Dapr相較於其他服務網格框架來講提供的比較特異性的內容,今天我們來講講狀態管理。
目錄:
一、通過Dapr實現一個簡單的基於.net的微服務電商系統
二、通過Dapr實現一個簡單的基於.net的微服務電商系統(二)——通訊框架講解
三、通過Dapr實現一個簡單的基於.net的微服務電商系統(三)——一步一步教你如何擼Dapr
四、通過Dapr實現一個簡單的基於.net的微服務電商系統(四)——一步一步教你如何擼Dapr之訂閱釋出
五、通過Dapr實現一個簡單的基於.net的微服務電商系統(五)——一步一步教你如何擼Dapr之狀態管理
附錄:(如果你覺得對你有用,請給個star)
一、電商Demo地址
什麼是狀態?簡單來講就是資料狀態。比如我訪問/api/user/1,返回{userid:1,name:xiaoming},無論我的例項有多少個,我通過介面訪問這個url都能夠得到該條資訊。一般的設計中我們的應用是無狀態的,所謂無狀態就是每一個例項並不握持狀態(排除快取的情況),而是通過第三方元件可能是快取元件也可能是資料庫來維持狀態。資料庫維持狀態這個大家都比較熟悉了畢竟都是靠CRUD起家的。另外一種則就是快取狀態,快取狀態有多種方式,最簡單以及最熟悉的就是我們asp.net的session以及system.cache。到了.netcore時代,微軟給我們很貼心的提供了Extensions.Caching.xxx來對我們的快取提供一些外部支援(雖然在fx時代也有,不過相對比較麻煩)。而到了dapr,則對其提供了更進一步的支援包括更廣泛的元件支援列表、非介入性SDK整合(可直接通過http訪問)。通過這樣的方式讓我們的服務很容易對外提供併發安全的、一致性的狀態體驗。
狀態管理和上一章講到的訂閱釋出一樣,主要是依賴於Dapr強大的Component來連線Dapr適配的各種各樣的快取中介軟體,同時對上層(應用)抽象為一組rest api作為讀/寫操作入口,它的讀寫操作格式如下(僅列出部分,完整的API參考這裡):
GET http://localhost:<daprPort>/v1.0/state/<storename>/<key>
POST http://localhost:<daprPort>/v1.0/state/<storename>
DELETE http://localhost:<daprPort>/v1.0/state/<storename>/<key>
state代表我們將呼叫dapr的狀態服務,storename則是我們申明的型別為state的component,key則是我們需要存取到redis的kv鍵值對(值在post body中以json格式傳送)
一份標準的狀態component如下(此處依然以redis為例,檢視這裡是所有支援列表):
apiVersion: dapr.io/v1alpha1 kind: Component metadata: name: statestore spec: type: state.redis version: v1 metadata: - name: redisHost value: redis.infrastructure.svc.cluster.local:6379 - name: keyPrefix value: none
選擇Dapr為我們託管狀態管理的好處是什麼呢?1、我們遮蔽了技術複雜性,避免了在基礎設施層去整合各種型別的狀態中介軟體SDK,2、Dapr為我們實現了分散式併發和資料一致性,具體來講在併發控制方面Dapr提供了一套OCC樂觀併發控制機制,通過附加的etag來做版本校驗確保使用者回寫過程和伺服器端的版本一致才能操作。3、dapr為我們提供了bulk批處理,可以批量插入/刪除資料,這部分demo沒有涉及,大家可以看看這裡
同樣的我們來看看程式碼,狀態管理相較比較簡單,首先我們還是開啟之前的解決方案,在RPC專案裡建立一個model,該model繼承一個StateStore,主要是強制規範統一命名必須包含key,data。
public class TestState : Oxygen.Client.ServerSymbol.Store.StateStore { public TestState() { Key = "TestState"; } public override string Key { get; set; } public override object Data { get; set; } }
接著我們再在ClientCallService的建構函式引入IStateManager依賴,同時在Call方法中我們寫入一個狀態(其他程式碼隨上一章內容不變)
private readonly IServiceProxyFactory serviceProxyFactory; private readonly IStateManager stateManager; public ClientCallService(IServiceProxyFactory serviceProxyFactory, IStateManager stateManager) { this.serviceProxyFactory = serviceProxyFactory; this.stateManager = stateManager; } public async Task<dynamic> Call() { var result1 = new OutDto(); var result2 = new OutDto(); var remoteService = serviceProxyFactory.CreateProxy<IHelloService>(); await stateManager.SetState(new TestState() { Data = new OutDto() { Word = "123" } });try { result1 = await remoteService.HelloWorld(); result2 = await remoteService.HelloWorldByName(new InputDto() { Name = "xiaoming" }); } catch(Exception e) { } return new { result1, result2 }; }
接著我們在servicesample列印出來:
var result = await stateManager.GetState<OutDto>(new TestState()); Console.WriteLine(result.Word);
啟動專案,開啟postman訪問並列印控制檯,可以看到狀態被正確的從clientsaample寫入,並從servicesample讀取列印到了控制檯上(這裡注意如果不想狀態被其他服務讀寫也就是僅能在當前服務的scope內被讀寫可以在設定component時刪除keyPrefix節即可)
狀態管理就講到這裡,整體使用上比較簡單,開發者只需要考慮持久化裝置的可用性以及可擴充套件性,其他都可以交給Dapr即可。
今天補一個小的功能點,在oxygen框架中我為AOP提供了一個入口,可以在ConfigureServices時通過LocalMethodAopProvider這個靜態類的RegisterPipelineHandler方法註冊請求前、方法前、方法後、方法異常四個匿名委託。請求前主要是注入了一個OxygenHttpContextWapper的包裝器類,該類包含了原始請求中的path/header/cookie等等原始data,並提供了一個當前請求的ILifetimeScope用於使用者進行物件注入。在方法前則提供了一個object型別的入參,方便使用者做方法前校驗。方法後則是攔截的方法體返回的result。而異常則是方法內丟擲的所有unhandle異常都會被這個委託捕獲,方便使用者統一處理。
今天的分享就到這裡,歡迎大家評論區留言交流,歡迎對github上star+fork~