Aspire Service Defaults

aaronontheweb/dotnet-skills/skills/aspire-service-defaults

作者 aarononthewebe426ed93a9f3无许可证1.2K 个星标收录于 2026年10月8日更新于 2026年10月8日仓库3周前更新

Create a shared ServiceDefaults project for Aspire applications. Centralizes OpenTelemetry, health checks, resilience, and service discovery configuration across all services.

AI 生成的概览

创建共享的 Aspire ServiceDefaults 项目,集中管理 OpenTelemetry、健康检查、弹性和服务发现。

功能
提供在 .NET Aspire 分布式应用中构建共享 ServiceDefaults 项目的说明和 C# 代码。内容涵盖项目文件、包含 AddServiceDefaults 的 Extensions.cs、OpenTelemetry 配置、健康检查端点、HTTP 弹性和服务发现。还展示 API 与工作服务如何调用这些扩展,以及如何添加自定义健康检查和跟踪源。
适用场景
适用于构建需要跨服务保持一致可观测性、健康检查、弹性和服务发现的 Aspire 分布式应用。也适用于配置 HttpClient 弹性或共享健康检查端点。
运行要求
需要 .NET 9 Aspire 应用以及所引用的 NuGet 包(OpenTelemetry、Microsoft.Extensions.Http.Resilience、Microsoft.Extensions.ServiceDiscovery)。不包含脚本,仅为说明和代码示例。

Aspire Service Defaults

When to Use This Skill

Use this skill when:

  • Building Aspire-based distributed applications
  • Need consistent observability (logging, tracing, metrics) across services
  • Want shared health check configuration
  • Configuring HttpClient resilience and service discovery

What is ServiceDefaults?

ServiceDefaults is a shared project that provides common configuration for all services in an Aspire application:

  • OpenTelemetry - Logging, tracing, and metrics
  • Health Checks - Readiness and liveness endpoints
  • Service Discovery - Automatic service resolution
  • HTTP Resilience - Retry and circuit breaker policies

Every service references this project and calls AddServiceDefaults().


Project Structure

src/  MyApp.ServiceDefaults/    Extensions.cs    MyApp.ServiceDefaults.csproj  MyApp.Api/    Program.cs  # Calls AddServiceDefaults()  MyApp.Worker/    Program.cs  # Calls AddServiceDefaults()  MyApp.AppHost/    Program.cs

ServiceDefaults Project

Project File

xml
<Project Sdk="Microsoft.NET.Sdk">  <PropertyGroup>    <TargetFramework>net9.0</TargetFramework>    <IsAspireSharedProject>true</IsAspireSharedProject>  </PropertyGroup>
  <ItemGroup>    <FrameworkReference Include="Microsoft.AspNetCore.App" />    <PackageReference Include="Microsoft.Extensions.Http.Resilience" />    <PackageReference Include="Microsoft.Extensions.ServiceDiscovery" />    <PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" />    <PackageReference Include="OpenTelemetry.Extensions.Hosting" />    <PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" />    <PackageReference Include="OpenTelemetry.Instrumentation.Http" />    <PackageReference Include="OpenTelemetry.Instrumentation.Runtime" />  </ItemGroup></Project>

Extensions.cs

csharp
using Microsoft.AspNetCore.Builder;using Microsoft.AspNetCore.Diagnostics.HealthChecks;using Microsoft.Extensions.DependencyInjection;using Microsoft.Extensions.Diagnostics.HealthChecks;using Microsoft.Extensions.Logging;using OpenTelemetry;using OpenTelemetry.Metrics;using OpenTelemetry.Trace;
namespace Microsoft.Extensions.Hosting;
public static class Extensions{    private const string HealthEndpointPath = "/health";    private const string AlivenessEndpointPath = "/alive";
    /// <summary>    /// Adds common Aspire services: OpenTelemetry, health checks,    /// service discovery, and HTTP resilience.    /// </summary>    public static TBuilder AddServiceDefaults<TBuilder>(this TBuilder builder)        where TBuilder : IHostApplicationBuilder    {        builder.ConfigureOpenTelemetry();        builder.AddDefaultHealthChecks();
        builder.Services.AddServiceDiscovery();
        builder.Services.ConfigureHttpClientDefaults(http =>        {            // Resilience: retries, circuit breaker, timeouts            http.AddStandardResilienceHandler();
            // Service discovery: resolve service names to addresses            http.AddServiceDiscovery();        });
        return builder;    }
    public static TBuilder ConfigureOpenTelemetry<TBuilder>(this TBuilder builder)        where TBuilder : IHostApplicationBuilder    {        // Logging        builder.Logging.AddOpenTelemetry(logging =>        {            logging.IncludeFormattedMessage = true;            logging.IncludeScopes = true;        });
        builder.Services.AddOpenTelemetry()            // Metrics            .WithMetrics(metrics =>            {                metrics                    .AddAspNetCoreInstrumentation()                    .AddHttpClientInstrumentation()                    .AddRuntimeInstrumentation();            })            // Tracing            .WithTracing(tracing =>            {                tracing                    .AddSource(builder.Environment.ApplicationName)                    .AddAspNetCoreInstrumentation(options =>                        // Exclude health checks from traces                        options.Filter = context =>                            !context.Request.Path.StartsWithSegments(HealthEndpointPath) &&                            !context.Request.Path.StartsWithSegments(AlivenessEndpointPath))                    .AddHttpClientInstrumentation();            });
        builder.AddOpenTelemetryExporters();
        return builder;    }
    private static TBuilder AddOpenTelemetryExporters<TBuilder>(this TBuilder builder)        where TBuilder : IHostApplicationBuilder    {        // Use OTLP exporter if endpoint is configured (Aspire Dashboard, Jaeger, etc.)        var useOtlp = !string.IsNullOrWhiteSpace(            builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);
        if (useOtlp)        {            builder.Services.AddOpenTelemetry().UseOtlpExporter();        }
        return builder;    }
    public static TBuilder AddDefaultHealthChecks<TBuilder>(this TBuilder builder)        where TBuilder : IHostApplicationBuilder    {        builder.Services.AddHealthChecks()            .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]);
        return builder;    }
    /// <summary>    /// Maps health check endpoints. Call after UseRouting().    /// </summary>    public static WebApplication MapDefaultEndpoints(this WebApplication app)    {        // Only expose in development - see security note below        if (app.Environment.IsDevelopment())        {            // Readiness: all health checks must pass            app.MapHealthChecks(HealthEndpointPath);
            // Liveness: only "live" tagged checks            app.MapHealthChecks(AlivenessEndpointPath, new HealthCheckOptions            {                Predicate = r => r.Tags.Contains("live")            });        }
        return app;    }}

