Dapr 助力应用架构的可持续性
Posted [虚幻私塾】
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Dapr 助力应用架构的可持续性相关的知识,希望对你有一定的参考价值。
Python微信订餐小程序课程视频
https://edu.csdn.net/course/detail/36074
Python实战量化交易理财系统
https://edu.csdn.net/course/detail/35475
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9oDmbCrQ-1645847046105)(https://img2022.cnblogs.com/blog/510/202202/510-20220226082347920-1069021184.png “image”)]
在文章亚马逊可持续软件工程实践[1] 有这么一段我们为什么要关注“可持续发展”:
联合国于 2015 年制定了一个全球框架《巴黎协定》[2],随后各缔约国纷纷制定了“碳中和”路径和目标,对地球环境的健康发展做出承诺。今年两会,中国也将“碳中和”和“碳达峰”写入政府工作报告,目标在 2030 年前实现“碳达峰”,2060 年前实现“碳中和”。毋庸置疑,可持续发展已经成为不言而喻的社会共识。但在社会共识之下,还需要个人、企业、政府等各类社会主体的参与——其中体量庞大的科技企业是至关重要的参与者。
在探讨应用程序的碳足迹时,我们通常应该考虑两个角度。运行它使用了多少能量(即内核数量、执行时间、硬件效率等)以及产生多少能量对环境产生了影响,这称为碳强度,取决于位置、时间和数据中心使用的能源类型(天然气、煤炭、风能等)。2016 年的一项研究证明,大约 55% 的消耗能源取决于计算系统,其余 45% 用于支持计算(冷却、UPS 等)。
2020 年标志着 IT 界开始形成全球共识:软件和应用程序具有必须考虑的足迹,并且正在开发算法来评估这些足迹[3]。我最近一直在探索 Dapr 的创新,这是一个为分布式应用程序运行时创建的开源项目,目前是CNCF的孵化项目,项目官网:https://dapr.io/。根据其主页介绍,“Dapr 是一个可移植的、事件驱动的运行时,它使任何开发人员都可以轻松构建在云和边缘上运行的弹性、无状态和有状态的应用程序,并包含语言和开发人员框架的多样性。” Dapr 的主要梦幻般的特性确实是其实现的简单性以及跨任何编程语言、框架和基础架构工作的能力,它促使开发人员在接近他们的软件架构时更容易开始考虑IT可持续性。我们从三个方面来谈谈Dapr 在可持续性方面给开发人员带来的方便。
首先是Dapr 助力应用程序衡量自身的碳排放。在分布式解决方案中,我们通常可以有几种不同的基础设施环境和编程语言。理想情况下,这应该通过一个专用的微服务来完成,该微服务可以持续监控每种能源的碳排放,并且能够为应用程序的其他部分提供这些信息。Dapr 在衡量其基础设施的性能影响方面非常精确,并就如何衡量采用 Dapr 的微服务所使用的性能提供了指导。正如Dapr文档[6]中提到的,有一些方法可以检索 CPU 和内存使用情况,这有助于消除总体碳影响。此外,以递归方式,此微服务还应监控其自身的碳排放。
第二点是Dapr 助力应用程序通过控制其 IaC(基础设施即代码)来驱动/改变其碳排放,碳排放并不是一个固定的数量,并且很大程度上取决于基础设施所在数据中心的能源状况,因此应用程序可以具有自动化,触发全部或部分基础设施向影响较小的站点或区域移动。对于某些地区来说,这可能不是立竿见影的,甚至是不可行的(例如需要考虑数据主权和延迟),但在适用的情况下, 分布式应用程序的高度优化的可持续基础设施,可以保证在影响较小的环境中运行基础设施(出于同样的原因,可能是最便宜的),Dapr 非常容易就可以做到环境的适应性。从目前全球情况来看,亚马逊、谷歌、微软这三家世界级科技巨头引领了“绿色云”改造。亚马逊表示将提前十年达成《巴黎协定》,并在 2025 年实现 100% 可再生能源。并且还成立了 20 亿美元的气候承诺基金,投资那些能帮助亚马逊达成目标的公司。谷歌大胆承诺,到 2030 年实现数据中心全天候无碳运行,而不是依靠每年的直接能源匹配。微软(Microsoft)自 2012 年起实现碳中和,并承诺通过电力购买协议,到 2025 年实现数据中心完全使用可再生能源。而在中国,互联网科技企业也都在不断加大力量投入到碳中和改造中。今年 8 月国际环保组织绿色和平(Greenpeace)发布了中国科技巨头《绿色云端2021》[5]排行榜,从能源信息披露、节能减碳表现、可再生能源方面合梳理了中国互联网科技企业的气候行动,相比去年,中国互联网科技企业整体平均得分上升 11%。
第三点是Dapr 助力应用程序根据用户行为驱动/改变其碳排放影响,应用程序可以根据功能的碳排放提供不同级别的功能,并根据功能的影响级别提供多样化的用户体验,将知情权选择留给最终用户。有更多的空闲时间吗?你为什么不试试这个稍微高一点的延迟水平,通过使用更环保的基础设施来节省 xx% 的碳?你真的需要加载所有高分辨率图片吗?等等。这显然会在编程方面带来一些开销,但其执行的美妙之处在于应用程序将定义并教育最终用户对其影响发表意见,并且在一段时间后通过监控用户所做的选择,您还可以拥有关于他们希望如何与您的软件交互的反馈。随着时间的推移,您将清楚地了解哪种组合可以更好地平衡性能和节能。Dapr 偏爱异步架构模式,尤其是依赖于微服务之间的发布/订阅交互。通过向外扩展和向内扩展来处理请求,可以在对用户需求的动态响应和对我们希望为工作负载提供的资源的控制之间实现最佳折衷。
IT可持续性是我们在设计应用程序时必须开始考虑的工作了,优化工作负载以提高利用率,我们可以分散定时任务,降低负载峰值,达到调整 CPU 使用效率的目的。 重要的是,优化并不是只考虑某一个地方就可以,需要我们在架构和系统层面去思考,每位开发者都可以从现在开始去理解“可持续性”相关的术语和原则,在工作中寻找优化负载的机会。Dapr 在架构和系统层面为我们设计 可持续的应用程序提供了秘密武器。
[1]亚马逊可持续软件工程实践: https://www.infoq.cn/article/9dIy3T8NEmohhPU9QFe2
[2]巴黎协定:https://zh.wikipedia.org/wiki/%E5%B7%B4%E9%BB%8E%E5%8D%94%E5%AE%9A
[3]绿色软件工程原理: https://principles.green/
[4] Dapr : https://dapr.io/
[5]绿色云端 :https://www.eet-china.com/mp/a69760.html
[6]服务调用性能: https://docs.dapr.io/operations/performance-and-scalability/perf-service-invocation/
微软的分布式应用框架 Dapr
微服务架构已成为构建云原生应用程序的标准,微服务架构提供了令人信服的好处,包括可伸缩性,松散的服务耦合和独立部署,但是这种方法的成本很高,需要了解和熟练掌握分布式系统。为了使用所有开发人员能够使用任何语言和任何框架轻松地构建便携式微服务应用程序,无论是开发新项目还是迁移现有代码
Dapr 介绍
Github: https://github.com/dapr/dapr
Dapr是一种可移植的,事件驱动的,无服务器运行时,用于构建跨云和边缘的分布式应用程序。
Distributed Application Runtime. An event-driven, portable runtime for building microservices on cloud and edge.
其中提到了多语言和多开发者框架,我认为这是他选择的通过通信共享信息,即 HTTP
和 GRPC
支持多语言等特性。微软想通过这个设定一个构建微服务应用的规则。从根本上确立你开发的每一个应用的独立性。赋能每个开发者,为了使Dapr对于不同的语言更加方便,它还包括针对Go,Java,JavaScript,.NET和Python的语言特定的SDK。这些SDK通过类型化的语言API(而不是调用http / gRPC API)公开了Dapr构建块中的功能,例如保存状态,发布事件或创建actor。这使开发人员可以使用他们选择的语言编写无状态和有状态功能以及参与者的组合。并且由于这些SDK共享Dapr运行时,您甚至可以获得跨语言的actor和功能支持!
Dapr还可以与任何开发人员框架集成。例如,在Dapr .NET SDK中,您将找到ASP.NET Core集成,该集成带来了可响应其他服务的发布/订阅事件的状态路由控制器,从而使ASP.NET Core成为构建微服务Web应用程序的更好框架。
不过需要注意的是Dapr目前正处于Alpha阶段, 今天刚发布了0.2版本。在v1.0稳定版本发布之前,建议不要用于生产环境。
下面进行一个 QuickStart
环境
- Install Docker(微服务已经离不开容器化了)
- Install Dapr CLI
- Install .Net Core SDK 3.0
在Windows 上通过Powershell 安装:
powershell -Command "iwr -useb https://raw.githubusercontent.com/dapr/cli/master/install/install.ps1 | iex"
然后把 c:dapr 添加到环境变量 PATH
运行dapr命令,检查输出是否正常
C:workshopGithubdotnet-sdk>dapr --help
__
____/ /___ _____ _____
/ __ / __ ‘/ __ / ___/
/ /_/ / /_/ / /_/ / /
\\__,_/\\__,_/ .___/_/
/_/
======================================================
A serverless runtime for hyperscale, distributed systems
Usage:
dapr [command]
Available Commands:
help Help about any command
init Setup dapr in Kubernetes or Standalone modes
list List all dapr instances
publish publish an event to multiple consumers
run Launches dapr and your app side by side
send invoke a dapr app with an optional payload
stop Stops a running dapr instance and its associated app
uninstall removes a dapr installation
Flags:
-h, --help help for dapr
--version version for dapr
Use "dapr [command] --help" for more information about a command.
执行初始化(会启动 docker 容器)
dapr init
Making the jump to hyperspace...
Downloading binaries and setting up components
Success! Dapr is up and running
下载.NET SDk代码
https://github.com/dapr/dotnet-sdk ,里面有.NET Core的多个示例代码:
示例 | 描述 |
1. Actor | Demonstrates creating virtual actors that encapsulate code and state. Also see docs in this repo for a tutorial. |
2. ASP.NET Core | Demonstrates ASP.NET Core integration with Dapr by create Controllers and Routes. |
3. gRPC client | The gRPC client sample shows how to make Dapr calls to publish events, save state, get state and delete state using a gRPC client. |
我们一起来看下ASP.NET Core的Demo;
例子中主 我们使用 Dapr
的交互。Dapr通过 Runtime
- 提供 Dapr API 给多语言调用。
- 提供 状态管理 By state stores
/// <summary>
/// Sample showing Dapr integration with controller.
/// </summary>
[ApiController]
public class SampleController : ControllerBase
{
/// <summary>
/// Gets the account information as specified by the id.
/// </summary>
/// <param name="account">Account information for the id from Dapr state store.</param>
/// <returns>Account information.</returns>
[HttpGet("{account}")]
public ActionResult<Account> Get(StateEntry<Account> account)
{
if (account.Value is null)
{
return this.NotFound();
}
return account.Value;
}
/// <summary>
/// Method for depositing to account as psecified in transaction.
/// </summary>
/// <param name="transaction">Transaction info.</param>
/// <param name="stateClient">State client to interact with dapr runtime.</param>
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
[Topic("deposit")]
[HttpPost("deposit")]
public async Task<ActionResult<Account>> Deposit(Transaction transaction, [FromServices] StateClient stateClient)
{
var state = await stateClient.GetStateEntryAsync<Account>(transaction.Id);
state.Value ??= new Account() { Id = transaction.Id, };
state.Value.Balance += transaction.Amount;
await state.SaveAsync();
return state.Value;
}
/// <summary>
/// Method for withdrawing from account as specified in transaction.
/// </summary>
/// <param name="transaction">Transaction info.</param>
/// <param name="stateClient">State client to interact with dapr runtime.</param>
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
[Topic("withdraw")]
[HttpPost("withdraw")]
public async Task<ActionResult<Account>> Withdraw(Transaction transaction, [FromServices] StateClient stateClient)
{
var state = await stateClient.GetStateEntryAsync<Account>(transaction.Id);
if (state.Value == null)
{
return this.NotFound();
}
state.Value.Balance -= transaction.Amount;
await state.SaveAsync();
return state.Value;
}
}
这里重点是状态存储,即将state
通过 StateClient 存储在Dapr
中,我们通过状态转移在Dapr
里实现了stateless。
Dapr 运行.NET 应用程序
演示Dapr的服务调用,在终端中切换到项目目录,然后使用dapr启动应用
C:workshopGithubdotnet-sdksamplesAspNetCoreControllerSample>dapr run --app-id routing --app-port 5000 dotnet run
Starting Dapr with id routing. HTTP Port: 61102. gRPC Port: 61103
You‘re up and running! Both Dapr and your app logs will appear here.
注意: 以上dapr run命令,通过app-id指定了应用的ID,通过app-port指定了应用的端口(webapi默认使用5000作为http端口),后跟dotnet run命名启动当前项目。可参考Dapr文档服务调用
后台运行的 CLI
命令,这里是前台打印的日志, 注意到 .NET App
在指定的 5000
端口运行,同时还有状态存储的 redis
在 6379
端口运行
== DAPR == time="2019-11-16T18:33:22+08:00" level=info msg="starting Dapr Runtime -- version 0.2.0 -- commit c75b11
1-dirty"
== DAPR == time="2019-11-16T18:33:22+08:00" level=info msg="log level set to: info"
== DAPR == time="2019-11-16T18:33:22+08:00" level=info msg="standalone mode configured"
== DAPR == time="2019-11-16T18:33:22+08:00" level=info msg="dapr id: routing"
== DAPR == time="2019-11-16T18:33:22+08:00" level=info msg="loaded component messagebus (pubsub.redis)"
== DAPR == time="2019-11-16T18:33:22+08:00" level=info msg="loaded component statestore (state.redis)"
== DAPR == time="2019-11-16T18:33:22+08:00" level=info msg="application protocol: http. waiting on port 5000"
== APP == info: Microsoft.Hosting.Lifetime[0]
== APP == Now listening on: http://localhost:5000
== APP == info: Microsoft.Hosting.Lifetime[0]
== APP == Application started. Press Ctrl+C to shut down.
== APP == info: Microsoft.Hosting.Lifetime[0]
== APP == Hosting environment: Development
== APP == info: Microsoft.Hosting.Lifetime[0]
== APP == Content root path: C:workshopGithubdotnet-sdksamplesAspNetCoreControllerSample
== DAPR == time="2019-11-16T18:33:31+08:00" level=info msg="application discovered on port 5000"
== DAPR == 2019-11-16 18:33:32.029764 I | redis: connecting to localhost:6379
== DAPR == 2019-11-16 18:33:32.036316 I | redis: connected to localhost:6379 (localAddr: [::1]:61164, remAddr:
[::1]:6379)
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="actor runtime started. actor idle timeout: 1h0m0s.
actor scan interval: 30s"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="actors: starting connection attempt to placement se
rvice at localhost:6050"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="http server is running on port 61102"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="gRPC server is running on port 61103"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="local service entry announced"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="dapr initialized. Status: Running. Init Elapsed 917
6.5164ms"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="actors: established connection to placement service
at localhost:6050"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="actors: placement order received: lock"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="actors: placement order received: update"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="actors: placement tables updated"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="actors: placement order received: unlock"
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[100]
== APP == Start processing HTTP request GET http://localhost:61102/v1.0/state/17
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[100]
== APP == Sending HTTP request GET http://localhost:61102/v1.0/state/17
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[101]
== APP == Received HTTP response after 2228.2998000000002ms - OK
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[101]
== APP == End processing HTTP request after 2257.3405000000002ms - OK
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[100]
== APP == Start processing HTTP request POST http://localhost:61102/v1.0/state
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[100]
== APP == Sending HTTP request POST http://localhost:61102/v1.0/state
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[101]
== APP == Received HTTP response after 67.46000000000001ms - Created
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[101]
== APP == End processing HTTP request after 68.0343ms - Created
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[100]
== APP == Start processing HTTP request GET http://localhost:61102/v1.0/state/17
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[100]
== APP == Sending HTTP request GET http://localhost:61102/v1.0/state/17
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[101]
== APP == Received HTTP response after 5.8247ms - OK
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[101]
== APP == End processing HTTP request after 6.268400000000001ms - OK
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[100]
== APP == Start processing HTTP request POST http://localhost:61102/v1.0/state
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[100]
== APP == Sending HTTP request POST http://localhost:61102/v1.0/state
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[101]
== APP == Received HTTP response after 4.5181000000000004ms - Created
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[101]
== APP == End processing HTTP request after 4.6208ms - Created
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[100]
== APP == Start processing HTTP request GET http://localhost:61102/v1.0/state/17
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[100]
== APP == Sending HTTP request GET http://localhost:61102/v1.0/state/17
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[101]
== APP == Received HTTP response after 20.2967ms - OK
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[101]
== APP == End processing HTTP request after 20.691100000000002ms – OK
为了同时实现可移植性和与现有代码的轻松集成,Dapr通过http或gRPC提供了标准API。Dapr端口可从Dapr启动日志中获取,如以下日志表示Dapr公开的HTTP端口为61102(通过Dapr也可使用gRPC方式进行服务调用)
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="http server is running on port 61102"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="gRPC server is running on port 61103"
我们可通过以下地址来调用示例方法,根据Dapr服务调用API规范,其代理调用规则为:
POST/GET/PUT/DELETE http://localhost:<Dapr端口>/v1.0/invoke/<id>/method/<method-name>
:
直接调用:GET http://localhost:5000/17
通过Dapr服务调用: GET http://localhost:61102/v1.0/invoke/routing/method/17
注意: Dapr的服务调用是有dapr sidecar来实现的,在被调用的服务中无需注入任何与dapr相关的代码。
以上是关于Dapr 助力应用架构的可持续性的主要内容,如果未能解决你的问题,请参考以下文章
Dapr+Net6+K8S最小原型项目架构和实现-12-将Dapr项目部署到K8S集群