引言
Dapr 是微软主导的云原生开源我的项目,2019 年 10 月首次公布,到往年 2 月正式公布 V1.0 版本。在不到一年半的工夫内,github star 数达到了 1.2 万,超过同期的 kubernetes、istio、knative 等,发展势头迅猛,业界关注度十分高。
Dapr 这个词是是「Distributed Application runtime」的首字母缩写,十分精炼的解释了 dapr 是什么:dapr 是一个为利用提供分布式能力的运行时。
什么是 Runtime
Runtime 是一个形象的概念,字面意思是程序运行的时候。个别是指用来 反对程序运行的实现。形容的是程序失常执行须要的反对:库、命令和环境等。
常见的 runtime 为程序提供的反对:
- 语言 runtime(C/Goang…):操作系统交互,垃圾回收,并发管制等
- Java runtime: 虚拟机
- 容器运行时:namespace,cgroup 等
容器运行时,就是容器运行起来须要的一系列程序和环境。比方如何应用 namespace 实现资源隔离,如何应用 cgroup 实现资源限度,这些都是容器运行时须要提供的实现。
特色:
- runtime 是在程序之外,不禁程序编写者提供
- runtime 的生命周期通常和程序生命周期关联
咱们写 java 程序的不须要写 java 虚拟机;构建一个容器,通常不须要去写 runc 的代码。
什么是 Distributed Application Runtime
Dapr 所提供的「分布式应用运行时」,是利用程序运行所需分布式能力的实现,这些能力涵盖服务通信、数据长久化、内部 binding,pub-sub 等等。比方服务调用须要有容错重试机制,比方一个数据长久化操作心愿应用乐观锁,比方公布音讯是要求有投递保障。
长期以来,这些性能的适配都是集成在业务代码里的。dapr 翻新之处是将这些性能,从原来 application runtime 中拆分进去,作为一个独立的 runtime。dapr runtime 也满足下面说到的 runtime 的特色。
理解 service mesh 的同学可能会看出,这和 service mesh 应用的 sidecar 模式很相似,这是一种让零碎解耦、让开发人员关注点拆散的形式。但咱们也很好奇,dapr 和 service mesh 有什么关联,这些越来越多的 sidecar 模型到底有什么区别?(knative 也用到了 sidecar 模式)。因而,在深刻 dapr 之前,咱们先理解一个重要的实践背景:Multi runtime。
Multi Runtime
Multi runtime 是由 Red Hat 首席架构师 Bilgin Ibryam 提出的,实际上 multi runtime 和 dapr 并没有间接的关系,multi runtime 的提出是在 dapr 开源之后。作者的文章重点对当今分布式应用的需要做了归类,并且剖析了以后风行的云原生我的项目是如何满足这些分布式需要,包含 kubernetes,istio,dapr 等,最初,作者对分布式应用和中间件的将来倒退,做了推导和预测,这就是 multi runtime。
分布式应用的需要:
- 生命周期:包含部署,健康检查,程度扩大,配置管理等,目前这些需要的最佳实际,都陆续在 kubernetes 上有了落地。
- 网络:网络方面的需要 是 service Mesh 的主战场,比方 istio 能够满足这里绝大部分需要,除了 pub/sub。
- 状态:包含数据的读写,状态其实是十分难以治理的,波及幂等,缓存,数据流等等。
- 绑定:次要是指和零碎内部资源的交互。
右边的这些需要,在传统软件时代,是耦合在利用代码里的,但现如今,有越来越多的分布式能力从利用中剥离,而剥离的形式也在逐步变动,从最晚期,这些能力从业务代码剥离到依赖库中,而后有一些个性剥离到平台层(kubernetes)。而现在会有更多的非业务能力,剥离到 sidecar 中。
作者预测:实践上每个微服务能够有多个 runtime: 一个业务运行时,和多个分布式能力运行时,但最现实的状况是,或者最可能呈现的状况是:在业务之外的运行时合并为一个,通过高度模块化、标准化和可配置的形式,给业务提供所有分布式能力。
原文:Multi-runtime Microservices Architecture
Dapr
Dapr 是什么
dapr is a portable,event-driven runtime that makes it easy for any developer to build resilient,stateless and stateful applications that run on the cloud and edge and embraces the diversity of languages and developer frameworks.
关键字:可移植,事件驱动,弹性,有状态和无状态,云和边端,语言无关,框架无关。
这些次要是 dapr 的愿景,外围是要提供一个有规范,可配置,蕴含各种分布式能力的运行时。
Dapr 架构
dapr 的设计是典型的分层架构,其核心理念,是利用形象层来实现利用关注点的拆散,用以升高分布式应用的复杂性。
在 dapr 的架构中,外围的三个组成部分:API,Building Blocks 和 Components。
Dapr Building Blocks
这是 dapr 对外提供能力的根本单元,是对分布式能力的形象和归类,包含以下几大类
- service-to-service invocation
- State management
- Publish and subscribe
- Resource bindings
- Actors
- Observability
- Secrets
这些都是和利用开发非亲非故的。每一种 building block 都是齐全独立的,利用能够按需调用。
咱们能够比照下 dapr building blocks 和之前 multi runtime 提出的 4 大类 分布式能力需要。其中 lifecycle 不属于 runtime 领域,lifecycle 能力通常是由平台提供,目前云原生畛域基本上是被 kubernetes 垄断,除此之外的 networking,state 和 binding 都包含在 dapr 的 building blocks 中。
Dapr Components
Components 提供和各种分布式实现的对接,包含自建的,云上的,边缘等等。
实践上 building block 能够组合应用任意的 components,一个 component 也能够被不同的 building block 应用。比方 actor 和 state 都会应用 state component; 另一个例子,service invocation 会应用 name resolution 和 middleware component,而且不同的场景下,能够抉择不同的 component 实现。
Component 类型和实现:在实现层面,每一种 component 类型 定义了一系列接口(interface definition),每一种 component 类型 有多种 component 实现,他们都实现了 component 类型要求的接口(interface)。
Dapr API
利用如何能应用到这些分布式能力,这是 dapr 最外围的设计,也是 dapr 利用和非 dapr 利用最要害的区别: dapr 利用规范 API 裸露各种分布式能力。API 定义了利用所需的分布式能力。dapr 提供两种 API: HTTP1.1/REST 和 HTTP2/gRPC,两者在性能上是等价的。这些 API 是平台无关的,或者说是实现无关的,这是 dapr 是否风行的一个要害。
利用只须要依照 API 标准发动,不论是服务拜访,还是存储,还是公布音讯到队列里,都是 HTTP 接口。不论是操作 redis 还是 mysql 都是一样的 API。在利用看来,所有所需的能力,都能够用 HTTP 协定来示意,这些能力的获取是标准化的,只有利用须要的分布式能力不变,那利用的代码就不须要扭转。
将「分布式原语」映射到 Http API 上,极大地缩小了程序员心智的开销。在利用代码中不再须要引入相干的组件调用库,不须要去封装组件的具体调用形式,不须要对不同的实现做辨别。
另外在用户利用侧,dapr 还提供了多种语言的 SDK,这些 SDK 的目标是用更便捷的形式来裸露 building Blocks 的 API,用更加语义化的办法调用,来封装 Http/gRPC 的调用。
总结:
- API: 通过标准化的形式裸露 building block
- Building Block: 是能力的形象
- Components: 对接能力的实现
API 调用是如何实现
一个存储调用的例子:比方一个电商零碎,须要长久化存储,传统的做法是,咱们要先决策应用什么存储,mysql 或者 redis 等,咱们须要在代码里引入相应的 SDK,编写各异的实现,将来如果利用想要切换存储类型,或者从本地存储迁徙到云上,改变十分大。
假如这个零碎的特色是读多写少,那咱们偏向于用乐观锁来更新数据。业务提出来的「用乐观锁管制并发写入」这就是一个典型的分布式需要,而这种需要的实现在不同的存储系统中不尽相同,比方 mysql 是须要用户显式指定一个字段作为版本信息,用户写操作是须要把版本信息传回服务器,而 redis 乐观锁须要用户指定在 redis server 端 watch 某个 key。相似的需要还有数据库一致性,是应用最终一致性还是强一致性,各种存储实现也不同。
如上图所示,如果接入应用 dapr runtime,利用发动存储调用非常简单,不须要在利用代码里引入 redis 或者 mysql 的 SDK,也不必关怀理论存储应用是什么通信协议,利用代码里只须要应用分布式原语和 dapr runtime 通信,通信的协定是简略的 Http 或者 gRPC,dapr runtime 去实现这些分布式能力。
Service Invocation
次要能力:
- 服务发现
- 通信安全
- 失败重试
- 可观测性
在 kubernetes 中应用 dapr,dapr 会为每个服务生成一个新的 service(以 -dapr
结尾),sidecar 之间的通信都是 gRPC,每个利用须要指定一个 app-id 用于服务发现,利用须要显示的发动对 runtime API 的调用,没有相似 mesh 的 iptables 通明拦挡。
大家能够脑洞一下,如果 dapr 这种模式能大规模风行,那市面上大部分 RPC 是不是都不再须要了,现在大部分 RPC 尽管各有专场,然而大部分性能都是相似的,服务发现,编解码,网络传输,有的 RPC 框架还带服务治理的能力。大部分能力目前都能够由 mesh 或者 dapr 这类 runtime 来提供,这也是一个显著的趋势。
State management
次要能力:
- CRUD,包含批量操作
- 事务
- 并发:first-write-wins、last-write-wins
- 一致性:最终统一、强一致性
- 可插拔(Pluggable state stores)
State 提供统一的 键值对 存储形象,这里不包含关系型或者其余类型的存储。总的来说,在云原生畛域(以 kubernetes 和 etcd 为代表),键值对存储的适用范围更广。另外相比其余存储类型,键值对存储引擎的接口形象更容易实现,即便是关系型数据库,也能轻松的实现对键值对 API 的反对。
但依然不是所有的存储引擎都能提供等价的键值对存储能力(见 dapr 存储实现差别)。为了保障应用程序的可移植性,这里确实是须要一些适配工作。比方像 Memcached,Cassandra 这些是不反对事务的,而很多数据库也不能提供基于 ETag 的乐观锁能力。
对于并发管制,在 API 层,dapr 利用 HTTP ETags 来实现并发管制,相似 kubernetes 对象的 resource version,具体地:dapr 在返回数据时,会带上 Etag 属性。如果用户须要应用乐观锁做更新操作,申请中须要带回 Etag,只有当 Etag 和服务器上数据的雷同时,更新操作才会胜利。如果更新操作没有带上 Etag,那并发模式将是 last-write-wins
。
Publish and subscribe
应用公布和订阅模式,微服务间能够充沛的解耦。
次要能力:
- 对立的音讯格局:Cloud Events
- At-Least-Once guarantees(音讯绝不会丢,但可能会反复传递)
- 反对音讯过期工夫(per message TTL)
- 反对 topic 可见性配置
Runtime 不仅能够做能力的对接适配,还能够做加强,这是一个例子:如果音讯组件原生反对音讯有效期,那 runtime 间接转发 TTL 相干操作,过期的行为由组件间接管制,而对于那些不反对音讯有效期的组件,dapr 会在 runtime 中补齐相干的过期性能。(CloudEvent 里有 expiration)
两种订阅形式
二者提供的性能是统一的。内部申明形式须要多保护一个 CRD 对象,适宜订阅者或订阅主题常常发生变化的场景,这样在调整时不须要改利用代码。利用编码方式刚好相同,订阅配置写死在代码里,适宜订阅主题不须要动静调整的场景。
Bindings
Bindings 其实和之前的 pub/sub 十分相似,也是利用异步通信传递音讯。它俩次要的区别是:pub/sub 次要面向的是 dapr 外部利用,而 bindings 次要解决的和内部依赖零碎的输入输出。
实际上它俩上层的 components 有很多是重叠的,比如说 kafka,redis 既能够作为外部消息传递,也能够作为内部消息传递。pub/sub 根本能够等同于音讯队列,但 bindings 次要是处理事件(trigger handler),比方 twitter 关键字事件,比方 github webhooks 等。
Actor
- 最根本的计算单元,封装了能够执行的行为和公有状态
- 通过信箱异步通信
- 外部单线程
- 虚构的:不须要显示创立,主动 GC
Actor 是一种并发编程的模型,Actor 示意的是一个最根本的计算单元,封装了能够执行的行为和公有状态。actor 之间互相隔离,它们并不相互共享内存,也就是说,一个 actor 能维持一个公有的状态,并且这个状态不可能被另一个 actor 所扭转。在 actor 模型里每个 actor 都有地址(信箱),所以它们才可能互相发送音讯。每个 actor 只能程序地解决音讯。单个 actor 不思考并发。
Dapr 中 actor 是虚构的,它们并不一定要常驻内存。它们不须要显式创立或销毁。dapr actor runtime 在第一次接管到该 actor ID 的申请时主动激活 actor。如果该 actor 在一段时间内未被应用,那么 runtime 将回收内存对象。如果当前须要重新启动,它还将还原 actor 的所有原有数据。
Actor placement service 为零碎提供了 actor 散发和治理,placement 会跟踪 actor 类型和所有实例的分区,并将这些分区信息同步到每个 dapr 实例中,并跟踪他们的创立和销毁。
Middleware Pipelines
留神 middleware pipelines 是一个 component 类型,而不是 building block。
Dapr 官网提供流量管控的能力比拟弱,和 istio 相比的话,目前 dapr 只有重试,加密等多数的管控能力,但 dapr 提供一个扩大的形式:这就是 middleware pipelines,用户能够按需编写不同的实现,并把他们级联起来应用。
其实这种形式在各种编程语言 web 框架中十分常见,只是叫法不同,有的叫装璜者模型,有的叫洋葱模型,其实模式都是一样:申请在路由到用户代码之前,会先按序执行 middleware pipelines,申请通过利用解决后,再按相同程序执行上述 middleware pipeline。通常在前序中对 request 做相应的加强解决,在后续中对 response 做加强解决。
咋一看这可能是一个不太起眼的性能,但和传统 web 框架的 middleware 不一样,dapr runtime 自身是在利用过程之外,所以不存在语言限度的问题。这使得 middleware 提供的性能能够跨语言共享。比方 dapr 原生没有提供限流和自定义鉴权的性能(呼声很高的 2 个场景),咱们能够遵循 middleware 的接口按需实现,而后植入 dapr 运行时中。
部署模式
Dapr 应用 sidecar 模式来裸露 building blocks 的能力,这里的 sidecar 除了包含 sidecar container 外,还能够是 sidecar process。
在非容器化环境中,用户利用和 dapr runtime 都是独立的过程;而在 kubernetes 这种容器化环境中,dapr runtime 作为 sidecar container 注入到 业务 pod 中,这和 service mesh sidecar 模式是统一的。
管制面
整个管制面还是一个微服务。和 istio 晚期有点相似。
Sidecar injector:利用 kubernetes mutating webhook 给业务 pod 注入 dapr runtime sidecar 容器,以及运行所需的环境变量,启动参数等。包含连贯管制面 operator 的地址(control-plane-address
)等。
Operator:会 list watch 用户定义的 Component 资源,并下发给数据面的 dapr runtime。数据面 runtime 会持有一个 OperatorClient 去 连贯管制面 Operator。
Sentry: 为 dapr 零碎中的工作负载提供基于 mtls 的平安通信。mtls 能强制通信单方进行身份认证,同时在认证之后保障通信都走加密通道。Sentry 的性能很相似 istio 里的 Citadel(目前曾经合并到 istiod)。在整个过程中,sentry 充当证书颁发机构(CA),解决 dapr sidecar 发动的签订证书申请,另外还要负责证书的轮转。除了 dapr sidecar 之间的主动 mTLS 之外,sidecar 和 dapr 管制面服务之间也是强制性的 mTLS。
Placement:用于跟踪 actor 的类型和实例散布,并同步给数据面的 runtime。
性能
sidecar 模式会带来额定的性能开销。以咱们应用 service mesh 的教训来看,这种模式的性能开销次要是 2 个方面,一个是流量通过 sidecar 的拦挡、流量管控和转发损耗,另一个是 sidecar 须要从管制面同步治理数据,sidecar 须要存储和解决这些数据,这可能会给数据面内存和 CPU 带来压力,特地是大规模场景下。
在官网对 dapr V1.0 的性能测试数据看: 在不开启 mtls 和 遥测的状况下,提早 P90 大略减少 1.4 ms,在开启 mtls 和 0.1 tracing rate 状况下,P90 数据大略还会减少了 3ms 左右。
这个数据要比 istio 好,dapr sidecar 没有太多的流量管控和批改的性能,也没有应用 iptables 拦挡,开销绝对较小。为了尽可能进步通信效率,dapr sidecar 之间的通信固定应用 gRPC 协定。而且 dapr 从数据面同步的数据量也非常少,所以也不会有相似 istio 场景下频繁 reload xDS 的问题。
但相比 service mesh,dapr sidecar 管控了更多的流量类型,比方状态存储,利用系统对这类流量的提早变动更加敏感,用户在接入 dapr 之前须要谨慎评估。目前 dapr 还在我的项目初期,业界还没有太多大规模,精细化的落地测评。
和 Service Mesh 比拟
二者都应用了 sidecar 模式,性能上也有重叠,实践上二者是能够共存的,尽管同时应用这 2 种技术可能不是一个最优的计划(开销和保护老本)。
Service mesh 定位偏差于服务级别的网络基础设施层。Service mesh 做了很多致力来让 mesh 层对应用层通明,冀望服务能平滑的迁徙。现实的状况下,利用开发者应该不感知 mesh 层的存在,所以 mesh 面向的次要是零碎运维人员。
Dapr 旨在提供利用所须要的分布式能力,这些能力是和业务的失常运作非亲非故的,dapr 提供的能力不是通明的,是须要利用显示的调用,所以 dapr 次要面向的开发人员。
服务调用方面,mesh 应用通明转发,对应用程序更敌对,然而反对的协定无限,mesh 对七层的协定扩大始终是一个难点。而在 dapr 里必须显示发动调用,所有调用都是会转为 gRPC,不须要思考协定扩大。
一些重叠的性能点:
- 服务通信 mTLS
- 遥测
- 重试
Istio 和 Dapr 进一步比拟
istio 有弱小的流量管控能力,这些是 dapr 不具备的。在 istio 数据面中,每个 envoy 都同步获取了整个网格内服务信息(通过 xDS)的全貌,包含服务所有的 endpoint IP,以及这些 endpoint 的特色,这让 istio 能够实现很多简单的负载平衡场景。
而 dapr sidecar 没有实现相似的能力,在 kubernetes 平台下,dapr 利用间的服务互访,还是依赖 kubernetes service 提供的随机负载平衡。这是 dapr 的短板,dapr runtime 不感知其余 endpoint 的信息,因而 dapr 甚至不能提供 round robin 的负载平衡策略。
Dapr 的外围性能是为利用提供了标准化的分布式能力,诸如状态治理,订阅公布,Actor 等等,这些畛域 istio 根本不波及。
另外在遥测畛域,二者也有区别,istio 的遥测次要是集中在服务间调用,而 dapr 除了能察看服务间调用,还把观测范畴扩大到了 pub/sub 畛域,这得益于 dapr 应用 cloud events 格局来传递 pub-sub 音讯,这样 dapr 能够将遥测信息写入 cloud events 进行传递。
另外目前 dapr 在 kubernetes 的管制面是微服务,而 Isito 管制面曾经是一个单体,将来 dapr 管制面有可能也会合并成一个单体。
总结
尽管后面咱们剖析了 dapr 这种 multi runtime 呈现的背景和趋势,但仍不得不说 dapr 的设计十分的新鲜。dapr 的翻新之处在于提供标准化的分布式能力 API,这一点既是开发人员十分欢送的模式,但也是业务接入最大的挑战,因为这波及到我的项目的革新甚至重写。另外,dapr 还提供了良好的实现扩大层,目前官网曾经实现了大量支流中间件的的接入,另外 Azure 自家的不少云产品都曾经实现了 dapr compatible。
我想应该有不少程序员都做过这样的「美梦」: 我不想面对各种依赖组件简单的差别,我只想面向接口编程、面向形象编程。现在 dapr 把这种理想化的架构模式初步实现了!这也是为什么 dapr 目前尽管还不是很成熟,但曾经吸引了大量开发者的关注。接下来随着社区的踊跃投入,dapr 生态将会更加壮大。
【腾讯云原生】云说新品、云研新术、云游新活、云赏资讯,扫码关注同名公众号,及时获取更多干货!!