.NET Aspire 云原生微服务实战:从本地开发到生产部署完整指南作者:Crown_22 | 云原生 .NET 开发者 | 技术分享前言2024年微软正式发布 .NET Aspire——一个专为云原生应用设计的开发框架。它不是又一个微服务框架,而是解决了微服务开发中最让人头疼的问题:本地开发体验。传统微服务开发的痛点:本地启动10个服务,每个都要单独配置服务发现、数据库连接、消息队列的配置到处都是硬编码本地环境和生产环境的配置差异巨大Dashboard 观测性工具部署复杂.NET Aspire 的核心理念:用 C# 代码定义整个应用拓扑,一键启动本地开发环境,无缝迁移到生产。一、.NET Aspire 架构全景1.1 核心组件┌─────────────────────────────────────────────────────┐ │ .NET Aspire Dashboard │ │ (Traces, Metrics, Logs, Console) │ └──────────────────────┬──────────────────────────────┘ │ ┌──────────────────────┴──────────────────────────────┐ │ AppHost 项目 │ │ (编排器:定义所有服务和资源) │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │ │ │ Web API │ │ Worker │ │ Redis │ │ Postgres│ │ │ │ Service │ │ Service │ │ (资源) │ │ (资源) │ │ │ └──────────┘ └──────────┘ └──────────────────┘ │ └─────────────────────────────────────────────────────┘ │ ┌──────────────────────┴──────────────────────────────┐ │ ServiceDefaults 项目 │ │ (OpenTelemetry, 服务发现, 健康检查, 弹性策略) │ └─────────────────────────────────────────────────────┘1.2 项目结构MyCloudApp/ ├── src/ │ ├── MyApp.AppHost/ # 编排器项目 │ │ ├── Program.cs │ │ └── MyApp.AppHost.csproj │ ├── MyApp.ServiceDefaults/ # 服务默认配置 │ │ ├── Extensions.cs │ │ └── MyApp.ServiceDefaults.csproj │ ├── MyApp.ApiService/ # Web API 服务 │ │ ├── Program.cs │ │ └── MyApp.ApiService.csproj │ └── MyApp.WorkerService/ # 后台任务服务 │ ├── Program.cs │ └── MyApp.WorkerService.csproj ├── tests/ └── MyApp.sln二、从零搭建 Aspire 项目2.1 创建项目# 安装 Aspire 工作负载dotnet workloadinstallaspire# 创建 Aspire 解决方案dotnet new aspire-starter-nMyCloudApp# 或者手动添加到现有项目dotnet new aspire-apphost-nMyApp.AppHost2.2 AppHost 编排器// src/MyApp.AppHost/Program.csvarbuilder=DistributedApplication.CreateBuilder(args);// 添加基础设施资源varpostgres=builder.AddPostgres("postgres").WithDataVolume().WithPgAdmin();// 开发时启用 PGAdmin 管理界面varredis=builder.AddRedis("redis").WithDataVolume();varrabbitmq=builder.AddRabbitMQ("rabbitmq").WithManagementPlugin();// 添加数据库varcatalogDb=postgres.AddDatabase("catalogdb");varorderDb=postgres.AddDatabase("orderdb");// 添加微服务varcatalogApi=builder.AddProjectProjects.MyApp_CatalogService("catalog-api").WithReference(catalogDb).WithReference(redis).WithHttpHealthCheck("/health");varorderApi=builder.AddProjectProjects.MyApp_OrderService("order-api").WithReference(orderDb).WithReference(rabbitmq).WithReference(catalogApi)// 服务间依赖.WithHttpHealthCheck("/health");varworkerService=builder.AddProjectProjects.MyApp_WorkerService("worker").WithReference(rabbitmq).WithReference(redis);builder.Build().Run();2.3 ServiceDefaults 配置// src/MyApp.ServiceDefaults/Extensions.csusingMicrosoft.Extensions.DependencyInjection;usingMicrosoft.Extensions.Diagnostics.HealthChecks;usingOpenTelemetry;usingOpenTelemetry.Metrics;usingOpenTelemetry.Resources;usingOpenTelemetry.Trace;namespaceMicrosoft.Extensions.Hosting;publicstaticclassExtensions{publicstaticIHostApplicationBuilderAddServiceDefaults(thisIHostApplicationBuilderbuilder){builder.ConfigureOpenTelemetry();builder.AddDefaultHealthChecks();returnbuilder;}privatestaticvoidConfigureOpenTelemetry(thisIHostApplicationBuilderbuilder){builder.Logging.AddOpenTelemetry(logging={logging.IncludeFormattedMessage=true;logging.IncludeScopes=true;});builder.Services.AddOpenTelemetry().ConfigureResource(resource=resource.AddService(builder.Environment.ApplicationName)).WithTracing(tracing=tracing.AddAspNetCoreInstrumentation().AddGrpcClientInstrumentation().AddHttpClientInstrumentation().AddSource("MyApp.*")).WithMetrics(metrics=metrics.AddAspNetCoreInstrumentation().AddHttpClientInstrumentation().AddRuntimeInstrumentation().AddMeter("MyApp.*"));builder.Services.AddOpenTelemetry().UseOtlpExporter();}privatestaticvoidAddDefaultHealthChecks(thisIHostApplicationBuilderbuilder){builder.Services.AddHealthChecks().AddCheck("self",()=HealthCheckResult.Healthy());}}三、微服务实现3.1 Catalog Service(商品服务)// src/MyApp.CatalogService/Program.csusingMicrosoft.EntityFrameworkCore;usingMyApp.CatalogService.Data;usingMyApp.CatalogService.Endpoints;varbuilder=WebApplication.CreateBuilder(args);// 服务注册builder.AddServiceDefaults();builder.AddNpgsqlDbContextCatalogDbContext("catalogdb");builder.Services.AddStackExchangeRedisCache(builder.Configuration.GetConnectionString("redis")!);builder.Services.AddEndpointsApiExplorer();builder.Services.AddSwaggerGen();varapp=builder.Build();// 中间件app.MapDefaultEndpoints();app.UseSwagger();app.UseSwaggerUI();// 数据库迁移using(varscope=app.Services.CreateScope()){vardb=scope.ServiceProvider.GetRequiredServiceCatalogDbContext();awaitdb.Database.MigrateAsync();}// 端点app.MapCatalogEndpoints();app.Run();// src/MyApp.CatalogService/Models/Product.csnamespaceMyApp.CatalogService.Models;publicclassProduct{publicintId{get;set;}publicstringName{get;set;}=string.Empty;publicstringDescription{get;set;}=string.Empty;publicdecimalPrice{get
NET_Aspire云原生微服务实战:从本地开发到生产部署完整指南
.NET Aspire 云原生微服务实战:从本地开发到生产部署完整指南作者:Crown_22 | 云原生 .NET 开发者 | 技术分享前言2024年微软正式发布 .NET Aspire——一个专为云原生应用设计的开发框架。它不是又一个微服务框架,而是解决了微服务开发中最让人头疼的问题:本地开发体验。传统微服务开发的痛点:本地启动10个服务,每个都要单独配置服务发现、数据库连接、消息队列的配置到处都是硬编码本地环境和生产环境的配置差异巨大Dashboard 观测性工具部署复杂.NET Aspire 的核心理念:用 C# 代码定义整个应用拓扑,一键启动本地开发环境,无缝迁移到生产。一、.NET Aspire 架构全景1.1 核心组件┌─────────────────────────────────────────────────────┐ │ .NET Aspire Dashboard │ │ (Traces, Metrics, Logs, Console) │ └──────────────────────┬──────────────────────────────┘ │ ┌──────────────────────┴──────────────────────────────┐ │ AppHost 项目 │ │ (编排器:定义所有服务和资源) │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │ │ │ Web API │ │ Worker │ │ Redis │ │ Postgres│ │ │ │ Service │ │ Service │ │ (资源) │ │ (资源) │ │ │ └──────────┘ └──────────┘ └──────────────────┘ │ └─────────────────────────────────────────────────────┘ │ ┌──────────────────────┴──────────────────────────────┐ │ ServiceDefaults 项目 │ │ (OpenTelemetry, 服务发现, 健康检查, 弹性策略) │ └─────────────────────────────────────────────────────┘1.2 项目结构MyCloudApp/ ├── src/ │ ├── MyApp.AppHost/ # 编排器项目 │ │ ├── Program.cs │ │ └── MyApp.AppHost.csproj │ ├── MyApp.ServiceDefaults/ # 服务默认配置 │ │ ├── Extensions.cs │ │ └── MyApp.ServiceDefaults.csproj │ ├── MyApp.ApiService/ # Web API 服务 │ │ ├── Program.cs │ │ └── MyApp.ApiService.csproj │ └── MyApp.WorkerService/ # 后台任务服务 │ ├── Program.cs │ └── MyApp.WorkerService.csproj ├── tests/ └── MyApp.sln二、从零搭建 Aspire 项目2.1 创建项目# 安装 Aspire 工作负载dotnet workloadinstallaspire# 创建 Aspire 解决方案dotnet new aspire-starter-nMyCloudApp# 或者手动添加到现有项目dotnet new aspire-apphost-nMyApp.AppHost2.2 AppHost 编排器// src/MyApp.AppHost/Program.csvarbuilder=DistributedApplication.CreateBuilder(args);// 添加基础设施资源varpostgres=builder.AddPostgres("postgres").WithDataVolume().WithPgAdmin();// 开发时启用 PGAdmin 管理界面varredis=builder.AddRedis("redis").WithDataVolume();varrabbitmq=builder.AddRabbitMQ("rabbitmq").WithManagementPlugin();// 添加数据库varcatalogDb=postgres.AddDatabase("catalogdb");varorderDb=postgres.AddDatabase("orderdb");// 添加微服务varcatalogApi=builder.AddProjectProjects.MyApp_CatalogService("catalog-api").WithReference(catalogDb).WithReference(redis).WithHttpHealthCheck("/health");varorderApi=builder.AddProjectProjects.MyApp_OrderService("order-api").WithReference(orderDb).WithReference(rabbitmq).WithReference(catalogApi)// 服务间依赖.WithHttpHealthCheck("/health");varworkerService=builder.AddProjectProjects.MyApp_WorkerService("worker").WithReference(rabbitmq).WithReference(redis);builder.Build().Run();2.3 ServiceDefaults 配置// src/MyApp.ServiceDefaults/Extensions.csusingMicrosoft.Extensions.DependencyInjection;usingMicrosoft.Extensions.Diagnostics.HealthChecks;usingOpenTelemetry;usingOpenTelemetry.Metrics;usingOpenTelemetry.Resources;usingOpenTelemetry.Trace;namespaceMicrosoft.Extensions.Hosting;publicstaticclassExtensions{publicstaticIHostApplicationBuilderAddServiceDefaults(thisIHostApplicationBuilderbuilder){builder.ConfigureOpenTelemetry();builder.AddDefaultHealthChecks();returnbuilder;}privatestaticvoidConfigureOpenTelemetry(thisIHostApplicationBuilderbuilder){builder.Logging.AddOpenTelemetry(logging={logging.IncludeFormattedMessage=true;logging.IncludeScopes=true;});builder.Services.AddOpenTelemetry().ConfigureResource(resource=resource.AddService(builder.Environment.ApplicationName)).WithTracing(tracing=tracing.AddAspNetCoreInstrumentation().AddGrpcClientInstrumentation().AddHttpClientInstrumentation().AddSource("MyApp.*")).WithMetrics(metrics=metrics.AddAspNetCoreInstrumentation().AddHttpClientInstrumentation().AddRuntimeInstrumentation().AddMeter("MyApp.*"));builder.Services.AddOpenTelemetry().UseOtlpExporter();}privatestaticvoidAddDefaultHealthChecks(thisIHostApplicationBuilderbuilder){builder.Services.AddHealthChecks().AddCheck("self",()=HealthCheckResult.Healthy());}}三、微服务实现3.1 Catalog Service(商品服务)// src/MyApp.CatalogService/Program.csusingMicrosoft.EntityFrameworkCore;usingMyApp.CatalogService.Data;usingMyApp.CatalogService.Endpoints;varbuilder=WebApplication.CreateBuilder(args);// 服务注册builder.AddServiceDefaults();builder.AddNpgsqlDbContextCatalogDbContext("catalogdb");builder.Services.AddStackExchangeRedisCache(builder.Configuration.GetConnectionString("redis")!);builder.Services.AddEndpointsApiExplorer();builder.Services.AddSwaggerGen();varapp=builder.Build();// 中间件app.MapDefaultEndpoints();app.UseSwagger();app.UseSwaggerUI();// 数据库迁移using(varscope=app.Services.CreateScope()){vardb=scope.ServiceProvider.GetRequiredServiceCatalogDbContext();awaitdb.Database.MigrateAsync();}// 端点app.MapCatalogEndpoints();app.Run();// src/MyApp.CatalogService/Models/Product.csnamespaceMyApp.CatalogService.Models;publicclassProduct{publicintId{get;set;}publicstringName{get;set;}=string.Empty;publicstringDescription{get;set;}=string.Empty;publicdecimalPrice{get