mcpField
D
dotnet-aspire-patterns

dotnet-aspire-patterns

github.com/wshaddix云与 DevOps8667 次安装35 周前更新
代码仓库

关于 dotnet-aspire-patterns

dotnet-aspire-patterns 是面向分布式应用开发者的 .NET Aspire 技能,涵盖 AppHost 编排、服务发现、后端服务组件、本地可观测性和健康检查,并区分开发编排与生产部署。

主要功能

  1. 01AppHost 拓扑与资源生命周期
  2. 02服务发现和连接字符串
  3. 03数据库、缓存与消息代理组件
  4. 04用于健康检查和遥测的 ServiceDefaults
  5. 05展示日志、追踪和指标的本地仪表板
  6. 06Aspire 与手动编排的选型指导

适用场景

  1. 01连接多个 .NET 服务和后端基础设施
  2. 02依据资源健康状态安排启动顺序
  3. 03统一各服务项目的可观测性
  4. 04评估本地编排方案

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/wshaddix/dotnet-skills --skill dotnet-aspire-patterns

安装后在 Claude Code 中输入/dotnet-aspire-patterns确认已加载。

详细介绍

dotnet-aspire-patterns

用于构建云就绪型分布式应用的 .NET Aspire 编排模式。涵盖 AppHost 配置、服务发现、用于集成后端服务(数据库、缓存、消息代理)的组件模型、本地可观测性 Aspire 仪表板、分布式运行状况检查,以及何时选择 Aspire 而非手动容器编排。

不在范围内: 编写原始 Dockerfile 和多阶段构建——请参阅 [skill:dotnet-containers]。Kubernetes 清单、Helm 图表和 Docker Compose——请参阅 [skill:dotnet-container-deployment]。OpenTelemetry SDK 配置和自定义指标——请参阅 [skill:dotnet-observability]。DI 服务生命周期机制——请参阅 [skill:dotnet-csharp-dependency-injection]。后台服务托管——请参阅 [skill:dotnet-background-services]。

交叉引用:[skill:dotnet-containers] 介绍容器镜像优化和基础镜像选择,[skill:dotnet-container-deployment] 介绍生产环境中的 Kubernetes/Compose 部署,[skill:dotnet-observability] 介绍 Aspire 默认设置以外的 OpenTelemetry 详细信息,[skill:dotnet-csharp-dependency-injection] 介绍 DI 基础知识,[skill:dotnet-background-services] 介绍托管服务生命周期模式。


Aspire 概述

.NET Aspire 是一个有主见的技术栈,用于构建可观测、可用于生产环境的分布式应用。它提供:

  • 编排——使用 C# 定义分布式拓扑(AppHost)
  • 组件——用于常见后端服务的预配置 NuGet 包
  • 服务默认配置——用于 OpenTelemetry、运行状况检查和弹性的共享配置
  • 仪表板——用于本地开发的 UI,可查看跟踪、日志、指标和资源状态

Aspire 不是部署目标。它负责编排本地开发和测试体验。对于生产环境,它会生成由部署工具(Azure Developer CLI、Kubernetes 等)使用的清单。

何时使用 Aspire
场景建议
多个 .NET 服务 + 后端基础设施Aspire AppHost——简化本地开发和服务连接
单个 API 和一个数据库可选——对于简单的拓扑,Aspire 会增加开销
仅使用非 .NET 服务(Node、Python)Aspire 可以引用容器镜像,但工具带来的好处会减少
已需要 Kubernetes/Compose 用于本地开发评估迁移成本;在开发场景中,Aspire 可替代 docker-compose
团队需要统一的可观测性默认设置Aspire ServiceDefaults 可在所有项目中统一 OTel 配置

AppHost 配置

AppHost 是一个 .NET 项目(Aspire.Hosting.AppHost SDK),用于定义分布式应用拓扑。它引用其他项目和后端服务,并通过服务发现将它们连接起来。

AppHost 项目设置
<Project Sdk="Microsoft.NET.Sdk">

  
  <Sdk Name="Aspire.AppHost.Sdk" Version="9.1.*" />

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <IsAspireHost>true</IsAspireHost>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Aspire.Hosting.AppHost" Version="9.1.*" />
    <PackageReference Include="Aspire.Hosting.PostgreSQL" Version="9.1.*" />
    <PackageReference Include="Aspire.Hosting.Redis" Version="9.1.*" />
    <PackageReference Include="Aspire.Hosting.RabbitMQ" Version="9.1.*" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\MyApi\MyApi.csproj" />
    <ProjectReference Include="..\MyWorker\MyWorker.csproj" />
  </ItemGroup>