Usage in Services

API Service

csharp
var builder = WebApplication.CreateBuilder(args);
// Add all service defaultsbuilder.AddServiceDefaults();
// Add your servicesbuilder.Services.AddControllers();
var app = builder.Build();
// Map health endpointsapp.MapDefaultEndpoints();
app.MapControllers();app.Run();

Worker Service

csharp
var builder = Host.CreateApplicationBuilder(args);
// Works for non-web hosts toobuilder.AddServiceDefaults();
builder.Services.AddHostedService<MyWorker>();
var host = builder.Build();host.Run();

Adding Custom Health Checks

csharp
public static TBuilder AddDefaultHealthChecks<TBuilder>(this TBuilder builder)    where TBuilder : IHostApplicationBuilder{    builder.Services.AddHealthChecks()        // Basic liveness        .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"])
        // Database readiness        .AddNpgSql(            builder.Configuration.GetConnectionString("postgres")!,            name: "postgres",            tags: ["ready"])
        // Redis readiness        .AddRedis(            builder.Configuration.GetConnectionString("redis")!,            name: "redis",            tags: ["ready"])
        // Custom check        .AddCheck<MyCustomHealthCheck>("custom", tags: ["ready"]);
    return builder;}

Adding Custom Trace Sources

For Akka.NET or custom ActivitySources:

csharp
public static TBuilder ConfigureOpenTelemetry<TBuilder>(this TBuilder builder)    where TBuilder : IHostApplicationBuilder{    builder.Services.AddOpenTelemetry()        .WithTracing(tracing =>        {            tracing                .AddSource(builder.Environment.ApplicationName)                // Akka.NET tracing                .AddSource("Akka.NET")                // Custom sources                .AddSource("MyApp.Orders")                .AddSource("MyApp.Payments")                .AddAspNetCoreInstrumentation()                .AddHttpClientInstrumentation();        });
    return builder;}

Production Health Checks

For production, protect health endpoints or use different paths:

csharp
public static WebApplication MapDefaultEndpoints(this WebApplication app){    // Always map for Kubernetes probes, but consider:    // - Using internal-only ports    // - Adding authorization    // - Rate limiting
    app.MapHealthChecks("/health", new HealthCheckOptions    {        // Only return status, not details        ResponseWriter = (context, report) =>        {            context.Response.ContentType = "text/plain";            return context.Response.WriteAsync(report.Status.ToString());        }    });
    app.MapHealthChecks("/alive", new HealthCheckOptions    {        Predicate = r => r.Tags.Contains("live"),        ResponseWriter = (context, report) =>        {            context.Response.ContentType = "text/plain";            return context.Response.WriteAsync(report.Status.ToString());        }    });
    return app;}

Integration with AppHost

The AppHost automatically configures OTLP endpoints:

csharp
// AppHost/Program.csvar builder = DistributedApplication.CreateBuilder(args);
var postgres = builder.AddPostgres("postgres");var redis = builder.AddRedis("redis");
var api = builder.AddProject<Projects.MyApp_Api>("api")    .WithReference(postgres)    .WithReference(redis);
builder.Build().Run();

Services receive OTEL_EXPORTER_OTLP_ENDPOINT automatically, sending telemetry to the Aspire Dashboard.


Best Practices

PracticeReason
One ServiceDefaults projectConsistent config across all services
Filter health checks from tracesReduces noise in observability data
Tag health checksSeparate liveness from readiness
Use StandardResilienceHandlerBuilt-in retry, circuit breaker, timeout
Add custom trace sourcesCapture domain-specific spans

Resources

来源与署名

来源:aaronontheweb/dotnet-skills位于skills/aspire-service-defaults提交e426ed9

许可证: 无许可证

内容归原作者所有。SourceWeft 从公开仓库中收录这些内容。

举报或申请下架