Dapr微服务应用开发系列4:状态管理构件块

  • Dapr微服务应用开发系列0:概述

  • Dapr微服务应用开发系列1:环境配置

  • Dapr微服务应用开发系列2:Hello World与SDK初接触

  • Dapr微服务应用开发系列3:服务调用构件块

题记:这篇介绍状态管理构件块,这个概念相对于微服务框架而言是比较特殊的。

注:本文仅针对非Actor状态存储的情况进行说明,对于Actor状态存储会在讲述Actor的时候一并说明。

原理

要用好这个构件块,首先需要正确理解状态管理的概念。

大部分微服务开发框架或者说指导,都提倡微服务以无状态类型的方式来运行,这种无状态微服务当然更容易进行伸缩,但是在遇到需要处理一些类似Session这样的数据的时候,为了应对分布式的环境往往要借助于外部存储(一般是数据库或者缓存中间件)。但是这样做不可避免引入了对外部服务以及特定协议的依赖。

如果对Service Fabric熟悉的同学,可能对有状态服务这个概念有所了解,这种SF中特有的微服务类型,直接通过运行时和SDK给微服务提供了一种开箱即用的状态管理机制——即可靠集合(Reliable Collections)。这种机制让你可以通过Key/Value的方式来存取相关状态值(业务数据),在某些情况下甚至可以当作一个NoSQL来使用。有状态服务的优势是把数据和业务逻辑作为一个整体来处理,特别适合领域驱动设计为指导的每个微服务对应独立的数据源的原则。

由于Dapr和Service Fabric有一些渊源,所以“有状态”的这个概念也被引入到了Dapr当中,但是这个时候的微服务类型其实还是依旧保持着无状态。因此状态管理构件块本质上是给开发人员提供了一种状态值存取的机制和API,并把状态存储的存储源(也即状态存储组件)和访问协议进行了抽象和屏蔽。原理图如下(其中使用了Redis作为状态存储组件,这也是开发环境默认的组件):

从上图所知,微服务只需要对自己的Dapr边车进行访问,即可完成状态的保存和获取(可批量)。而存取的方式遵循了标准了HTTP规范的谓词。

因而,有了状态管理构件块,微服务轻可以利用其完成临时状态的持久化和微服务间共享,重可以利用其实现有状态服务来保存业务模型。

能力

Dapr的状态管理构件块并非是简简单单为大家提供了一种状态存取的机制(可以从原理图直观的看出),更为重要的是提供了如下额外能力:

  • 隐藏在运行微服务的时候配置存储组件:开发的时候只需要关心Dapr的规范接口,并使用某些简单易得的存储组件来进行调试,比如默认的Redis存储组件;运行的时候可以引入(替换)为其他存储组件,比如Azure CosmosDB,而无需改变业务代码。

  • 内置重试机制:和服务调用构件块一样,状态管理的API提供了内置的重试能力,并可以用同样的语义配置重试策略。这样的重试能力也为并发和一致性等能力提供了基础。

  • 内置并发控制机制:Dapr依赖ETag这一特殊状态属性来保证乐观并发控制的处理。也即在更新或者删除状态的时候,会检查ETag是否匹配,从而决定是否完成数据操作。众所周知,乐观并发这种模式是比较适合数据冲突很少的情况,也即数据的更新主要由不同的业务数据操作而导致。注意:某些状态存储中间件是不支持ETag的,所以Dapr进行了额外的处理模拟了这一机制。

  • 内置一致性处理机制:Dapr支持两种一致性处理——强一致性和最终一致性。对于强一致性,Dapr会等待所有底层请求返回确认信息才最终完成操作;最终一致性不会等待底层请求确认。

  • 批量处理:Dapr的状态管理提供了两种模式的批量处理。Bulk模式用于把一种同类型的请求合并,这种时候不会保证事务性;Multi模式可以把不同类型的请求一起发送,可以保证事务性。