</Project>
定义拓扑
var builder = DistributedApplication.CreateBuilder(args);

// Backing services -- Aspire manages containers automatically
var postgres = builder.AddPostgres("pg")
    .WithPgAdmin()              // Adds pgAdmin UI container
    .AddDatabase("ordersdb");

var redis = builder.AddRedis("cache")
    .WithRedisCommander();      // Adds Redis Commander UI

var rabbitmq = builder.AddRabbitMQ("messaging")
    .WithManagementPlugin();    // Adds RabbitMQ management UI

// Application projects -- wired with service discovery
var api = builder.AddProject<Projects.MyApi>("api")
    .WithReference(postgres)
    .WithReference(redis)
    .WithReference(rabbitmq)
    .WithExternalHttpEndpoints();  // Marks endpoints as public in deployment manifests

builder.AddProject<Projects.MyWorker>("worker")
    .WithReference(postgres)
    .WithReference(rabbitmq)
    .WaitFor(api);              // Start worker after API is healthy

builder.Build().Run();
资源生命周期

WaitFor 控制启动顺序。资源会等到依赖项报告运行状况正常后再启动:

// Worker waits for both the database and API to be ready
builder.AddProject<Projects.MyWorker>("worker")
    .WithReference(postgres)
    .WaitFor(postgres)          // Wait for database container health check
    .WaitFor(api);              // Wait for API health endpoint

如果不使用 WaitFor,资源会并行启动。仅在启动顺序重要时使用它(例如,需要数据库架构已存在的工作器)。


服务发现

Aspire 会自动配置服务发现,使项目可以通过资源名称而非硬编码 URL 相互解析。

工作原理
  1. AppHost 将终结点信息作为环境变量和配置注入
  2. Aspire.ServiceDefaults 项目配置 Microsoft.Extensions.ServiceDiscovery
  3. 应用代码通过 HttpClient 或连接字符串按名称解析服务
使用已发现的服务
// In MyApi/Program.cs
var builder = WebApplication.CreateBuilder(args);

// AddServiceDefaults registers service discovery, OpenTelemetry, health checks
builder.AddServiceDefaults();

// HttpClient resolves "worker" via service discovery
builder.Services.AddHttpClient("worker-client", client =>
{
    client.BaseAddress = new Uri("https+http://worker");
});

https+http:// 方案前缀会告知服务发现提供程序优先尝试 HTTPS,失败时回退到 HTTP。这是在 Aspire 中进行服务间通信的推荐模式。

连接字符串

对于后端服务(数据库、缓存),Aspire 会通过标准的 ConnectionStrings 配置节注入连接字符串:

// AppHost: .WithReference(postgres) on the API project
// injects ConnectionStrings__ordersdb automatically

// In MyApi/Program.cs
builder.AddNpgsqlDbContext<OrdersDbContext>("ordersdb");
// Resolves ConnectionStrings:ordersdb from configuration

组件模型

Aspire 组件是 NuGet 包,可为后端服务提供预配置的客户端集成。它们负责连接管理、运行状况检查、遥测和弹性。

Hosting 包与客户端包
包类型安装位置用途
Aspire.Hosting.*AppHost 项目定义和配置资源(容器、连接)
Aspire.* (client)服务项目使用资源并启用运行状况检查和遥测

<PackageReference Include="Aspire.Hosting.PostgreSQL" Version="9.1.*" />


<PackageReference Include="Aspire.Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.1.*" />
常见组件
组件Hosting 包客户端包
PostgreSQL(EF Core)Aspire.Hosting.PostgreSQLAspire.Npgsql.EntityFrameworkCore.PostgreSQL
PostgreSQL(Npgsql)Aspire.Hosting.PostgreSQLAspire.Npgsql
Redis(缓存)Aspire.Hosting.RedisAspire.StackExchange.Redis
Redis(输出缓存)Aspire.Hosting.RedisAspire.StackExchange.Redis.OutputCaching
RabbitMQAspire.Hosting.RabbitMQAspire.RabbitMQ.Client
Azure Service BusAspire.Hosting.Azure.ServiceBusAspire.Azure.Messaging.ServiceBus
SQL Server(EF Core)Aspire.Hosting.SqlServerAspire.Microsoft.EntityFrameworkCore.SqlServer
MongoDBAspire.Hosting.MongoDBAspire.MongoDB.Driver
客户端注册
var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();

// Each Add* method registers the client, health check, and telemetry
builder.AddNpgsqlDbContext<OrdersDbContext>("ordersdb");
builder.AddRedisClient("cache");
builder.AddRabbitMQClient("messaging");

