目錄
手把手教你學Dapr - 3. 使用Dapr運行第一個.Net程序
介紹
使用狀態管理,您的應用程序可以將數據作為鍵/值對
存儲在支持的狀態存儲中。
您的應用程序可以使用 Dapr 的狀態管理 API 使用狀態存儲組件來保存和讀取鍵/值對,如下圖所示。例如,通過使用 HTTP POST,您可以保存鍵/值對,通過使用 HTTP GET,您可以讀取鍵並返回其值。
特性
可插拔狀態存儲
Dapr 數據存儲被建模為組件,可以在不更改代碼的情況下更換它。例如:MySQL、Redis、Azure CosmosDB等。
可配置的狀態存儲行為
Dapr 允許開發人員將額外的元數據附加到狀態操作請求中,用以描述請求的處理方式。如:
- 並發要求
- 一致性要求
默認
情況下,您的應用程序應假定數據存儲最終一致
並使用最后寫入獲勝
的並發模式
並發
Dapr 支持使用 ETags 的樂觀並發控制
(OCC)。當請求狀態時,Dapr 總是將 ETag 屬性附加到返回的狀態。當用戶代碼嘗試更新或刪除狀態時,應該通過請求正文附加 ETag 以進行更新或通過 If-Match
標頭進行刪除。只有當提供的 ETag 與狀態存儲中的 ETag 匹配時,寫操作才能成功。建議您在使用 ETag 時使用重試策略
來補償此類沖突。
如果您的應用程序在寫入請求時省略 ETag,則 Dapr 在處理請求時會跳過 ETag 檢查。與使用 ETag 的先寫贏模式相比,這實質上啟用了最后寫贏
模式。
自動加密
Dapr 支持應用程序狀態的自動客戶端加密,並支持密鑰輪換。這是一項預覽功能,所有 Dapr 狀態存儲都支持。
一致性
Dapr 支持強一致性和最終一致性,最終一致性
作為默認
行為。
-
當使用強一致性時,Dapr 在確認寫入請求之前等待所有副本(或指定的仲裁)確認。
-
當使用最終一致性時,一旦底層數據存儲接受寫入請求,Dapr 就會立即返回,即使這是單個副本。
批量操作
Dapr 支持兩種類型的批量操作 - 批量(bulk
)或多(multi
)。
注
:bulk與multi的區別在於bulk不是事務性的,multi是事務處理。
Actor狀態
事務狀態存儲可用於存儲Actor狀態。要指定用於Actor的狀態存儲,請在狀態存儲組件的元數據部分中將屬性 actorStateStore
的值指定為 true
。
注
:Actors 狀態以特定方案存儲在事務狀態存儲中允許一致的查詢。所以只能有一個狀態存儲組件被用於所有的Actor。
直接查詢狀態存儲
Dapr 無需任何轉換即可保存和檢索狀態值。您可以直接從底層狀態存儲查詢和聚合狀態。
例如,要在 Redis 中獲取與應用程序 ID “myApp” 關聯的所有狀態鍵,請使用:
KEYS "myApp*"
查詢Actor狀態
如果數據存儲支持 SQL 查詢,您可以使用 SQL 查詢查詢參與者的狀態。例如使用:
SELECT * FROM StateTable WHERE Id='<app-id>||<actor-type>||<actor-id>||<key>'
您還可以跨Actor實例執行聚合查詢,避免Actor 框架常見的基於回合的並發限制。例如,要計算所有溫度計Actor的平均溫度,請使用:
SELECT AVG(value) FROM StateTable WHERE Id LIKE '<app-id>||<thermometer>||*||temperature'
保存並獲取狀態
狀態管理是任何應用程序最常見的需求之一:新的或遺留的、單體或微服務。處理不同的數據庫、測試、處理重試和故障可能既費時又費力。
先決條件
准備好Dapr運行環境可以看之前的文章
手把手教你學Dapr - 3. 使用Dapr運行第一個.Net程序
設置狀態存儲
Windows打開目錄%USERPROFILE%\.dapr\components
-
創建文件
statestore.yaml
-
使用
redis
作為狀態存儲的數據庫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"
注
:這個yaml已經通過actorStateStore開啟了Actor狀態
保存和檢索單個狀態
注
:設置 app-id 很重要,因為狀態鍵以該值作為前綴。如果您不設置它,則在運行時為您生成一個,下次運行該命令時將生成一個新的,您將無法再訪問以前保存的狀態。換句話說,如果你要共享狀態可以自定義一個保留app-id作為共享狀態而不是留空。
運行Dapr Sidecar
運行一個空的Sidecar,因為我們只用它來幫助訪問狀態存儲,所以與之前不同的是,dapr run后面沒有接dotnet run去作為某一個程序的Sidecar
dapr run --app-id myapp --dapr-http-port 3500 --dapr-grpc-port 50001
創建客戶端
創建控制台程序,添加Dapr.Client
NuGet包引用。
修改Program.cs
using Dapr.Client;
var storeName = "statestore";
var key = "myFirstKey";
var value = "myFirstValue";
var client = new DaprClientBuilder().Build();
await client.SaveStateAsync(storeName, key, value);
Console.WriteLine("State has been stored");
var data = await client.GetStateAsync<string>(storeName, key);
Console.WriteLine($"Got value: {data}");
Console.ReadKey();
刪除單個狀態
await client.DeleteStateAsync(storeName, key);
通過事務保存和檢索多個狀態
Dapr 還允許您在同一個調用中保存和檢索多個狀態。
var lst = new List<StateTransactionRequest>()
{
new StateTransactionRequest("test1", System.Text.Encoding.UTF8.GetBytes("value1"), StateOperationType.Upsert),
new StateTransactionRequest("test2", System.Text.Encoding.UTF8.GetBytes("value2"), StateOperationType.Upsert),
};
await client.ExecuteStateTransactionAsync(storeName, lst);
var datas = await client.GetBulkStateAsync(storeName, lst.Select(r => r.Key).ToList(), 0);
Console.WriteLine($"Got items: {string.Join(",", datas.Select(d => $"{d.Key}={d.Value}"))}");
強一致性
使用強一致性時,Dapr將確保底層狀態存儲在寫入或刪除狀態之前,一旦數據被寫入到所有副本或收到來自quorum的ack,就會返回響應。
對於GET請求,Dapr 將確保存儲在副本之間一致地返回最新數據。默認為最終一致性,除非在對狀態 API 的請求中另有說明。
await client.SaveStateAsync(storeName, key, value, new StateOptions() { Consistency = ConsistencyMode.Strong });
var etagData = await client.GetStateAndETagAsync<string>(storeName, key, ConsistencyMode.Strong);
Console.WriteLine($"ETag:{etagData.etag}");
await client.DeleteStateAsync(storeName, key, new StateOptions() { Consistency = ConsistencyMode.Strong });
先寫贏和最后寫贏
Dapr 允許開發人員在使用數據存儲時選擇兩種常見的並發模式:首先寫入獲勝
和``最后寫入獲勝`。 First-Write-Wins 在您有多個應用程序實例的情況下很有用,所有實例都同時寫入同一個鍵。
Dapr 的默認
模式是最后寫入獲勝
。
下面的例子展示了如何獲取一個 ETag,然后使用它來保存狀態,然后刪除狀態:
await client.SaveStateAsync(storeName, key, value, new StateOptions() { Concurrency = ConcurrencyMode.FirstWrite });
var firstWriteWinData = await client.GetStateAndETagAsync<string>(storeName, key);
var etag = firstWriteWinData.etag;
await client.TrySaveStateAsync(storeName, key, DateTime.Now.Ticks.ToString(), etag, new StateOptions() { Concurrency = ConcurrencyMode.FirstWrite });
var firstWriteWinDeleteSucceeded = await client.TryDeleteStateAsync(storeName, key, etag);
Console.WriteLine($"First write wins delete:{firstWriteWinDeleteSucceeded}");
firstWriteWinData = await client.GetStateAndETagAsync<string>(storeName, key);
firstWriteWinDeleteSucceeded = await client.TryDeleteStateAsync(storeName, key, firstWriteWinData.etag);
Console.WriteLine($"First write wins delete:{firstWriteWinDeleteSucceeded}");
注
:這里演示了ETag在更新后嘗試刪除失敗的例子,最后再重新獲取新的狀態以修正ETag再刪除
在不同的應用程序之間共享狀態
為了實現狀態共享,Dapr 支持以下鍵前綴策略
appid
- 這是默認策略。 appid 前綴允許狀態只能由具有指定 appid 的應用程序管理。所有狀態鍵都將以 appid 為前綴,並以應用程序為范圍。name
- 此設置使用狀態存儲組件的名稱作為前綴。對於給定的狀態存儲,多個應用程序可以共享相同的狀態。none
- 此設置不使用前綴。多個應用程序在不同的狀態存儲之間共享狀態
舉個例子:要指定前綴策略,請在狀態組件上添加名為 keyPrefix 的元數據鍵
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
namespace: production
spec:
type: state.redis
version: v1
metadata:
- name: keyPrefix
value: <key-prefix-strategy>
注
:此示例演示相對較復雜,思路大概是使用多個statestore.yaml,然后根據不同的storename切換不同策略即可。感興趣的小伙伴可以自行嘗試。
自動加密狀態並管理密鑰輪換
注
:截止目前,這個功能是個預覽版,感興趣的小伙伴可以自行嘗試
應用程序狀態通常需要靜態加密,以在企業工作負載或受監管環境中提供更強的安全性。 Dapr 提供基於 AES256 的自動客戶端加密。
狀態的生存時間(TTL)
Dapr 為每個狀態在請求時設置生存時間 (TTL)。這意味着應用程序可以為每個存儲的狀態設置生存時間,並且這些狀態在到期后無法檢索。
注
:只有一部分 Dapr 狀態存儲組件與狀態 TTL 兼容。對於支持的狀態存儲,只需在發布消息時設置 ttlInSeconds
元數據。其他狀態存儲將忽略此值。
await client.SaveStateAsync(storeName, key, value, metadata: new Dictionary<string, string>() { { "ttlInSeconds", "3" } });
var ttlData = await client.GetStateAsync<string>(storeName, key);
Console.WriteLine($"TTL Data:{ttlData}");
Thread.Sleep(5000);
ttlData = await client.GetStateAsync<string>(storeName, key);
Console.WriteLine($"TTL Data:{ttlData}");
持久化狀態
要顯式設置持久化狀態(忽略為鍵設置的任何 TTL),請將 ttlInSeconds
值指定為 -1
。
本章源碼
Assignment05
https://github.com/doddgu/dapr-study-room
我們正在行動,新的框架、新的生態
我們的目標是自由的
、易用的
、可塑性強的
、功能豐富的
、健壯的
。
所以我們借鑒Building blocks的設計理念,正在做一個新的框架MASA Framework
,它有哪些特點呢?
- 原生支持Dapr,且允許將Dapr替換成傳統通信方式
- 架構不限,單體應用、SOA、微服務都支持
- 支持.Net原生框架,降低學習負擔,除特定領域必須引入的概念,堅持不造新輪子
- 豐富的生態支持,除了框架以外還有組件庫、權限中心、配置中心、故障排查中心、報警中心等一系列產品
- 核心代碼庫的單元測試覆蓋率90%+
- 開源、免費、社區驅動
- 還有什么?我們在等你,一起來討論
經過幾個月的生產項目實踐,已完成POC,目前正在把之前的積累重構到新的開源項目中
目前源碼已開始同步到Github(文檔站點在規划中,會慢慢完善起來):
QQ群:7424099
微信群:加技術運營微信(MasaStackTechOps),備注來意,邀請進群