第 6 章 Dapr 状态管理构建块

The Dapr state management building block | Microsoft Docs

分布式应用程序由一组独立的服务构成。尽管每个服务应当是无状态的,有些服务必须却必须跟踪状态的变化以生成业务操作。例如对于电子商务系统中的购物车服务,如果该服务不能跟踪状态,那么在离开站点的时候,顾客就会丢失购物车的内容,导致销售机会的丧失和顾客体验的下降。对于这些场景,状态需要被持久化到分布式的状态存储中。Dapr 状态管理构建块简化了状态跟踪处理,并提供跨多种数据存储的高级功能。

尝试状态管理构建块的话,可以看一下第 3 章中的计数器示例应用程序。

处理何种问题

在分布式应用程序中跟踪状态是一种挑战,例如:

  • 应用程序可能需要不同类型的数据存储
  • 对访问数据和更新数据的不同一致性级别
  • 在同一时间可能由多个用户更新数据,需要冲突处理方案
  • 在与数据存储交互的时候,出现暂时的瞬时错误必须能够进行重试

Dapr 状态管理构建块定位于处理这些挑战,在不需要依赖,或者学习第 3 方存储 SDK 学习曲线的情况下,Dapr 平滑地处理了状态跟踪问题。

重要:

Dapr 状态管理提供 key/value API。该功能不支持关系存储,以及图数据存储。

如何工作

应用程序与 Dapr 的 sidebar 交互来存储并提供 key/value 数据。在该机制的背后,sidebar API 基于可配置的状态存储组件来持久化数据。开发人员可以从众多支持的状态存储中选择,包括 Azure Cosmos DB,SQL Server,以及 Cassandra。

该 API 即可以使用 HTTP 也可以使用 gRPC 进行调用,使用如下所示的 URL 调用 HTTP API

http://localhost:<dapr-port>/v1.0/state/<store-name>/
  • : Dapr 监听的 HTTP 端口
  • : 状态存储组件使用的名称

图 5-1 展示了使用 Dapr 的购物车服务,使用名为 statestore 的 Dapr 存储组件存储键值对。

图 5-1 在 Dapr 状态存储中存储 key/value

注意前图中的步骤:

  1. 购物车服务调用 Dapr sidebar 的状态管理 API。请求的 body 部分封装了多个 key/value 对。
  2. Dapr sidebar 基于组件配置文件检查状态存储,在本例中,使用 Redis 缓存状态存储。
  3. sidecar 持久化数据到 Redis cache 中

获取存储的数据通过简单的 API 调用完成。在下面的示例中,通过 curl 命令调用 Dapr sidecar 的 API 来提取数据。

curl http://localhost:3500/v1.0/state/statestore/basket1

该命令的响应体中包含了存储的数据。

{
"items": [
{
"itemId": "DaprHoodie",
"quantity": 1
}
],
"customerId": 1
}

下面开始介绍如何使用更高级状态管理构建块特性。

一致性

CAP 理论是一套应用于存储状态的分布式系统的理论。图 5-2 展示了 CAP 理论的 3 个属性。

图 5-2 CAP 理论

CAP 理论指出分布式系统在一致性、可用性和分区容差之间的权衡。进而,任何数据分布式数据存储只能保证三种属性中的两种。

  • 一致性(C)。群集中的每个节点都会使用最新的数据进行响应,即使系统必须在所有副本更新之前阻止请求。如果查询当前正在更新的项目的“一致系统”,则在所有副本成功更新之前,不会得到响应。但是,您将始终收到最新的数据。
  • 可用性(A)。每个节点都会立即返回响应,即使该响应不是最新的数据。如果您在“可用系统”中查询正在更新的项目,您将得到该服务在此时可能提供的最佳答案。
  • 分区公差(P)。保证即使复制数据节点出现故障或与其他复制数据节点失去连接,系统仍能继续运行。

分布式应用程序必须处理 P 属性。当服务通过网络呼叫相互通信时,将发生网络中断(P)。考虑到这一点,分布式应用程序必须是 AP 或 CP。