组件的 Add* 方法:

  1. 在 DI 中注册客户端/DbContext
  2. 为资源添加运行状况检查
  3. 为客户端配置 OpenTelemetry 检测
  4. 应用默认弹性设置(重试、超时)

ServiceDefaults

ServiceDefaults 项目是一个由所有服务项目引用的共享库。它统一处理横切关注点。

ServiceDefaults 的配置内容
public static class Extensions
{
    public static IHostApplicationBuilder AddServiceDefaults(
        this IHostApplicationBuilder builder)
    {
        // Service discovery
        builder.ConfigureOpenTelemetry();
        builder.AddDefaultHealthChecks();
        builder.Services.AddServiceDiscovery();

        // Resilience for HttpClient
        builder.Services.ConfigureHttpClientDefaults(http =>
        {
            http.AddStandardResilienceHandler();
            http.AddServiceDiscovery();
        });

        return builder;
    }

    public static IHostApplicationBuilder ConfigureOpenTelemetry(
        this IHostApplicationBuilder builder)
    {
        builder.Logging.AddOpenTelemetry(logging =>
        {
            logging.IncludeFormattedMessage = true;
            logging.IncludeScopes = true;
        });

        builder.Services.AddOpenTelemetry()
            .WithMetrics(metrics => metrics
                .AddAspNetCoreInstrumentation()
                .AddHttpClientInstrumentation()
                .AddRuntimeInstrumentation())
            .WithTracing(tracing => tracing
                .AddAspNetCoreInstrumentation()
                .AddGrpcClientInstrumentation()
                .AddHttpClientInstrumentation());

        builder.AddOpenTelemetryExporters();
        return builder;
    }

    public static IHostApplicationBuilder AddDefaultHealthChecks(
        this IHostApplicationBuilder builder)
    {
        builder.Services.AddHealthChecks()
            .AddCheck("self", () => HealthCheckResult.Healthy());

        return builder;
    }

    public static WebApplication MapDefaultEndpoints(
        this WebApplication app)
    {
        app.MapHealthChecks("/health");
        app.MapHealthChecks("/alive", new HealthCheckOptions
        {
            Predicate = r => r.Tags.Contains("live")
        });

        return app;
    }
}
使用 ServiceDefaults

每个服务项目都会引用 ServiceDefaults 项目并调用扩展方法:

var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();

// ... service-specific registrations

var app = builder.Build();
app.MapDefaultEndpoints();

// ... middleware and endpoints

app.Run();

控制面板

Aspire 控制面板提供本地可观测性 UI,会随 AppHost 自动启动。它会显示:

  • 资源 —— 所有项目、容器和可执行文件的状态
  • 控制台日志 —— 汇总所有资源的 stdout/stderr
  • 结构化日志 —— 带结构化属性的 OpenTelemetry 日志记录
  • 追踪 —— 所有服务间的分布式追踪
  • 指标 —— 实时指标图表
访问控制面板

运行 AppHost(dotnet run --project MyApp.AppHost)时,控制面板 URL 会打印到控制台:

info: Aspire.Hosting.DistributedApplication[0]
      Login to the dashboard at https://localhost:17043/login?t=<token>
在非 Aspire 项目中使用控制面板

对于未使用完整 Aspire 技术栈的项目,控制面板可作为独立容器使用:

docker run --rm -it -p 18888:18888 -p 4317:18889 \
  -d --name aspire-dashboard \
  mcr.microsoft.com/dotnet/aspire-dashboard:9.1

将应用配置为向 http://localhost:4317 导出 OTLP 遥测数据,然后通过 http://localhost:18888 查看。


运行状况检查和分布式追踪

组件运行状况检查

每个 Aspire 组件都会自动注册运行状况检查。AppHost 会用这些检查来确定资源是否就绪:

// In AppHost -- WaitFor uses health checks to gate startup
builder.AddProject<Projects.MyApi>("api")
    .WithReference(postgres)
    .WaitFor(postgres);     // Waits for Npgsql health check to pass
自定义运行状况检查

在 Aspire 默认检查之外,添加特定于应用的运行状况检查:

builder.Services.AddHealthChecks()
    .AddCheck<OrderProcessingHealthCheck>(
        "order-processing",
        tags: ["ready"]);

有关运行状况检查模式(存活性与就绪性、自定义检查、运行状况检查发布)的详细信息,请参阅 [skill:dotnet-observability]。

分布式追踪集成

Aspire 通过 ServiceDefaults 配置 OpenTelemetry 追踪。追踪会自动跨 HTTP 边界传播。如需自定义 Span:

private static readonly ActivitySource s_activitySource = new("MyApp.Orders");