需要注意的是由于Dapr需要支持尽量多的状态存储源,所以必然有一些存储源是无法支持以上所有的能力的(主要是事务能力),可以通过浏览这个列表[1]来确认存储源的支持情况,还算我们常用的Redis、SQL Server、MySQL、PostgreSQL和CosmosDB是可以完整支持。

规范

Dapr状态管理构件块由于提供了这种特定的能力给你的微服务使用,所以给使用的方式制订了如下规范:

  • 由于状态管理依赖于状态组件,所以首先规定了应用状态组件的声明格式

  • 从概念所知,状态的存储需要依赖状态键,所以接着规定了键的构成方式

  • 最为重要的规范是规定了状态存取的HTTP/gRPC的地址格式

状态组件声明

通过如下yaml文件来声明对状态组件的引用(在本地开发环境可以不声明,使用默认的状态存储源):

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:name: <NAME>namespace: <NAMESPACE>
spec:type: state.<TYPE>metadata:- name:<KEY>value:<VALUE>- name: <KEY>value: <VALUE>

由于整个声明的解释,会在后续单独的组件文章中详细展开。我们只要记住其中的 metadata.name 代表了存储源的名称,对于应用程序而言需要匹配的就是这个名称。另外 spec.type 代表了存储源所使用的存储类型,这个对于应用开发者而言可以了解到存储源是否具备完整的状态存储能力。

键的组成模式

从上所知,状态管理构件块是以键值的方式保存数据的,为了保证和存储源的兼容,那么就需要按照一定的模式来定义键的组成。

普通的(非Actor)状态的键为:<App ID>||<state key>

对于应用开发者而言,其实只需要关心 <state key> 即可

请求地址

要对状态进行操作,需要对如下地址进行HTTP/gRPC请求:http://localhost:<daprPort>/v1.0/state/<storename>

其中daprPort代表了Dapr边车的特定协议端口,HTTP默认50001或者gRPC默认3500;storename即是在组件声明中的 metadata.name

保存状态

对上述地址进行POST请求,并传递一个键值对(外加可选的etag)的数组作为请求体,比如:

[{"key": "weapon","value": "DeathStar","etag": "1234"},{"key": "planet","value": {"name": "Tatooine"}}
]

获取状态

对上述地址进行GET请求,并传递状态键作为路由参数:

GET http://localhost:<daprPort>/v1.0/state/<storename>/<key>

返回结果是一个json的对象,具体格式是由你确定(即你保存状态的时候传入什么格式);另外etag会附加在响应头 ETag 当中。

以bulk的方式获取状态

对上述地址进行POST/PUT请求,并传递 bulk 作为路由参数:

POST/PUT http://localhost:<daprPort>/v1.0/state/<storename>/bulk

同时再构建一个如下格式的请求体,把需要获取的状态键放到 keys 数组当中,同时设定 parallelism 的值来确定在存储源中执行查找操作的并行度(如果状态是以分区的方式保存在存储源中的话):

{"keys": [ "key1", "key2" ],"parallelism": 10
}

请求后,响应体是一个包含了键值对数组的json对象:

[{"key": "key1","data": "value1","etag": "1"},{"key": "key2","data": "value2","etag": "1"}
]

删除状态

对上述地址进行GET请求,并传递状态键作为路由参数:

DELETE http://localhost:<daprPort>/v1.0/state/<storename>/<key>

删除状态请求没有响应体,通过响应状态码204来确认删除成功。

事务操作

如果你希望一次请求执行多步操作的话,可以使用这种请求方式。这种请求由于是支持事务的,所以并非所有存储源都支持。

对上述地址进行POST/PUT请求,并传递 transaction 作为路由参数:

POST/PUT http://localhost:<daprPort>/v1.0/state/<storename>/transaction

请求体是一个操作的数组,标明了各个操作要完成的操作类型和状态内容,如:

{"operations": [{"operation": "upsert","request": {"key": "key1","value": "myData"}},{"operation": "delete","request": {"key": "key2"}}],"metadata": {"partitionKey": "planet"}
}

其中 operation 有两种类型:upsert (更新或插入)和 delete(删除)。

目前支持事务操作的存储源有:

  • Redis

  • MongoDB

  • MySQL

  • RethinkDB

  • PostgreSQL

  • SQL Server

  • Azure CosmosDB

DOTNET SDK

由于状态管理构件块为你的应用程序提供了一些和状态相关的操作接口,SDK除了提供 DaprClient 这个客户端封装类方便你使用.NET函数库来操作状态以外,也为ASP.NET Core提供了更加便捷的模型绑定属性标记类 FromStateAttribute 方便你在Controller中通过属性绑定的方式来获取状态。

DaprClient中和状态相关的方法有:

  • GetStateAsync:基于storeName和key获取状态值

  • GetBulkStateAsync:基于storeName和keys列表获取多个状态值

  • GetStateAndETagAsync:基于storeName和key获取状态值和etag

  • GetStateEntryAsync:基于storeName和key获取StateEntry封装类,此类包含了状态的更详细信息

  • SaveStateAsync:基于storeName和key保存状态值

  • ExecuteStateTransactionAsync:执行状态事务操作

  • DeleteStateAsync:基于storeName和key删除状态值

FromStateAttribute可以在Controller的Action中直接获取StateEntry,如:

[HttpGet("{account}")]
public ActionResult<Account> Get([FromState(StoreName)] StateEntry<Account> account)
{if (account.Value is null){return this.NotFound();}return account.Value;
}

用法与例子