AP:应用程序选择可用性而不是一致性。Dapr 通过其 eventual consistency 最终一致性策略支持这一选择。考虑底层数据存储,例如Azure CosmosDB,它在多个副本上存储冗余数据。为了最终的一致性,状态存储将更新写入一个副本,并与客户端一起完成写入请求。在此之后,存储将异步更新其副本。读取请求可以从任何副本返回数据,包括尚未收到最新更新的副本。

CP:应用程序选择一致性而不是可用性。Dapr 以其 strong consistency 强一致性策略支持这一选择。在这种情况下,状态存储将在完成写入请求之前同步更新所有(或在某些情况下,一组)所需的副本。读取操作将在多个副本中一致地返回最新数据。

状态操作的一致性级别是通过向操作附加 一致性提示 来指定的。以下 curl 命令使用强一致性提示将 Hello=World键/值对写入状态存储:

curl -X POST http://localhost:3500/v1.0/state/<store-name> \
-H "Content-Type: application/json" \
-d '[
{
"key": "Hello",
"value": "World",
"options": {
"consistency": "strong"
}
}
]'

重要

实际是由底层的 Dapr 存储组件完成该操作的一致性提示。不是所有的数据存储都支持所有这两种一致性级别。如果没有设置一致性提示,默认的行为为 eventual

并发性

在多用户应用程序中,多个用户有可能同时更新相同的数据。Dapr 支持乐观并发控制(OCC)来管理冲突。OCC基于这样一种假设:由于用户处理数据的不同部分,所以更新冲突不常见。更有效的方法是假设更新会成功,如果不成功则重试。另一种方法是实现悲观锁定,这会影响性能,因为长时间运行的锁定会导致数据争用。

Dapr 支持使用 ETag 的乐观并发控制(OCC)。ETag 是与存储的密钥/值对的特定版本关联的值。每次密钥/值对更新时,ETag 值也会更新。当客户端检索密钥/值对时,响应包括当前 ETag 值。当客户端更新或删除密钥/值对时,它必须将该 ETag 值发送回请求正文。如果另一个客户端同时更新了数据,ETag 将不匹配,请求将失败。此时,客户端必须检索更新的数据,再次进行更改,然后重新提交更新。这种策略称为 first-write-wins

Dapr 还支持 last-write-wins 策略。使用这种方法,客户机不会将 ETag 附加到写请求。状态存储组件将始终允许更新,即使在会话期间基础值已更改。last-write-wins 对于低数据争用的高吞吐量写入场景非常有用。此外,可以容忍覆盖偶尔的用户更新。

事务

Dapr可以将多项更改作为一个事务实现的单个操作写入数据存储。此功能仅适用于支持 [ACID 事务](https://en.wikipedia.org/wiki/ACID> 的数据存储。在撰写本文时,这些存储包括Redis、MongoDB、PostgreSQL、SQL Server和Azure CosmosDB。

在下面的示例中,多项目操作在单个事务中发送到状态存储。所有操作都必须成功,事务才能提交。如果一个或多个操作失败,则整个事务回滚。

curl -X POST http://localhost:3500/v1.0/state/<store-name>/transaction \
-H "Content-Type: application/json" \
-d '{
"operations": [
{
"operation": "upsert",
"request": { "key": "Key1", "value": "Value1"
}
},
{
"operation": "delete",
"request": { "key": "Key2" }
}
]
}'

对于不支持事务的数据存储,多组键值仍然可以作为单个请求发送。下面的示例展示了 bulk 写操作。

curl -X POST http://localhost:3500/v1.0/state/<store-name> \
-H "Content-Type: application/json" \
-d '[
{ "key": "Key1", "value": "Value1" },
{ "key": "Key2", "value": "Value2" }
]'

对于批量操作,Dapr 将提交每单组的键值更新作为独立的请求到数据存储中。

使用 Dapr .NET SDK

Dapr .NET SDK 对 .NET 平台提供特定语言的支持。开发者可以使用在第 3 章中介绍的 DaprClient 类来读取和写入数据。下面的示例展示了如何使用 DaprClient.GetStateAsync<TVale> 方法从数据存储中读取数据。该方法需要使用存储的名称,statestore 和键值来作为参数:

var weatherForecast
= await daprClient.GetStateAsync<WeatherForecast>("statestore", "AMS");

如果存储中没有关于键值 AWS 的数据,返回结果为 default(WeatherForecast)

写入数据到数据存储中,使用 DaprClient.SaveStateAsync<TValue> 方法。

daprClient.SaveStateAsync("statestore", "AMS", weatherForecast);

示例使用 last-write-wins 策略,因为没有提供 ETag 到存储组件中。为了使用乐观并发控制 (OCC) 的 first-write-wins 策略,首先使用 DaprClient。GetStateAndEtagAsync 方法获取当前的 ETag 值,然后带有获取的 Etag 来写入更新之后的值,使用 DaprClient.TrySaveStateAsync 方法。

var (weatherForecast, etag) = await daprClient.GetStateAndETagAsync<WeatherForecast>("statestore", city);

// ... make some changes to the retrieved weather forecast

var result = await daprClient.TrySaveStateAsync("statestore", city, weatherForecast, etag);

在数据读取之后,当数据 ( 关联的 ETag ) 在存储中变化之后, DaprClient.TrySaveStateAsync 方法将会失败。该方法返回一个 boolean 值来指示该调用是否成功。处理失败的一种策略是简单地从存储中重新加载数据,重新变更,最后重新提交更新。

如果你总是期望写入成功,而不管对数据的其它变更,使用 last-write-wins 策略。

该 SDK 提供了其它方法来批量获取数据,删除数据,以及执行事务。更多信息,请参考:Dapr .NET SDK repository

ASP.NET Core 集成

Dapr 也支持 ASP.NET Core,一种跨平台的构建现代云基础的 Web 应用程序的框架。Dapr SDK 提供了直接集成状态管理能力到 ASP.NET Core 模型绑定 的能力。通过在 IMVCBuilder.AddDapr 后面追加 .AddDapr 扩展方法到你的 Startup.cs 类中进行简单的配置。如下所示:

public void ConfigureServices(IServiceCollection services)
{
services.AddControllers().AddDapr();
}

一旦配置完成,Dapr 可以通过 ASP.NET Core 的 FromState 特性,直接注入 key/value 对到控制器的 Action 方法中。不再需要引用 DaprClient 对象。下面的示例展示了对给定城市返回天气预报的一个 Web API。

[HttpGet("{city}")]
public ActionResult<WeatherForecast> Get([FromState("statestore", "city")] StateEntry<WeatherForecast> forecast)
{
if (forecast.Value == null)
{
return NotFound();
} return forecast.Value;
}

在该示例中,控制器使用 FromState 特性加载天气预报。第一个特性参数是状态存储,statestore 。第二个特性参数是 city,它是路由模板变量的名称,用来获取状态的键值。如果你忽略掉第二个参数,绑定方法的参数 forecast 将被用于查找路由模板变量。

StateEntry 包含提取的键值对的所有信息的属性:StoreNmaeKeyValue 以及 Etag。ETag 用于实现乐观并发控制 (OCC)。该类还提供用于删除和更新获取的键值对而不需要 DaprClient 实例。在下一个示例中,TrySaveAsync 方法用来使用 OCC 更新获取的天气预报。

[HttpPut("{city}")]
public async Task Put(WeatherForecast updatedForecast, [FromState("statestore", "city")] StateEntry<WeatherForecast> currentForecast)
{
// update cached current forecast with updated forecast passed into service endpoint
currentForecast.Value = updatedForecast; // update state store
var success = await currentForecast.TrySaveAsync(); // ... check result
}

状态存储组件

到本书编写时为止,Dapr 提供如下的事务状态存储:

  • Azure CosmosDB
  • Azure SQL Server
  • MongoDB
  • PostgreSQL
  • Redis

Dapr 还包括对支持 CRUD,但是没有事务支持的状态存储:

  • Aerospike
  • Azure Blob Storage
  • Azure Table Storage
  • Cassandra
  • Cloudstate
  • Couchbase
  • etcd
  • Google Cloud Firestore
  • Hashicorp Consul
  • Hazelcast
  • Memcached
  • Zookeeper

