狀態管理解決了什么
分布式應用程式中的狀態可能很有挑戰性, 例如:
- 應用程式可能需要不同型別的資料存盤,
- 訪問和更新資料可能需要不同的一致性級別,
- 多個用戶可以同時更新資料,這需要解決沖突,
- 服務必須重試 與資料存盤互動 時發生的任何短期暫時性錯誤,
Dapr 狀態管理解決了這些難題, 它簡化了跟蹤狀態,而無需依賴關系或第三方存盤 SDK 上的學習曲線,
作業原理

應用程式與 Dapr sidecar 互動,以存盤和檢索鍵/值資料, 在底層,sidecar API 使用可配置的狀態存盤組件來保存資料, 開發人員可以從不斷增長的受支持狀態存盤集合中選擇,其中包括 Azure Cosmos DB、SQL Server 和 Cassandra,
可以使用 HTTP 或 gRPC 呼叫 API, 使用以下 URL 呼叫 HTTP API:
http://localhost:<dapr-port>/v1.0/state/<store-name>/
<dapr-port>:Dapr 偵聽的 HTTP 埠,<store-name>:使用的狀態存盤組件的名稱,
狀態組件
Dapr支持的組件
為本地自承載開發初始化時,Dapr 將 Redis 注冊為默認狀態存盤, 下面是默認狀態存盤配置的示例,組態檔位置為C:\Users\<username>\.dapr\components, 記下默認名稱 statestore :
apiVersion: dapr.io/v1alpha1 kind: Component metadata: name: statestore spec: type: state.redis version: v1 metadata: - name: redisHost value: localhost:6379 - name: redisPassword value: "" - name: actorStateStore value: "true"
專案演示
仍然使用 上一篇服務呼叫 的FrontEnd專案,新建StateController
using Dapr; using Dapr.Client; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Threading.Tasks; namespace FrontEnd.Controllers { [Route("[controller]")] [ApiController] public class StateController : ControllerBase { private readonly ILogger<StateController> _logger; private readonly DaprClient _daprClient; public StateController(ILogger<StateController> logger, DaprClient daprClient) { _logger = logger; _daprClient = daprClient; } // 獲取一個值 [HttpGet] public async Task<ActionResult> GetAsync() { var result = await _daprClient.GetStateAsync<string>("statestore", "guid"); return Ok(result); } //保存一個值 [HttpPost] public async Task<ActionResult> PostAsync() { await _daprClient.SaveStateAsync<string>("statestore", "guid", Guid.NewGuid().ToString(), new StateOptions() { Consistency = ConsistencyMode.Strong }); return Ok("done"); } //洗掉一個值 [HttpDelete] public async Task<ActionResult> DeleteAsync() { await _daprClient.DeleteStateAsync("statestore", "guid"); return Ok("done"); } //通過tag防止并發沖突,保存一個值 [HttpPost("withtag")] public async Task<ActionResult> PostWithTagAsync() { var (value, etag) = await _daprClient.GetStateAndETagAsync<string>("statestore", "guid"); await _daprClient.TrySaveStateAsync<string>("statestore", "guid", Guid.NewGuid().ToString(), etag); return Ok("done"); } //通過tag防止并發沖突,洗掉一個值 [HttpDelete("withtag")] public async Task<ActionResult> DeleteWithTagAsync() { var (value, etag) = await _daprClient.GetStateAndETagAsync<string>("statestore", "guid"); return Ok(await _daprClient.TryDeleteStateAsync("statestore", "guid", etag)); } // 從系結獲取一個值,健值name從路由模板獲取 [HttpGet("frombinding/{name}")] public async Task<ActionResult> GetFromBindingAsync([FromState("statestore", "name")] StateEntry<string> state) { return Ok(state.Value); } // 根據系結獲取并修改值,健值name從路由模板獲取 [HttpPost("withbinding/{name}")] public async Task<ActionResult> PostWithBindingAsync([FromState("statestore", "name")] StateEntry<string> state) { state.Value = Guid.NewGuid().ToString(); return Ok(await state.TrySaveAsync()); } // 獲取多個個值 [HttpGet("list")] public async Task<ActionResult> GetListAsync() { var result = await _daprClient.GetBulkStateAsync("statestore", new List<string> { "guid" }, 10); return Ok(result); } // 洗掉多個個值 [HttpDelete("list")] public async Task<ActionResult> DeleteListAsync() { var data = await _daprClient.GetBulkStateAsync("statestore", new List<string> { "guid" }, 10); var removeList = new List<BulkDeleteStateItem>(); foreach (var item in data) { removeList.Add(new BulkDeleteStateItem(item.Key, item.ETag)); } await _daprClient.DeleteBulkStateAsync("statestore", removeList); return Ok("done"); } } }
cmd運行
dapr run --dapr-http-port 3501 --app-port 5001 --app-id frontend dotnet .\FrontEnd\bin\Debug\net5.0\FrontEnd.dll
可通過postman呼叫sidecar的endpoint


查看store存盤中的內容
進入容器內部
docker exec -it dapr_redis /bin/sh
呼叫redis-cli
redis-cli
查看所有key
keys *
可以看到有"frontend||guid"這個key,所以狀態在redis中存盤中Name的規則是appName||keyName,這樣可以防止不同app的鍵沖突
我們通過type key查看下這個鍵的型別,可以發現他是一個hash
127.0.0.1:6379> type frontend||guid hash
再通過hgetall key查看他的資料,發現有兩個鍵,一個data,一個version
127.0.0.1:6379> hgetall frontend||guid 1) "data" 2) "\"e17b3e06-ba30-42c5-8960-48511c70b496\"" 3) "version" 4) "1"
data很明顯是存入的資料,version呢?現在猜測是防止并發沖突的etag,我們下面來驗證一下
在StateController中新增介面
// 獲取一個值和etag [HttpGet("withetag")] public async Task<ActionResult> GetWithEtagAsync() { var (value,etag) = await _daprClient.GetStateAndETagAsync<string>("statestore", "guid"); return Ok($"value is {value}, etag is {etag}"); }
通過dapr重啟這個app,并呼叫withetag api,很明顯redis中version與etag相等,初步印證我們的猜測

我們可以通過post方法修改一下guid這個key,修改后etag會變更,再來看一下redis中version和etag是不是一個東西
首先呼叫POST方法修改值

再呼叫withetag方法,看下etag,發現etag變成了2

在比較一下redis中的version
127.0.0.1:6379> hgetall frontend||guid 1) "data" 2) "\"36a55558-35c3-402c-ac9e-615014eb6904\"" 3) "version" 4) "2"
現在可以確定etag就是redis中的version了
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/302608.html
標籤:.NET技术