其实通过上面对规范的讲解,对状态管理的基本用法应该有一定的理解了。官方文档给出了如下文章来分别讲述了状态管理构件块的3种使用场景:

  1. 如何保存和获取状态[2]:基本的HTTP使用方式,如果希望看到DOTNET SDK的使用方式,需要参考(https://github.com/dapr/dotnet-sdk)中的例子,其中包含了Client的使用和ASP.NET Core中的使用

  2. 如何构建有状态服务[3],其依赖了状态管理构件块提供的并发和一致性特性

  3. 如何在服务之间共享状态[4],通过给状态存储源设置不同的keyPrefix策略让不同的服务之间可以以特定的键组成格式来读取同一个存储源

另外,我的dapr-dotnet-quickstarts开源项目(https://github.com/heavenwing/dapr-dotnet-quickstarts)也包含了状态管理构件块的基本用法的例子:

  • StateManagement:使用原生的HTTP请求来保存、获取和删除状态

  • StateManagementWithSdk:使用SDK的DaprClient来保存、获取和删除状态

参考资料

[1]

列表: https://docs.dapr.io/operations/components/setup-state-store/supported-state-stores/

[2]

如何保存和获取状态: https://docs.dapr.io/developing-applications/building-blocks/state-management/howto-get-save-state/

[3]

如何构建有状态服务: https://docs.dapr.io/developing-applications/building-blocks/state-management/howto-stateful-service/

[4]

如何在服务之间共享状态: https://docs.dapr.io/developing-applications/building-blocks/state-management/howto-share-state/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/304569.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

开线程插数据_python笔记7-多线程之线程同步(锁lock)

前言丨关于吃火锅的场景&#xff0c;小伙伴并不陌生&#xff0c;前面几章笔记里面我都有提到&#xff0c;今天我们吃火锅的场景&#xff1a;吃火锅的时候a同学往锅里下鱼丸&#xff0c;b同学同时去吃掉鱼丸&#xff0c;有可能会导致吃到生的鱼丸。为了避免这种情况&#xff0c;…

一文读懂欧拉函数

欧拉函数φ(N)表示小于或等于N的正整数中与N互质的数的个数。又称φ函数、欧拉商数。下面介绍欧拉函数的几个性质&#xff1a;我们根据这几个性质就可以求出欧拉函数。基本思路是首先置φ(N)N&#xff0c;然后再枚举素数p&#xff0c;将p的整数倍的欧拉函数φ(kp)进行如下操作。…

windows 7在工作组模式下管理 Hyper-V

之前在工作组模式下win7来管理server core 下的hyper-v一直不成功&#xff0c;server core下的相应的防火墙端口也已开放但hyper管理器就是无法和hyper-v主机通讯在2008R2下没有问题&#xff0c;现把解决的过程记录下来&#xff0c;以备后查。1、下载Windows 7 远程服务器管理工…

打工就是 “演戏”,你可以认真,但别太当真

这是头哥侃码的第235篇原创时间飞逝&#xff0c;2021年的春节假期已经结束了。在我的记忆中&#xff0c;以前每逢春节的时候&#xff0c;身边的亲戚和朋友们都会非常忙碌&#xff0c;什么聚个餐&#xff0c;串个门呀&#xff0c;什么拜个年&#xff0c;打个牌啦……但从去年开始…

2018年的第一个工作日,推荐几个优质公众号

2017刚过去不久&#xff0c;我们迎来了2018。工作、生活节奏超快的今天&#xff0c;想要不断提升自我&#xff0c;碎片化阅读学习是你最佳的选择&#xff0c;如果你有一颗学习的心&#xff0c;那这些学习型的公众号&#xff0c;绝对会让你受益匪浅。小编为你精选了技术领域几个…

redis session java获取attribute_redis里的数据结构

Redis作为当前使用非常广泛的内存数据库&#xff0c;在代码层面做了很多极致的优化&#xff0c;已获取更好的性能。其中重要的一部分&#xff0c;就是对于底层数据结构的使用。Redis会根据数据量、数据大小等来优化对于不同结构的使用&#xff0c;从而获得更佳的运行效率和内存…

.NET 云原生架构师训练营(模块二 基础巩固 安全)--学习笔记

2.8 安全认证 VS 授权ASP .NET Core 认证授权中间件认证JWT 认证授权认证 VS 授权认证是一个识别用户是谁的过程授权是一个决定用户可以干什么的过程401 Unauthorized 未授权403 Forbidden 禁止访问ASP .NET Core 认证授权中间件在接收到请求之后&#xff0c;认证&#xff08;A…

java超长字符序列化_String 字符串最长可以有多长?

Java技术栈www.javastack.cn打开网站看更多文章String 类可以说是在 Java 中使用最频繁的类了&#xff0c;就算是刚刚接触 Java 的初学者也不会陌生&#xff0c;因为对于 Java 程序来说&#xff0c;main 方法就是使用一个 String 类型数组来作为参数的(String[] args)。对于这样…

一文读懂 AVL 树

背景AVL 树是一棵平衡的二叉查找树&#xff0c;于 1962 年&#xff0c;G. M. Adelson-Velsky 和 E. M. Landis 在他们的论文《An algorithm for the organization of information》中发表。所谓的平衡之意&#xff0c;就是树中任意一个结点下左右两个子树的高度差不超过 1。&am…

欧几里得范数_从范数到正则化

范数是一个在数学领域中常用的工具&#xff0c;同时也是学习机器学习原理中经常碰到的概念。本文将从范数的定义出发&#xff0c;逐步带你理解其在机器学习中的应用。首先需要明确的是&#xff0c;范数是一个函数&#xff0c;在机器学习中我们通常用它来衡量向量的大小。 范数定…

IdentityServer4之持久化很顺手的事

前言原计划打算在春节期间多分享几篇技术文章的&#xff0c;但到最后一篇也没出&#xff0c;偷懒了吗&#xff1f;算是吧&#xff0c;过程是这样的&#xff1a;每次拿出电脑&#xff0c;在孩姥姥家的院子总有阳光沐浴&#xff0c;看不清屏幕&#xff0c;回屋又有点冷(在强行找理…

手写体识别代码_Python识别图片中的文字

一、前言不知道大家有没有遇到过这样的问题&#xff0c;就是在某个软件或者某个网页里面有一篇文章&#xff0c;你非常喜欢&#xff0c;但是不能复制。或者像百度文档一样&#xff0c;只能复制一部分&#xff0c;这个时候我们就会选择截图保存。但是当我们想用到里面的文字时&a…

递推与储存,是动态规划的关键

小智最近由于项目需要&#xff0c;经常要接触到一些规划类的问题。那今天就给大家讲一讲旅行商问题及其解法吧。旅行商问题&#xff0c;即TSP问题&#xff08;Travelling Salesman Problem&#xff09;。问题是&#xff0c;有一个旅行商人要拜访n个城市&#xff0c;每个城市只能…

dotnet core TargetFramework 解析顺序探索

dotnet core TargetFramework 解析顺序测试Intro现在 dotnet 的 TargetFramework 越来越多&#xff0c;抛开 .NET Framework 不谈&#xff0c;如果一个类库支持多个 TargetFramework 应用实际运行的时候会使用哪个版本的 API 呢&#xff0c;之前一直都是想当然的自以为是了&…

大数据时代,掌握数据分析需要做到这几点

这些年来&#xff0c;随着进入大数据时代&#xff0c;各行各业均有一个词频频被提到&#xff0c;那就是数据分析。那么数据分析究竟是什么呢&#xff1f;数据分析就是指用适当的统计分析方法对收集来的大量数据进行处理分析&#xff0c;提取有用信息并形成结论&#xff0c;从而…

93.7%的程序员!竟然都不知道Redis为什么默认16个数据库?

背景在实际项目中redis常被应用于做缓存&#xff0c;分布式锁/消息队列等。但是在搭建配置好redis服务器后很多朋友应该会发现和有这样的疑问&#xff0c;为什么redis默认建立了16个数据库&#xff0c;16个数据库的由来redis是一个字典结构的存储服务器&#xff0c;一个redis实…

“一边熬夜一边求不要猝死”,90后养生朋克指南,条条扎心!

随着一批又一批的90后步入中年秃头、失眠、衰老...健康的压力如影如随是时候开始养生朋克了当代青年&#xff1a;养生朋克指南养生朋克一边作死一边自救的养生方式比如一边熬夜一边涂贵价护肤品用最贵的眼霜 熬最长的夜心理活动经常是&#xff1a;一边熬夜一边祈祷自己不要猝死…

万级 K8S 集群背后,etcd 如何保持稳定性?

这几年&#xff0c;随着 Kubernetes 成为容器编排领域霸主&#xff0c;etcd 越来越火&#xff0c;GitHub star 已超过 34.2K。这与它的应用场景广泛密不可分&#xff0c;从服务发现到分布式锁&#xff0c;从配置存储到分布式协调&#xff0c;可以说&#xff0c;etcd 已成为云原…

qdialog 只有点击才能获得焦点_4 个突破点,让你的 Banner 点击率提升10倍

双 11 刚过&#xff0c;双 12又来了每年这个时候作为一名设计师&#xff0c;真的很难...老板压着做电商 banner还有很多人指指戳戳让你改稿好不容易按照别人的想法过稿结果banner 的点击率还不高其实&#xff0c;只需要稍微调整视觉重点你的电商banner 就会变得变得更吸引人&am…

穿背心的老院士,86岁,重病,还在敲代码,单手!

昨天有幸看到了一个视频&#xff0c;视频中&#xff0c;一位老先生穿着朴素的白背心&#xff0c;伏在桌上&#xff0c;对着电脑&#xff0c;一手按着写满密密麻麻数学公式的本子&#xff0c;另一只手仅用单指吃力又缓慢地按着键盘。老先生全神贯注地研究他是 “背心院士” 高伯…