配置

当初始化本地、自寄宿的开发时,Dapr 注册 Redis 作为默认的状态存储。下面是默认的状态存储配置示例。注意默认的名称: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"

注意

可以对单个应用程序使用不同的名称注册多个状态存储。

Redis 状态存储要求 redisHostredisPassword 元数据来连接到 Redis 实例。在上面的示例中,Redis 口令 (默认是空字符串) 作为明文存储。最佳实践是避免明文字符串,总是使用引用密钥。关于密钥存储,参见第 10 章。

其它的元数据字段 actorStateStore,指示状态存储如何被 actors 构建块所消费。

键前缀策略

在底层存储中,状态管理组件使用不同的策略来管理存储的 key/value 对。回顾签名的示例

curl -X POST http://localhost:3500/v1.0/state/statestore \
-H "Content-Type: application/json" \
-d '[{
"key": "basket1",
"value": {
"customerId": 1,
"items": [
{ "itemId": "DaprHoodie", "quantity": 1 }
]
}
}]'

使用 Redis 控制台工具,可以查看内部的 Redis 缓存来了解 Redis 状态存储是如何持久化数据的。

127.0.0.1:6379> KEYS *
1) "basketservice||basket1" 127.0.0.1:6379> HGETALL basketservice||basket1
1) "data"
2) "{\"items\":[{\"itemId\":\"DaprHoodie\",\"quantity\":1}],\"customerId\":1}"
3) "version"
4) "1"

输出显示了完整的 Redis 用于数据的 Key 是 basketservice||basket1。默认情况下,Dapr 使用 Dapr 实例的应用程序标识 (application_id) 作为键的前缀。这种命名约定使得多个 Dapr 实例可以共享同一个数据存储而不会出现键名称冲突。对于开发者来说,关键是在运行使用 Dapr 的应用程序时,总是指定相同的 application_id。如果忽略了,Dapr 将生成一个唯一的 application ID。而如果 application id 变化了,应用程序就不能访问使用以前的键保存的状态了。

也就是说,在

示例应用:Dapr 流量管控

总结

Dapr-6: Dapr 状态管理构建块的更多相关文章

  1. 面向.NET开发人员的Dapr- actors 构建块

    原文地址:https://docs.microsoft.com/en-us/dotnet/architecture/dapr-for-net-developers/actors The actor m ...

  2. Dapr-Actor构建块

    前言: 前篇-绑定 文章对Dapr的绑定构建块进行了解,本篇继续对 Actor 构建块进行了解学习. 一.Actor简介: Actors 为最低级别的"计算单元". 换句话说,您将 ...

  3. Dapr Workflow构建块的.NET Demo

    Dapr 1.10版本中带来了最有亮点的特性就是工作流构建块的的发布,虽然是Alpha 阶段,可以让我们尽早在应用系统中规划工作流, 在使用Dapr的系统中更好的编写负责的分布式应用系统.Dapr 工 ...

  4. 通过Dapr实现一个简单的基于.net的微服务电商系统(五)——一步一步教你如何撸Dapr之状态管理

    状态管理和上一章的订阅发布都算是Dapr相较于其他服务网格框架来讲提供的比较特异性的内容,今天我们来讲讲状态管理. 目录:一.通过Dapr实现一个简单的基于.net的微服务电商系统 二.通过Dapr实 ...

  5. Blazor+Dapr+K8s微服务之状态管理

    1         状态管理服务器端接口 1.1         添加Dapr.AspNetCore包 在DaprTest1.Server项目中添加Dapr.AspNetCore包,该包实现了ASP. ...

  6. Dapr实战(三)状态管理

    状态管理解决了什么 分布式应用程序中的状态可能很有挑战性. 例如: 应用程序可能需要不同类型的数据存储. 访问和更新数据可能需要不同的一致性级别. 多个用户可以同时更新数据,这需要解决冲突. 服务必须 ...

  7. 手把手教你学Dapr - 5. 状态管理

    上一篇:手把手教你学Dapr - 4. 服务调用 介绍 使用状态管理,您的应用程序可以将数据作为键/值对存储在支持的状态存储中. 您的应用程序可以使用 Dapr 的状态管理 API 使用状态存储组件来 ...

  8. Dapr-绑定构建块

    前言: 前篇-发布订阅文章对Dapr的订阅/发布进行了解,本篇继续对 绑定 构建块进行了解. 一.简介: Dapr 资源绑定使服务能够跨即时应用程序外部的外部资源集成业务操作. 来自外部系统的事件可能 ...

  9. 对CLR异常和状态管理的一点理解

    一:自己的感悟 今天读到<CLR via C#>的异常和状态管理这一章,作者给出了关于异常处理的诸多建议,里面有一些建议自己深有体会,比如说使用可靠性换取开发效率这一节.之前自己对异常怎么 ...

  10. Redux状态管理方法与实例

    状态管理是目前构建单页应用中不可或缺的一环,也是值得花时间学习的知识点.React官方推荐我们使用Redux来管理我们的React应用,同时也提供了Redux的文档来供我们学习,中文版地址为http: ...

