Microsoft DevOps 文档里的文章(https://docs.microsoft.com/zh-cn/azure/devops/report/dashboards/cycle-time-and-lead-time?view=azure-devops)中的这张图片在给我们介绍了 什么是周期时间 以及它如何影响我的项目流时非常有影响力。
第一次输入 "正在进行" 或 "已解决" 状态类别到输入 "已完成" 状态类别,计算周期时间。 当开发人员编写代码时,能够快速验证更改并进行修订对于保持较短的周期时间至关重要。
丰田生产方式之父大野耐一曾经说过:我们唯一要做的就是降低从接到订单到交付产品给客户的周期时间。周期时间的降低可以有效保证软件的按时交付 。所以周期时间是软件交付的核心目标。
特别是微服务的设计和开发,通常需要达成下列4个目标:
- 构建的API 驱动设计的微服务
- 一切都可以在本地构建、测试和运行,而无需复杂的设置。
- 云端和本地依赖关系的等效性
- 设备环境无关,可以自由在Windows,Linux,Mac 之间切换。
我们借助于Dapr 可以非常容易的达成以上4个目标, 使用 Docker Compose 和 Dapr 技巧进行本地开发,测试和生产环境运行于Kubernetes, Kubernetes现在是各大云厂的标配服务。借助于Dapr 的语言无关性,平台无关性,我们可以在环境上尽量的缩短了时间,保持较短的周期时间交付软件。
我们可以在大脑里面来回顾一下我们的开发过程,对于每个任务/代码更改:
- 开发人员会将更改部署到生产环境
- 如果发现任何错误,请重新部署旧 镜像
- 在本地修复所有更改
- 推动其分支以生成可部署的内部版本,然后返回到 (1)
只有当开发人员脱离这个循环时,他们才能将他们的代码签入主程序。这个过程太疯狂了!仅第 4 步在镜像创建和部署之间就花费了大约 20 分钟。两三个遗漏的错误可能会使开发人员在一天中花掉大约1个小时,并且考虑到除了日常工作之外,我们都在从事这项工作,这扼杀了生产力。还有可能要考虑到部署对依赖项的更改所需的周期,此处的部署花费了更长的时间。
下面我们从 提高软件开发生产力角度来聊聊,Dapr所提供的主要生产力提高优势是:
- 减少技术债务 - 通过生产和使用软件来实现,该软件封装了具有高变化率的领域,具有出色的关注点分离性,并具有广泛的解耦。
- 减少所需的编码工作量 — 通过使用"低代码"方法实现,方法是提供许多通常需要的预构建软件部件,尤其是实现较低业务价值的商品管道功能的部件,例如将服务连接在一起的代码。
- 开发人员更加专注于生成高价值的业务逻辑 — 通过减少花费大量时间生成商品管道代码和/或使用可以使用自动化代码的手动流程和工具来实现。
- 对共享状态的并发访问的有效协调 — 分布式系统中这个众所周知的困难区域在许多情况下可以使用 Actor 模型来生成此结果。
将分布式系统的服务彼此分离,可以使软件开发、扩展和维护软件更具时间和成本效益,也更容易。为什么?将软件片段彼此分离可以使其内部代码内容和代码结构彼此独立地变化,从而大大减少了需求更改时代码更改所需的工作量。这种脱钩是减少技术债务的最佳方法之一,并随着岁月的流逝保持低水平,从而从长远来看提高生产率。
Dapr 产生解耦的一个关键方式是通过其构建块,每个构建块都定义了分布式系统常用功能的概念接口。对于大多数构建块,Dapr 还提供了许多预构建的插件组件,每个组件都为构建块概念的特定实例实现构建块接口的全部或部分。使用预构建的插件组件还可以减少所需的编码工作量。如果没有 Dapr,开发人员不知道有多少次必须一遍又一遍地编写大致相同的低级管道代码,才能连接到我们的代码使用的每个数据库或云服务并与之交互?使用Dapr的构建块/组件方法,答案为零!使用预构建的插件组件可节省大量时间,使开发人员能够专注于更高价值的工作。
如果我告诉你,Dapr是一个用于分布式系统的瑞士军刀实用程序服务,你很可能会想发现Dapr的许多功能,因为它既是中介又是解耦者。Dapr 提供的主要功能如下,其中许多功能通过构建块和组件实现,但不是全部:
- 状态存储 — Dapr 为键/值对状态存储构建块定义一个概念接口,然后提供了许多预构建的插件状态存储提供程序组件,每个组件都连接到特定的外部键值对状态存储,如 Redis 存储或其他流行的键/值对数据库。Dapr State Store 的目的是提供低延迟存储,如缓存。有关对通用存储或数据库管理系统的支持,请参阅下面的资源绑定和触发器。
- Pub/Sub — 与状态存储类似,Dapr 为发布/订阅构建基块定义了一个概念接口,此外还提供了许多预构建的插件发布/订阅组件,每个组件都连接到外部发布/订阅消息传送服务,例如 Azure 服务总线主题或 Redis 流。
- 安全机密访问 — 与上述想法相同,但适用于各种外部机密存储,如密钥保管库。
- 资源绑定和触发器 — 与上述想法相同,但应用于各种外部资源(许多是云资源),如队列、事件中心、服务网格、Blob 存储、某些数据库等。
- 服务到服务调用 — 此构建基块允许"Daprized"服务通过 RPC 使用服务名称加方法名称寻址(而不是通过 HTTP 或 gRPC 地址)相互通信。这会将服务到服务的通信与特定网络终结点分离。使用集群计算主机时需要这样做,并且使用其他主机也可以节省时间。
- Actor — 此构建块允许每个"Daprized"服务使用 Actor 模型来利用其独特的特征:1) 保持状态和在同一实体内操作状态的代码,以及 2) "基于回合的并发",以防止当多个客户端同时使用同一个 Actor 时状态不同步。
- 可观察性 — 可观察性构建基块概念界面提供分布式系统中服务到服务交互流(包括各种组件的详细信息)的跟踪、指标和运行状况监视,以及将数据发送到外部聚合器,例如 Azure Monitor、Application Insights 和 Zipkin。
- 有效安全性 — 在"Daprized"服务的广度和深度以及整个协作"Daprized"服务系统中提供高级别的通常可配置的安全性。
- 中间件管道 — 允许以声明方式将自定义"中间件管道组件"代码"插入"到 Dapr 请求/响应处理管道。这允许 Dapr 编排开发人员定义的服务与 Dapr 之间通信的自定义处理,反之亦然。例如,Dapr 提供了一个现成的 OAuth 2.0 中间件管道组件。
- 巨大的可扩展性 — 这是由于 Dapr 的解耦、基于接口的设计,以及它的组件化插件架构。请注意,在给定的构建基块中,开发人员可以编写自己的代码来实现针对其特定需求定制的 Dapr 组件。
- HTTP 和 gRPC 通信,以及对最流行的编程语言和云提供商的支持。
请注意,上述所有预构建的插件组件也是可配置的。"插入"特定组件的行为只是在标准组件目录中提供声明性配置文件。Dapr 负责加载组件代码和"挂接"所需的工作。
如果我告诉你Dapr是一个Sidecar,你就会知道,通常Dapr的单个实例与服务的单个实例配对,每个实例都在自己的进程中运行。Dapr 目前不是代码库。Dapr Sidecar 实例和使用它的服务实例通过 HTTP 或 gRPC(使用 HTTP2)跨其进程边界相互通信,如下图 所示。当您将服务实例与 Dapr Sidecar 配对时,您实质上是"Daprize"您的服务。
此外,每个 Dapr Sidecar 实例都知道协作"Daprized"服务系统中的所有其他 Dapr Sidecar 实例。所有这些协作 Dapr Sidecar 实例都使用 gRPC 在单独的 Sidecar 专用通信通道上完全在后台相互通信,如下图所示。以这种方式在Dapr Sidecars之间进行通信是Dapr的Pub / Sub,服务调用和Actor模型功能的关键。Dapr Sidecar 和使用 Dapr Sidecar 实例的服务通常在单独的容器中运行,或作为单独的独立进程运行。
"Daprized"服务通常只与其单个私有 Dapr Sidecar 交互,如上图 所示,将所有凌乱的管道细节以及如何与其他服务、存储、机密等通信的知识留给 Dapr Sidecar 本身,以及 Dapr Sidecar 实例中使用的 Dapr 组件。这使得服务代码变得不那么复杂,并且在服务中的业务逻辑代码与 Dapr Sidecar 及其组件中的管道逻辑代码(也称为基础结构代码)之间提供了很好的关注点分离。在上面的图 中,黄色服务包含所有业务逻辑,而粉红色的 Dapr Sidecar 包含大部分(如果不是全部)管道代码。业务逻辑代码和管道逻辑代码之间的这种关注点分离是使用Dapr导致的技术债务显着减少的关键之一。因此,当需求不可避免地发生变化时,通常需要更改的代码比管道代码与业务逻辑代码混合和交织的情况要少得多。
最后,在业务逻辑和管道代码之间实现高度的关注点分离,再加上组件,开发人员将更多的时间集中在高价值的业务逻辑上,而将更少的时间集中在低价值的组件管道代码上。虽然低级管道规范是绝对必要的,但开发低级管道规范既复杂又耗时,并且需要高水平的经验 - 所有这些都需要时间和金钱。相反,使用预构建的"插件"组件可以将开发人员的大部分时间和技能重新定向到开发业务逻辑上,从而产生与软件最终用户最直接相关的价值。
现在可以看到Dapr Sidecar(加上它使用的组件)如何站在使用Sidecar的服务和分布式系统中的所有其他服务之间,以及"Daprized"服务可以连接到的所有可能的云服务或本地服务之间。这可能是很多服务!因此,在理解"Daperized"服务的以下3个主要使用场景时,Dapr作为中介的角色至关重要。
- 可移植服务 — 一次写入/随处运行。Dapr 在此方案中表现出色,允许开发人员只需插入服务用于与外部服务和资源交互的不同组件(以声明方式配置)。将服务与 Dapr Sidecar 实例放在单独的容器中一起放入容器中,可以高度隔离硬编码的外部依赖项。这允许相同的"Daprized"和容器化服务在本地、云中或边缘设备(如 IoT 现场网关)上运行,而无需更改服务代码。相反,可能只需要对与外部依赖项接口的 Dapr 组件的声明性定义进行更改。从本质上讲,将容器化的Dapr Sidecar与容器化服务集成实际上将服务的所有外部依赖关系分离,从而以较低的工作成本实现最大的可移植性,以将端口移植到不同的托管环境或连接到不同的外部服务。
- 多语言服务系统。借助 Dapr 的服务调用、发布/订阅、机密以及状态存储以及资源绑定和触发器,用各种语言编写的服务可以相互通信,而无需重写大量代码,开发人员也不必学习许多其他语言。为了便于理解,Dapr 为许多流行的语言(如 .NET C#、Java、JavaScript、Python 和 Go 等)提供了软件开发工具包 (SDK)。从本质上讲,使用任何受支持语言和 SDK 的开发人员将针对相同的标准化 Dapr 接口进行编程,而不是针对一堆特定于临时语言或外部服务接口进行编程。这也方便了其他2个使用场景。
- 延长旧版软件的使用寿命也属于此使用场景。但请注意,旧版软件绝对必须支持与其Dapr Sidecar的HTTP交互。如果是这种情况,那么"Daprize"遗留软件可能是可行的,以允许它更经济高效地成为服务系统的一部分,通过Dapr Sidecar及其构建块和组件与其他服务和资源进行通信。
- 具有动态依赖项的静态服务。需要将发布/订阅消息主干从 Redis 更改为 Azure 服务总线?当组织需要适应变化时,通常会发生这种情况。使用"Daprized"服务系统,与在不使用 Dapr 的情况下重写许多行代码(在许多单个服务中实现发布/订阅消息传递)的成本相比,进行此更改(即更改声明性组件定义)的每个服务的成本可能相当小。这同样适用于其他构建基块及其组件。
另请注意,"Daprized"服务可以托管在Kubernetes上的容器中,也可以托管在支持Docker等容器的其他主机上,包括在适当的情况下使用Docker Compose。"Daprized"服务也可以托管,而无需在各种计算主机(包括您自己的开发系统)上使用容器作为独立进程。
纵观软件的历史,Dapr的潜在节省时间和劳动力的特点真的是一件大事!到目前为止,还没有像Dapr这样的东西,它几乎可以在任何地方运行,并且还提供了与分布式系统服务的大规模解耦,以及组件化和出色的关注点分离。所有这些都减少了初始开发所需的工作,并且从长远来看,还导致技术债务明显低于平时。从短期和长期来看,所有这些都可以显著提高软件开发生产力,从而减少需要完成的工作量,节省时间和金钱。