手把手教你学Dapr - 5. 状态管理
介绍
使用状态管理,您的应用程序可以将数据作为键/值对存储在支持的状态存储中。
您的应用程序可以使用 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的平均温度,请使用:
保存并获取状态
状态管理是任何应用程序最常见的需求之一:新的或遗留的、单体或微服务。处理不同的数据库、测试、处理重试和故障可能既费时又费力。
先决条件
准备好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),备注来意,邀请进群

手把手教你学Dapr - 5. 状态管理的更多相关文章
- 手把手教你学Dapr - 6. 发布订阅
上一篇:手把手教你学Dapr - 5. 状态管理 介绍 发布/订阅模式允许微服务使用消息相互通信.生产者或发布者在不知道哪个应用程序将接收它们的情况下向主题发送消息.这涉及将它们写入输入通道.同样,消 ...
- 手把手教你学Dapr - 8. 绑定
目录 手把手教你学Dapr - 1. .Net开发者的大时代 手把手教你学Dapr - 2. 必须知道的概念 手把手教你学Dapr - 3. 使用Dapr运行第一个.Net程序 手把手教你学Dapr ...
- 手把手教你学Dapr - 9. 可观测性
目录 手把手教你学Dapr - 1. .Net开发者的大时代 手把手教你学Dapr - 2. 必须知道的概念 手把手教你学Dapr - 3. 使用Dapr运行第一个.Net程序 手把手教你学Dapr ...
- 手把手教你学Dapr - 3. 使用Dapr运行第一个.Net程序
上一篇:手把手教你学Dapr - 2. 必须知道的概念 注意: 文章中提到的命令行工具即是Windows Terminal/PowerShell/cmd其中的一个,推荐使用Windows Termin ...
- 手把手教你学Dapr - 4. 服务调用
上一篇:手把手教你学Dapr - 3. 使用Dapr运行第一个.Net程序 介绍 通过使用服务调用,您的应用程序可以使用标准的gRPC或HTTP协议与其他应用程序可靠.安全地通信. 为什么不直接用Ht ...
- 手把手教你学Dapr - 7. Actors
上一篇:手把手教你学Dapr - 6. 发布订阅 介绍 Actor模式将Actor描述为最低级别的"计算单元".换句话说,您在一个独立的单元(称为actor)中编写代码,该单元接收 ...
- 手把手教你学Dapr - 1. .Net开发者的大时代
Dapr全称 Distributed Application Runtime,分布式应用运行时 Dapr的口号 简化云原生应用开发,聚焦在应用的核心逻辑,让代码简单.可移植 Dapr的目标 最佳实践的 ...
- 手把手教你学Dapr - 2. 必须知道的概念
Sidecar 边车 Dapr API提供Http和gRPC两种通讯方式. 运行方式则可以是容器也可以是进程(Windows开发推荐使用Self Hosted,后续会解释). 这样的好处是与运行环境无 ...
- 每天记录一点:NetCore获得配置文件 appsettings.json vue-router页面传值及接收值 详解webpack + vue + node 打造单页面(入门篇) 30分钟手把手教你学webpack实战 vue.js+webpack模块管理及组件开发
每天记录一点:NetCore获得配置文件 appsettings.json 用NetCore做项目如果用EF ORM在网上有很多的配置连接字符串,读取以及使用方法 由于很多朋友用的其他ORM如S ...
随机推荐
- Docker小白到实战之Docker网络简单了解一下
前言 现在对于Docker容器的隔离性都有所了解了,但对容器IP地址的分配.容器间的访问等还是有点小疑问,如果容器的IP由于新启动导致变动,那又怎么才能保证原有业务不会被影响,这就和网络有挂钩了,接下 ...
- 深入浅出WPF-06.Binding(绑定)02
Binding对数据的校验和转换 1)数据校验,通过Binding的ValidationRules属性(Collection类型)进行校验.可以设置多个数据校验条件. <Slider Margi ...
- 1.pytest初尝试
语法规范 pytest命令会默认执行以 -- test_**.py -- 或 -- **_test.py -- 命名文件的测试用例 pytest的测试用例必须以 -- test -- 开头 def t ...
- HTML选择器的四种使用方法
选择器<style> 为了让.html代码更加简洁,这里引入选择器style 本文总共介绍选择器的四种使用方式 一.选择器的四种形式 1.ID选择器 id表示身份,在页面元素中的id不允许 ...
- JVM学习笔记——方法区
方法区 Method Area 方法区在逻辑上属于堆的一部分,但可以看做是一块独立于 Java 堆的内存空间.所有的字段和方法字节码,以及一些特殊的方法,如构造函数,接口代码在此定义.所有定义方法的信 ...
- Serverless 对研发效能的变革和创新
作者 | 杨皓然(不瞋) 对企业而言,Serverless 架构有着巨大的应用潜力.随着云产品的完善,产品的集成和被集成能力的加强,软件交付流程自动化能力的提高,我们相信在 Serverless 架构 ...
- 关于我的Visual Studio2017修改安装重试9次失败问题解决
环境:windows vs2017 现象描述:本来我的vs可用的,一天想暗爪QT,插件安装好了不能创建QT项目,准备升级,然后问题出现了. 过程: 把人弄崩溃了,二三十k的速度在走,半路又终止.根本不 ...
- jenkins容器内安装python3
前言 很多小伙伴可能在考虑 jenkins 拉取了 github 上的代码后,发现还越少 python3 环境,那能怎么办呢? 咨询了一位运维朋友给我的答案是,将 python3 挂载到容器工作目录上 ...
- docker环境下搭建python3.6
前言:当我们在一台电脑上搭建了python3.6的环境,下次换了个电脑或者换成linux的系统了又得重新搭建一次,设置环境变量,下载pip等操作.所以使用docker 一.安装python步骤: 1. ...
- 深度学习——手动实现残差网络ResNet 辛普森一家人物识别
深度学习--手动实现残差网络 辛普森一家人物识别 目标 通过深度学习,训练模型识别辛普森一家人动画中的14个角色 最终实现92%-94%的识别准确率. 数据 ResNet介绍 论文地址 https:/ ...