name: aspire-service-defaults description: 为Aspire应用程序创建共享的ServiceDefaults项目。集中所有服务的OpenTelemetry、健康检查、弹性和服务发现配置。 invocable: false
Aspire 服务默认配置
何时使用此技能
在以下情况下使用此技能:
- 构建基于Aspire的分布式应用程序
- 需要在服务间保持一致的观测性(日志、追踪、指标)
- 希望共享健康检查配置
- 配置HttpClient弹性和服务发现
什么是ServiceDefaults?
ServiceDefaults是一个共享项目,为Aspire应用程序中的所有服务提供通用配置:
- OpenTelemetry - 日志、追踪和指标
- 健康检查 - 就绪性和存活性端点
- 服务发现 - 自动服务解析
- HTTP弹性 - 重试和熔断器策略
每个服务都引用此项目并调用AddServiceDefaults()。
项目结构
src/
MyApp.ServiceDefaults/
Extensions.cs
MyApp.ServiceDefaults.csproj
MyApp.Api/
Program.cs # 调用 AddServiceDefaults()
MyApp.Worker/
Program.cs # 调用 AddServiceDefaults()
MyApp.AppHost/
Program.cs
ServiceDefaults 项目
项目文件
<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
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>
/// 添加通用的Aspire服务:OpenTelemetry、健康检查、
/// 服务发现和HTTP弹性。
/// </summary>
public static TBuilder AddServiceDefaults<TBuilder>(this TBuilder builder)
where TBuilder : IHostApplicationBuilder
{
builder.ConfigureOpenTelemetry();
builder.AddDefaultHealthChecks();
builder.Services.AddServiceDiscovery();
builder.Services.ConfigureHttpClientDefaults(http =>
{
// 弹性:重试、熔断器、超时
http.AddStandardResilienceHandler();
// 服务发现:将服务名称解析为地址
http.AddServiceDiscovery();
});
return builder;
}
public static TBuilder ConfigureOpenTelemetry<TBuilder>(this TBuilder builder)
where TBuilder : IHostApplicationBuilder
{
// 日志
builder.Logging.AddOpenTelemetry(logging =>
{
logging.IncludeFormattedMessage = true;
logging.IncludeScopes = true;
});
builder.Services.AddOpenTelemetry()
// 指标
.WithMetrics(metrics =>
{
metrics
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddRuntimeInstrumentation();
})
// 追踪
.WithTracing(tracing =>
{
tracing
.AddSource(builder.Environment.ApplicationName)
.AddAspNetCoreInstrumentation(options =>
// 从追踪中排除健康检查
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
{
// 如果配置了端点(Aspire仪表板、Jaeger等),则使用OTLP导出器
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>
/// 映射健康检查端点。在UseRouting()之后调用。
/// </summary>
public static WebApplication MapDefaultEndpoints(this WebApplication app)
{
// 仅在开发环境中暴露 - 参见下面的安全说明
if (app.Environment.IsDevelopment())
{
// 就绪性:所有健康检查必须通过
app.MapHealthChecks(HealthEndpointPath);
// 存活性:仅包含标记为“live”的检查
app.MapHealthChecks(AlivenessEndpointPath, new HealthCheckOptions
{
Predicate = r => r.Tags.Contains("live")
});
}
return app;
}
}
在服务中的使用
API 服务
var builder = WebApplication.CreateBuilder(args);
// 添加所有服务默认配置
builder.AddServiceDefaults();
// 添加您的服务
builder.Services.AddControllers();
var app = builder.Build();
// 映射健康端点
app.MapDefaultEndpoints();
app.MapControllers();
app.Run();
Worker 服务
var builder = Host.CreateApplicationBuilder(args);
// 也适用于非Web主机
builder.AddServiceDefaults();
builder.Services.AddHostedService<MyWorker>();
var host = builder.Build();
host.Run();
添加自定义健康检查
public static TBuilder AddDefaultHealthChecks<TBuilder>(this TBuilder builder)
where TBuilder : IHostApplicationBuilder
{
builder.Services.AddHealthChecks()
// 基本存活性
.AddCheck("self", () => HealthCheckResult.Healthy(), ["live"])
// 数据库就绪性
.AddNpgSql(
builder.Configuration.GetConnectionString("postgres")!,
name: "postgres",
tags: ["ready"])
// Redis就绪性
.AddRedis(
builder.Configuration.GetConnectionString("redis")!,
name: "redis",
tags: ["ready"])
// 自定义检查
.AddCheck<MyCustomHealthCheck>("custom", tags: ["ready"]);
return builder;
}
添加自定义追踪源
对于Akka.NET或自定义ActivitySources:
public static TBuilder ConfigureOpenTelemetry<TBuilder>(this TBuilder builder)
where TBuilder : IHostApplicationBuilder
{
builder.Services.AddOpenTelemetry()
.WithTracing(tracing =>
{
tracing
.AddSource(builder.Environment.ApplicationName)
// Akka.NET追踪
.AddSource("Akka.NET")
// 自定义源
.AddSource("MyApp.Orders")
.AddSource("MyApp.Payments")
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation();
});
return builder;
}
生产环境健康检查
对于生产环境,保护健康端点或使用不同的路径:
public static WebApplication MapDefaultEndpoints(this WebApplication app)
{
// 始终为Kubernetes探针映射,但请考虑:
// - 使用仅内部端口
// - 添加授权
// - 速率限制
app.MapHealthChecks("/health", new HealthCheckOptions
{
// 仅返回状态,不返回详细信息
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;
}
与AppHost集成
AppHost自动配置OTLP端点:
// AppHost/Program.cs
var 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();
服务自动接收OTEL_EXPORTER_OTLP_ENDPOINT,将遥测数据发送到Aspire仪表板。
最佳实践
| 实践 | 原因 |
|---|---|
| 一个ServiceDefaults项目 | 所有服务配置一致 |
| 从追踪中过滤健康检查 | 减少观测性数据中的噪音 |
| 标记健康检查 | 将存活性与就绪性分离 |
| 使用StandardResilienceHandler | 内置重试、熔断器、超时 |
| 添加自定义追踪源 | 捕获特定领域的跨度 |