public async Task<Order> ProcessOrderAsync(CreateOrderRequest request, CancellationToken ct)
{
    using var activity = s_activitySource.StartActivity("ProcessOrder");
    activity?.SetTag("order.customer_id", request.CustomerId);

    // Calls to other Aspire services carry trace context automatically
    var inventory = await _httpClient.GetFromJsonAsync<InventoryResponse>(
        $"https+http://inventory-api/api/stock/{request.ProductId}", ct);

    // ... process order
    return order;
}

有关分布式追踪的全面指南(自定义 ActivitySource、追踪上下文传播、Span 事件),请参阅 [skill:dotnet-observability]。


容器资源

添加容器镜像

对于没有 Aspire 组件的服务,可添加任意容器镜像:

var seq = builder.AddContainer("seq", "datalust/seq")
    .WithHttpEndpoint(port: 5341, targetPort: 80)
    .WithEnvironment("ACCEPT_EULA", "Y");

// Reference the container from a project
builder.AddProject<Projects.MyApi>("api")
    .WithReference(seq);
持久卷

默认情况下,Aspire 容器使用临时存储。添加卷以便在重启后保留数据:

var postgres = builder.AddPostgres("pg")
    .WithDataVolume("pg-data")     // Named volume for data persistence
    .AddDatabase("ordersdb");
外部资源

引用 Aspire 未管理的现有基础设施:

// Connection string from configuration (not an Aspire-managed container)
var existingDb = builder.AddConnectionString("legacydb");

builder.AddProject<Projects.MyApi>("api")
    .WithReference(existingDb);

Aspire 与手动容器编排

关注点AspireDocker Compose / 手动方式
配置语言C#(强类型)YAML
服务发现自动(注入环境变量)手动配置 DNS/环境变量
健康检查每个组件自动配置为每项服务手动配置 HEALTHCHECK
可观测性预配置 OTel + 仪表板手动配置 OTel 收集器
IDE 集成热重载、F5 调试手动附加调试器
生产部署生成清单(AZD、K8s)直接编写清单
非 .NET 服务容器引用(集成度较低)对所有语言一视同仁地支持
学习曲线.NET 专属抽象行业标准工具

当你的技术栈主要使用 .NET,且希望获得标准化的可观测性、服务发现和简化的本地开发体验时,选择 Aspire。当你需要精细控制、多语言服务,或团队已经熟练掌握 Compose/Kubernetes 时,选择手动编排。


核心原则

  • AppHost 仅用于开发阶段——它负责编排本地开发,而非生产部署
  • 使用组件,而非原始连接字符串——组件会自动添加健康检查、遥测和弹性功能
  • ServiceDefaults 不可或缺——每个 Aspire 服务项目都必须引用它,以确保可观测性一致
  • 通过 WaitFor 控制启动顺序——将其用于实际依赖(架构迁移、种子数据),而非每项资源
  • 不要重复配置 OTel——Aspire ServiceDefaults 会配置 OpenTelemetry;手动配置会导致重复收集

Agent 注意事项

  1. 不要在 Aspire 服务项目中手动配置 OpenTelemetry——ServiceDefaults 已注册 OTel 提供程序。添加手动 .AddOpenTelemetry() 调用会导致重复收集跟踪和指标,并增加遥测成本。
  2. 不要在 Aspire 服务项目中硬编码连接字符串——使用 builder.AddNpgsqlDbContext<T>("name") 或 builder.Configuration.GetConnectionString("name")。Aspire 会通过环境变量注入连接字符串;硬编码值会绕过服务发现。
  3. 不要对每项资源都使用 WaitFor——这会使启动过程串行化并延长启动时间。仅当服务确实无法在没有依赖项的情况下启动时才使用(例如,启动时执行数据库迁移)。
  4. 不要在服务项目中引用 Aspire.Hosting.* 包——Hosting 包仅应放在 AppHost 中。服务项目应使用客户端包(Aspire.Npgsql、Aspire.StackExchange.Redis 等)。
  5. 不要将 AppHost 与生产主机混为一谈——AppHost 在本地(或 CI 中)运行,用于编排资源。生产部署使用生成的清单或基础设施即代码。
  6. 新服务项目中不要省略 AddServiceDefaults()——缺少它会导致项目缺失服务发现、健康检查和遥测功能,从而使 Aspire 集成在无提示的情况下失效。

先决条件

  • .NET 10 SDK(或安装了 Aspire 工作负载的 .NET 8/9)
  • Docker Desktop 或 Podman(用于容器资源)
  • Aspire 工作负载:dotnet workload install aspire

参考资料

评论

支持 Markdown