谈谈对Dapr的一些感想

早在今年4月份的时候,就在Github的Trending榜上看到了Dapr,当时就对它留下了很深刻的印象。
后来由于工作原因,也只是偶尔关注一下Dapr的开发进展,没能持续跟进。最近正好有那么点时间,于是就打算记录一下自己对Dapr的一些心得体会。

如有不足之处,欢迎指出。

Dapr的定位

根据官方文档的介绍,Dapr是一个可移植的、事件驱动的运行时,能够帮助开发人员轻松构建弹性、无状态和有状态的应用程序。

它包含了以下的构建块:

  • 服务间调用
  • 状态管理
  • 发布和订阅
  • 资源绑定
  • Actor
  • 可观察性
  • 秘钥管理

Dapr提供了多语言的客户端以使用其能力,并以sidecar的形式工作,如下图所示,在dapr-go-example中有两个容器,其中名为daprd的容器就是dapr注入的sidecar:

sidecar.png
在我看来,Dapr的定位就是一个云原生时代的分布式服务构建框架,熟悉Azure的人可能会觉得它其实更像是Service Fabric的加强版,也可以与Java生态的Spring CloudMicroProfile,Go生态的kitgo-micro等等做类比。

Spring Cloud为例,我们将它提供的组件与Dapr的构建块作一些横向对比:

组件 Spring Cloud Dapr
服务间调用 Spring Cloud OpenFeign Service invocation
发布和订阅 Spring Cloud Stream Pub/Sub
事件驱动 Spring Cloud Function Bindings
秘钥管理 Spring Cloud Vault Secrets Management
可观察性 Spring Cloud Sleuth Observability

总的来说无论是Dapr还是上述这些项目,都是想帮助开发人员简单快速地构建分布式应用。但是由于时代背景的原因,它们的出发点、实现形式和Dapr又存在一些差异,下文会作一些详细的讨论。

Dapr主要帮助解决什么问题

异构服务通信

传统分布式中间件往往锁定某个语言,比如Java体系通常会使用Feign或者Dubbo实现,但它们并没有提供其他语言的库。

因此如果是多语言的环境,那么就需要基于某种通用协议如REST或者GRPC进行通信,可能还会需要额外的注册中心和负载均衡器,如下图所示是一个简单的实现思路:

简单的异构服务调用.png

当然,现实中的情况往往要比这复杂的多,并且考虑到会引入额外的中间件,带来的运维方面的成本也需要慎重考虑。

Dapr提供了多语言的SDK,如Java、Go、Python、PHP等,可以使用HTTP或者GRPC的方式进行异构服务间的调用,能很好地解决这个问题。

基础服务的抽象接口

Dapr提供了一些基础服务的抽象接口,以消息中间件为例,Dapr支持以下中间件的Pub/Sub:

Name Status Component version Since
Apache Kafka Beta v1 1.0
Hazelcast Alpha v1 1.0
MQTT Alpha v1 1.0
NATS Streaming Beta v1 1.0
In Memory Alpha v1 1.4
JetStream Alpha v1 1.4
Pulsar Alpha v1 1.0
RabbitMQ Alpha v1 1.0
Redis Streams GA v1 1.0

用Dapr抽象接口来使用基础服务能力的好处是————当你需要更换中间件的时候,可以少动点代码,换句话也可以说是增加了服务的可移植性,在熬小剑的文章里也有相关描述。

关于是否应该使用Dapr的权衡

上面说了使用Dapr的好处,但是软件架构始终是关于权衡的,Dapr也并非是万能良药。

运行环境

首先是Dapr的运行环境,尽管它支持在本地运行(Docker和非Docker的slim-init ),但对于生产环境这肯定不是一个好的做法,其中一个很重要的原因是Dapr控制平面是高可用问题,也会失去Dapr与AKS、EKS、GKE的良好集成能力。因此如果你的团队还没有迁移到Kubernetes上,或者并没有在Dapr官方已经提供了集成的托管集群上,建议将这个因素纳入考量范围。

异构服务通信

提供异构服务的通信能力是Dapr的主要能力之一,但如果你非常确定在可预见的很长一段时间内都不会出现多语言环境,那就需要好好衡量下了。

传统的中间件固然有其局限性,但是它们也确实提供了非常完善的功能,这是新生的Dapr所不具备的。

抽象接口

Dapr提供的抽象接口固然能为服务提供良好的可移植性,但前提是你没有大量使用特定于某个中间件产品的能力。比如当你使用Hazelcast或者Ignite做了大量的近距缓存时,Dapr可能并不能帮你增加应用的可移植性,因为它只提供一些所有同类中间件都具备的基本功能接口。

迁移成本

对于过去已经在项目中大量采用传统分布式框架的情况,迁移成本可能会是一个不大但也不小的问题,因为至少你需要替换掉服务间调用的代码。

Dapr提供的SDK

目前来看,Dapr的SDK已经能够覆盖到几个主流语言了,但仍然有不在支持列表内的或者正在开发中的,比如C++、Rust、Javascript目前就还没有那么完善:

Language Status Client SDK Server extensions Actor SDK
.NET Stable ASP.NET Core
Python Stable gRPC FastAPI Flask
Java Stable Spring Boot
Go Stable
PHP Stable
C++ In development
Rust In development
Javascript In development

\