理解微服务中的开发者体验
当人们谈论开发者体验(DX)时,通常想到的是工具或人机工程学,比如良好的文档、快速的构建时间和清晰的API。但在分布式系统中,开发者体验的范围要广泛得多。它关乎开发者能够多轻松地设置、运行和理解他们正在构建的系统。
在单体应用中,启动开发环境可能只需要运行像dotnet run这样的单个命令。但在基于微服务的系统中,你可能需要启动多个API、数据库、后台工作器和队列,所有这些都有特定的配置依赖。这种额外的开销不仅会拖慢速度,还会分散注意力,给日常开发增加摩擦。
随着时间的推移,这种摩擦会不断累积:
- 新开发者的上手速度变慢
- 跨服务边界调试变得更加困难
- 团队花在管理环境上的时间比编写功能还多
这就是为什么在微服务架构中开发者体验如此重要。这不仅关乎开发者的幸福感,更关乎开发速度、一致性和信心。如果你的本地环境不容易运行或理解,开发生命周期中的其他所有流程都会受到影响。
介绍.NET Aspire
随着微服务系统的增长,本地开发环境通常变成了脚本、Docker Compose文件和手动设置步骤的大杂烩。每个开发者最终都要管理自己版本的"如何让系统运行起来",配置上的微小差异可能导致团队之间的巨大不一致。
.NET Aspire是一个编排框架,旨在简化这一过程。它提供了一种在.NET解决方案中定义、配置和运行分布式应用程序的方法。
实际上,Aspire通过自动处理三个关键领域来帮助开发者:
服务编排:Aspire可以按正确顺序启动多个项目(API、工作器、数据库等)。它处理服务依赖关系,例如,确保你依赖的数据库准备就绪后,API才会启动。
配置管理:Aspire提供集中式配置模型,而不是在数十个appsettings.json文件或环境变量之间来回切换。它以一致的方式跨服务共享连接字符串、端口和环境设置。
可观测性和洞察:Aspire包含内置的OpenTelemetry支持和一个仪表板,提供对运行中服务的实时可见性,包括它们的健康状况、日志和端点。这使得调试和本地监控变得更加容易。
如何在项目中设置.NET Aspire
我们将创建一个微服务设置,并观察Aspire如何用最少的代码来编排它。确保你运行的是.NET 8或更高版本。Aspire需要这个版本。
创建新的Aspire项目
首先使用.NET CLI创建一个新的Aspire应用主机:
|
|
此命令创建一个新的Aspire"主机"项目,这是编排其他微服务、API和依赖项的入口点。
你会注意到生成的项目包含一个带有AppHostBuilder的Program.cs文件。这个构建器充当分布式系统的控制中心。
添加你的微服务
你现在可以在同一解决方案中引用现有项目或直接创建新项目。例如:
|
|
然后,通过编辑Program.cs将它们添加到你的Aspire主机:
|
|
在这个例子中:
AddProject向Aspire注册每个服务.WaitFor()强制执行启动依赖关系(例如,OrderService依赖于CatalogService)- Aspire负责以正确的顺序启动这些服务,共享环境变量,并自动管理端口
用一个命令运行所有服务
现在,从你的应用主机目录运行:
|
|
Aspire将:
- 启动所有注册的服务
- 分配可用端口
- 注入共享配置
- 启动显示服务健康状况、端点和日志的本地仪表板
你应该看到类似这样的输出:
|
|
当你在浏览器中打开仪表板时,你会看到所有服务、它们的状态以及它们的API链接。
添加本地数据库(可选)
为了展示Aspire如何处理依赖关系,让我们添加一个PostgreSQL容器:
|
|
现在当你运行应用时,Aspire将首先启动PostgreSQL,生成连接字符串,并将其传递给CatalogService。不需要手动设置或.env文件。
为什么这对开发者体验很重要
在Aspire之前,让本地环境运行意味着打开多个终端,等待数据库启动,并在项目之间复制连接字符串。有了Aspire,只需要一个命令。所有东西都会自动启动,配置在服务之间共享,并且内置了可观测性。这就是开发者体验的胜利。更少的时间与设置斗争,更多的时间实际编码。
框架:如何逐步采用.NET Aspire
如果你考虑在自己的团队中尝试Aspire,你不必一次性迁移所有内容。事实上,最好的方法是逐步采用。从小开始,逐渐扩展。
以下是你可以遵循的简单框架:
步骤1:从小开始
创建一个Aspire主机并连接一个或两个关键服务。这有助于你的团队在扩展之前理解编排流程。
|
|
步骤2:逐步添加依赖关系
随着增长,包含更多服务并使用.WaitFor()定义依赖关系和启动顺序。
|
|
步骤3:集成可观测性
利用Aspire内置的OpenTelemetry集成来获取指标和追踪。即使没有外部工具,你也能立即获得对服务交互的更好洞察。
步骤4:共享你的设置
将你的Aspire主机提交到源代码控制,这样每个开发者都使用相同的设置。这确保了环境之间的一致性,减少了经典的"在我机器上能运行"问题。
注意:Aspire不需要完全重写。它作为一个起始层工作得很好,而你的团队继续演进现有的编排设置。
如何使用.NET Aspire仪表板
.NET Aspire的突出特性之一是其内置仪表板,它在你本地运行微服务时提供实时可见性。
当你用dotnet run启动Aspire应用主机时,它会自动启动一个本地仪表板(默认在http://localhost:18888)。这个仪表板提供了所有服务的集中视图——API、数据库、后台工作器和任何连接的依赖项。
以下是你在里面能找到的内容:
服务概览
仪表板主页列出了分布式应用程序中的每个服务。对于每个服务,你可以看到:
- 名称和类型(例如,缓存、apiservice、webfrontend)
- 当前状态(运行中、启动中、已停止)
- 来源
- 端口和端点信息
- 启动时间和运行时间
- 日志和指标快捷方式
这立即取代了需要跟踪多个终端窗口或滚动浏览数十个日志来确认所有内容正确启动的需求。
仪表板自动检测不健康或失败的服务并高亮显示它们,因此你可以及早识别启动问题。
导航到端点
每个服务卡片包括其暴露端点的快速链接,提供对相关工具和界面的轻松访问。例如,API可能包括到Swagger UI或Scalar的链接,数据库可能链接到pgAdmin或类似的管理工具,内部服务可能提供到自定义仪表板的链接。
这种设置允许用户直接从仪表板测试API或验证数据库连接,无需记住特定端口或手动构建URL。
实时日志
点击特定服务会打开一个详细视图,显示直接从该服务流式传输的实时日志。
这在调试启动问题或服务交互时特别有用。你可以在一个地方查看所有服务的日志,而不是在单独的终端中运行dotnet run,颜色编码和时间戳使其更加清晰。
内置可观测性(OpenTelemetry)
Aspire默认包含OpenTelemetry,这意味着即使没有额外配置,你也能自动访问几个强大的可观测性特性。这些包括跨服务边界的分布式追踪、性能监控的指标,以及帮助跟踪跨多个服务请求的关联日志。
对于已经使用Grafana、Jaeger或SigNoz等工具的团队,Aspire可以用最少的设置将此遥测数据导出到你偏好的可观测性平台。
启用追踪后,你可以跟随请求从API到数据库,通过后台工作器,再返回,所有这些都可以在仪表板内完成。
实际场景:用.NET Aspire解决现实世界的DX挑战
到目前为止,我们已经了解了Aspire如何工作以及它开箱即用提供了什么。但为了真正理解它对开发者体验的影响,让我们看看几乎每个构建微服务的团队都面临的几个现实痛点,以及Aspire如何帮助解决它们。
以正确顺序启动多个服务
问题:在大多数微服务设置中,服务启动顺序很重要。例如,你的API网关可能依赖于用户服务和目录服务,而这两者都依赖于数据库。如果你以错误的顺序启动这些,网关将无法连接,你最终需要手动重启服务,直到一切稳定。
Aspire如何解决:Aspire提供了一种使用.WaitFor()表达依赖关系的简单方法:
|
|
端口冲突和配置漂移
问题:开发者经常遇到可怕的"端口5000已被使用",或者花时间编辑配置文件以避免冲突。随着时间的推移,本地设置在团队中产生分歧,使得上手和调试更加困难。
Aspire如何解决:Aspire在运行时动态管理端口和配置。每个服务获得唯一的端口分配,Aspire自动跨服务共享连接信息。
你仍然可以在需要时设置显式端口:
|
|
这消除了猜测工作,保持环境一致,并确保新开发者可以克隆仓库并启动所有内容,无需编辑配置文件。
简化新开发者上手
Aspire如何解决:Aspire在代码中定义你的整个环境。这意味着设置过程变得像克隆仓库并运行一个命令一样简单:
|
|
Aspire将启动所有必要的服务,配置依赖关系,并启动仪表板以提供可见性。这将上手过程从多小时的流程转变为可以在几分钟内完成的事情,设置问题大大减少。
改进调试和跨服务可见性
问题:微服务中的调试通常意味着在日志之间跳转,跨多个服务追踪请求,或者重现仅当多个服务一起运行时才出现的问题。
Aspire如何解决:通过内置的可观测性和Aspire仪表板,你可以在一个地方查看所有服务的日志,检查健康检查和指标,并使用OpenTelemetry追踪请求。这使得跨服务边界识别问题变得更加容易,并加快了调试速度,特别是在集成测试或本地开发期间。
运行可选或外部服务
问题:有时你不需要在本地运行每个服务。例如,你可能连接到共享的暂存API或外部依赖项,而不是运行本地实例。
Aspire如何解决:Aspire允许你使用条件检查使服务成为可选的:
|
|
这使你的设置变得灵活:你可以为开发运行最小环境,或为集成测试运行完整环境,所有这些都使用相同的配置。
进一步探索
一旦你熟悉了Aspire的基础知识,你可以将其扩展到本地编排之外,以简化工作流程的其他部分。
- 集成前端应用程序:将React、Angular或Node.js应用与你的.NET服务一起编排,实现统一的全栈设置
- 导出遥测数据:将Aspire的OpenTelemetry输出发送到Grafana、Jaeger或Azure Application Insights等平台进行更深入的分析
- 在CI/CD管道中使用Aspire:在持续集成运行期间为集成或冒烟测试启动完整环境,所有这些都使用你现有的Aspire配置
- 探索社区示例:查看官方的Aspire示例和模板,了解高级编排模式、云集成和可观测性设置
关键要点和何时使用.NET Aspire
正如我们在本指南中看到的,.NET Aspire不仅仅是另一个开发者工具,它是一个专门构建的框架,旨在改善基于微服务的应用程序中的开发者体验。
通过以一致、声明性的方式编排所有服务,Aspire帮助团队减少摩擦,加快设置速度,并使本地环境更加可靠和可观测。
关键要点
- 开发者体验(DX)随着系统增长而变得重要:微服务引入了灵活性和可扩展性,但它们也增加了复杂性;多个服务、端口、依赖关系和启动序列。没有良好的编排,开发者体验会迅速下降。
- Aspire简化了本地开发的编排:它自动处理服务启动、依赖关系、配置共享和可观测性,所有这些都在代码中定义,就在你的.NET解决方案中。
- Aspire仪表板提高了可见性:你获得了一个集中、实时的整个系统视图;服务、日志、健康状况和端点,消除了对多个终端或手动跟踪的需求。
- 新开发者上手变得更快更顺畅:单个
dotnet run命令可以启动整个开发环境,将设置时间从数小时或数天减少到几分钟。 - 内置可观测性意味着更好的调试和信心:通过开箱即用的OpenTelemetry集成,开发者可以用最少的设置跨服务追踪请求、监控性能和诊断问题。
何时(以及何时不)使用.NET Aspire
在以下情况下使用Aspire: 如果你正在构建.NET微服务并且厌倦了复杂的本地设置,Aspire是有意义的。当你的团队处理环境漂移、缓慢的上手或感觉像杂耍的启动序列时,它特别有价值。如果你想要一个命令启动整个系统,并且从一开始就内置可观测性,Aspire值得尝试。
在以下情况下可能不需要Aspire: 如果你当前的设置已经运行良好,Aspire可能不值得。也许你在本地使用Kubernetes或Docker Compose,一切运行顺利。或者你正在构建不需要编排的单体或单一服务。或者你的堆栈有很多需要自定义连接的非.NET组件。如果你的本地开发已经简单稳定,不要修复没有损坏的东西。
换句话说:Aspire在本地开发和上手阶段表现出色。帮助开发者以最小的摩擦构建、测试和迭代分布式系统。它并非旨在取代像Kubernetes这样的生产编排器,而是通过改善开发者的日常工作流程来补充它们。
结论
当团队转向微服务时,开发者体验经常被忽视,但它直接影响生产力、质量和士气。通过使用.NET Aspire,你可以将秩序、可见性和简单性带回你的本地开发环境。
如果你想简化微服务工作流程,试试Aspire。你将花更少的时间与设置斗争,更多的时间构建真正重要的东西——伟大的软件。