关于c#:手把手教你学Dapr-5-状态管理

3次阅读

共计 6033 个字符,预计需要花费 16 分钟才能阅读完成。

介绍

应用状态治理,您的应用程序能够将数据作为 键 / 值对 存储在反对的状态存储中。

您的应用程序能够应用 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

  1. 创立文件statestore.yaml
  2. 应用 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/dap…

咱们正在口头,新的框架、新的生态

咱们的指标是 自在的 易用的 可塑性强的 功能丰富的 强壮的

所以咱们借鉴 Building blocks 的设计理念,正在做一个新的框架MASA Framework,它有哪些特点呢?

  • 原生反对 Dapr,且容许将 Dapr 替换成传统通信形式
  • 架构不限,单体利用、SOA、微服务都反对
  • 反对.Net 原生框架,升高学习累赘,除特定畛域必须引入的概念,保持不造新轮子
  • 丰盛的生态反对,除了框架以外还有组件库、权限核心、配置核心、故障排查核心、报警核心等一系列产品
  • 外围代码库的单元测试覆盖率 90%+
  • 开源、收费、社区驱动
  • 还有什么?咱们在等你,一起来探讨

通过几个月的生产我的项目实际,已实现 POC,目前正在把之前的积攒重构到新的开源我的项目中

目前源码已开始同步到 Github(文档站点在布局中,会缓缓欠缺起来):

MASA.BuildingBlocks

MASA.Contrib

MASA.Utils

MASA.EShop

BlazorComponent

MASA.Blazor

QQ 群:7424099

微信群:加技术经营微信(MasaStackTechOps),备注来意,邀请进群

正文完
 0