随机推荐

  1. CF102411 ICPC 2019-2020 North-Western Russia Regional Contest题解

    A Accurate Movement 签到 M Managing Difficulties 签到 B Bad Treap 已知\(y=\sin(x)\),要求给出数组\(a[n]\),满足\(\fo ...

  2. torch.max()使用讲解

    output = torch.max(input, dim) input输入的是一个tensor dim是max函数索引的维度0/1,0是每列的最大值,1是每行的最大值 实例: import torc ...

  3. 数据库运维实操优质文章分享(含Oracle、MySQL等) | 2023年6月刊

    本文为大家整理了墨天轮数据社区2023年6月发布的优质技术文章/文档,主题涵盖Oracle.MySQL.PostgreSQL等数据库的安装配置.故障处理.性能优化等日常实践操作,以及常用脚本.注意事项 ...

  4. Oracle 23c 新特性实操体验优质文章汇总 | 有奖征文进行中欢迎参与

    继4月3日甲骨文宣布推出免费开发者版 Oracle Database 23c后,墨天轮社区发起 "Oracle 23c 免费开发者版特性体验"有奖征文活动,邀请大家分享Oracle ...

  5. 银河麒麟、中标麒麟学习实操资料汇总(含V4、V7、V10)

    数据库和操作系统关系十分密切,因为数据库是运行于操作系统上的一个管理数据的应用.在数据库国产化替代的浪潮之下,一批批国产操作系统也崭露头角.墨天轮社区便选取了中国操作系统排行榜上排名靠前的麒麟软件,依 ...

  6. Linux 系统常见 的命令

    uname -a 查看linux 的版本信息 : pwd :打印当前的工作目录 ,print work directory: cd  改变目录 ,change directory : cd . 当前目 ...

  7. RocketMQ 5.0 多语言客户端的设计与实现

    本文作者:古崟佑,阿里云中间件开发. RocketMQ 5.0 版本拥有非常多新特性,比如存储计算分离. batch 能力的提升等,它是具有里程碑意义的版本. 提到新版本,我们往往会首先想到服务端架构 ...

  8. direasch目录扫描

    direasch目录扫描工具 安装: 1.github源码下载解压 使用 git 安装:(推荐git clone https://github.com/maurosoria/dirsearch.git ...

  9. 5道大厂的JAVA经典面试题

    前言 本来想着给自己放松一下,刷刷博客,慕然回首,Java的四种引用,强弱软虚?泛型常用特点?Java创建对象有几种方式? 有没有可能两个不相等的对象有相同的hashcode?深拷贝和浅拷贝的区别是什 ...

  10. .NET 高性能异步套接字库,支持多协议、跨平台、高并发

    前言 .NET 生态中有哪些值得推荐的网络通信框架?今天,给大家推荐一个非常优秀的开源项目--NetCoreServer. NetCoreServer 是一款 .NET 开源.免费.快速且低延迟的异步 ...