.Net Aspire初体验

今天参加了Post Microsoft Build & AI Day深圳的集会,众多大佬分享了非常优质前沿的技术和实践,实在受益良多,为了消化吸收关于张队分享的.Net Aspire的内容,特实操一遍小示例并记录如下:

1、以VS2022为例,先升级到最新的版本v17.10.3,新建.NET Aspire Starter应用程序项目,选择文件夹及Redis勾选和勾选生成Tests(HTTPS不能去除勾选)。

 生成的文件夹结构如下:

 可以看到按模板生成了一个ApiService(这里是以前的天气广播内容);一个Web项目;一个AppHost( 一个Host,把ApiService及Web前端给引用进来);一个ServiceDefaults扩展类;一个Tests测试项目。

AspireApp1.ApiService项目的主要内容:

Program.cs:

 1 var builder = WebApplication.CreateBuilder(args);
 2 
 3 // Add service defaults & Aspire components.
 4 builder.AddServiceDefaults();
 5 
 6 // Add services to the container.
 7 builder.Services.AddProblemDetails();
 8 
 9 var app = builder.Build();
10 
11 // Configure the HTTP request pipeline.
12 app.UseExceptionHandler();
13 
14 var summaries = new[]
15 {
16     "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
17 };
18 
19 app.MapGet("/weatherforecast", () =>
20 {
21     var forecast = Enumerable.Range(1, 5).Select(index =>
22         new WeatherForecast
23         (
24             DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
25             Random.Shared.Next(-20, 55),
26             summaries[Random.Shared.Next(summaries.Length)]
27         ))
28         .ToArray();
29     return forecast;
30 });
31 
32 app.MapDefaultEndpoints();
33 
34 app.Run();
35 
36 record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
37 {
38     public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
39 }
View Code

AspireApp1.Web项目的主要内容:

Program.cs:

 1 using AspireApp1.Web;
 2 using AspireApp1.Web.Components;
 3 
 4 var builder = WebApplication.CreateBuilder(args);
 5 
 6 // Add service defaults & Aspire components.
 7 builder.AddServiceDefaults();
 8 
 9 // Add services to the container.
10 builder.Services.AddRazorComponents()
11     .AddInteractiveServerComponents();
12 
13 builder.Services.AddOutputCache();
14 
15 builder.Services.AddHttpClient<WeatherApiClient>(client => client.BaseAddress = new("http://apiservice"));
16 
17 var app = builder.Build();
18 
19 if (!app.Environment.IsDevelopment())
20 {
21     app.UseExceptionHandler("/Error", createScopeForErrors: true);
22 }
23 
24 app.UseStaticFiles();
25 app.UseAntiforgery();
26 
27 app.UseOutputCache();
28 
29 app.MapRazorComponents<App>()
30     .AddInteractiveServerRenderMode();
31 
32 app.MapDefaultEndpoints();
33 
34 app.Run();
View Code

WeatherApiClient.cs:

 1 namespace AspireApp1.Web;
 2 
 3 public class WeatherApiClient(HttpClient httpClient)
 4 {
 5     public async Task<WeatherForecast[]> GetWeatherAsync(int maxItems = 10, CancellationToken cancellationToken = default)
 6     {
 7         List<WeatherForecast>? forecasts = null;
 8 
 9         await foreach (var forecast in httpClient.GetFromJsonAsAsyncEnumerable<WeatherForecast>("/weatherforecast", cancellationToken))
10         {
11             if (forecasts?.Count >= maxItems)
12             {
13                 break;
14             }
15             if (forecast is not null)
16             {
17                 forecasts ??= [];
18                 forecasts.Add(forecast);
19             }
20         }
21 
22         return forecasts?.ToArray() ?? [];
23     }
24 }
25 
26 public record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
27 {
28     public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
29 }
View Code

AspireApp1.ServiceDefaults项目的主要内容:

Extensions.cs:

  1 using Microsoft.AspNetCore.Builder;
  2 using Microsoft.AspNetCore.Diagnostics.HealthChecks;
  3 using Microsoft.Extensions.DependencyInjection;
  4 using Microsoft.Extensions.Diagnostics.HealthChecks;
  5 using Microsoft.Extensions.Logging;
  6 using OpenTelemetry;
  7 using OpenTelemetry.Metrics;
  8 using OpenTelemetry.Trace;
  9 
 10 namespace Microsoft.Extensions.Hosting;
 11 
 12 // Adds common .NET Aspire services: service discovery, resilience, health checks, and OpenTelemetry.
 13 // This project should be referenced by each service project in your solution.
 14 // To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults
 15 public static class Extensions
 16 {
 17     public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBuilder builder)
 18     {
 19         builder.ConfigureOpenTelemetry();
 20 
 21         builder.AddDefaultHealthChecks();
 22 
 23         builder.Services.AddServiceDiscovery();
 24 
 25         builder.Services.ConfigureHttpClientDefaults(http =>
 26         {
 27             // Turn on resilience by default
 28             http.AddStandardResilienceHandler();
 29 
 30             // Turn on service discovery by default
 31             http.AddServiceDiscovery();
 32         });
 33 
 34         return builder;
 35     }
 36 
 37     public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicationBuilder builder)
 38     {
 39         builder.Logging.AddOpenTelemetry(logging =>
 40         {
 41             logging.IncludeFormattedMessage = true;
 42             logging.IncludeScopes = true;
 43         });
 44 
 45         builder.Services.AddOpenTelemetry()
 46             .WithMetrics(metrics =>
 47             {
 48                 metrics.AddAspNetCoreInstrumentation()
 49                     .AddHttpClientInstrumentation()
 50                     .AddRuntimeInstrumentation();
 51             })
 52             .WithTracing(tracing =>
 53             {
 54                 tracing.AddAspNetCoreInstrumentation()
 55                     // Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package)
 56                     //.AddGrpcClientInstrumentation()
 57                     .AddHttpClientInstrumentation();
 58             });
 59 
 60         builder.AddOpenTelemetryExporters();
 61 
 62         return builder;
 63     }
 64 
 65     private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostApplicationBuilder builder)
 66     {
 67         var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);
 68 
 69         if (useOtlpExporter)
 70         {
 71             builder.Services.AddOpenTelemetry().UseOtlpExporter();
 72         }
 73 
 74         // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package)
 75         //if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"]))
 76         //{
 77         //    builder.Services.AddOpenTelemetry()
 78         //       .UseAzureMonitor();
 79         //}
 80 
 81         return builder;
 82     }
 83 
 84     public static IHostApplicationBuilder AddDefaultHealthChecks(this IHostApplicationBuilder builder)
 85     {
 86         builder.Services.AddHealthChecks()
 87             // Add a default liveness check to ensure app is responsive
 88             .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]);
 89 
 90         return builder;
 91     }
 92 
 93     public static WebApplication MapDefaultEndpoints(this WebApplication app)
 94     {
 95         // Adding health checks endpoints to applications in non-development environments has security implications.
 96         // See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments.
 97         if (app.Environment.IsDevelopment())
 98         {
 99             // All health checks must pass for app to be considered ready to accept traffic after starting
100             app.MapHealthChecks("/health");
101 
102             // Only health checks tagged with the "live" tag must pass for app to be considered alive
103             app.MapHealthChecks("/alive", new HealthCheckOptions
104             {
105                 Predicate = r => r.Tags.Contains("live")
106             });
107         }
108 
109         return app;
110     }
111 }
View Code

AspireApp1.AppHost项目的主要内容:

Program.cs:

1 var builder = DistributedApplication.CreateBuilder(args);
2 
3 var apiService = builder.AddProject<Projects.AspireApp1_ApiService>("apiservice");
4 
5 builder.AddProject<Projects.AspireApp1_Web>("webfrontend")
6     .WithExternalHttpEndpoints()
7     .WithReference(apiService);
8 
9 builder.Build().Run();
View Code

还有一个Tests测试项目的内容:

WebTests.cs:

 1 using System.Net;
 2 
 3 namespace AspireApp1.Tests;
 4 
 5 public class WebTests
 6 {
 7     [Fact]
 8     public async Task GetWebResourceRootReturnsOkStatusCode()
 9     {
10         // Arrange
11         var appHost = await DistributedApplicationTestingBuilder.CreateAsync<Projects.AspireApp1_AppHost>();
12         await using var app = await appHost.BuildAsync();
13         await app.StartAsync();
14 
15         // Act
16         var httpClient = app.CreateHttpClient("webfrontend");
17         var response = await httpClient.GetAsync("/");
18 
19         // Assert
20         Assert.Equal(HttpStatusCode.OK, response.StatusCode);
21     }
22 }
View Code

2、我们将AspireApp1.AppHost项目设为启动项目,按F5运行。项目即启动生成和运行,可在输出窗口看到对应信息。等程序运行起来会自动打开一个浏览器概览仪表盘窗口如下:

 点击对应项目的终结点,即可看到对应的内容:

AspireApp1.ApiService:

 AspireApp1.Web:

 在仪表盘可以看到资源、控制台、结构化、跟踪、指标几个栏目,没有写一句代码,你的可观测、生产就绪的云原生分布式应用程序就搭建完成了。

以下是一些图示:

 

 

 

 另外多嘴一句,并不是用了Aspire就一定要上云,我突然有个主意,边缘运算、单体程序照样也可以用Aspire。另外Aspire和Dapr应该是有益的补充,而不是替代关系。

posted @ 2024-06-23 01:25  Bruce_Cheung  阅读(19)  评论(0编辑  收藏  举报