只有累積,沒有奇蹟

2024年3月24日 星期日

[NET] .NET Aspire From 0 To 1 : 可觀測性儀錶板

前言
上一篇與大家介紹關於 .NET Asipre 方案基本組成與概念,今天要介紹自己覺得 .NET Aspire 框架中重要的儀表板功能,此系列文目前會分為三篇分別是
  • .NET Aspire 快速入門 : 介紹 .NET Aspirea 的基礎知識,包括其設計理念、主要功能
  • .NET Aspire 可觀測性儀錶板 : 解釋 .NET Aspire 如何提供應用程式的可觀測性,包括如何蒐集遙測數據、監控和日誌記錄
  • .NET Aspire 整合 : 說明 .NET Aspire 如何與其他微軟雲服務(如 Azure)整合
希望可以這系列文的介紹與分享,讓有興趣的開發夥伴們可以更進一步了解對 .NET Aspire 框架的認識,與若對於上述內容有問題或是不清楚的地方,歡迎提出來一起討論。


探索 .NET Aspire 儀錶板
在 Visual Studio 執行後即可以看到 .NET Aspire Dashboard,Dashboard 是透過 Blazor 所撰寫的應用程式,開發者可以在 .NET Aspire 儀表板頁面顯示正在執行的應用程式,透過此儀表板可以看到應用程式的各項資訊,包括日誌 (Logs)、遙測數據、Metrics 和環境配置等,提供開發者對於應用程式的狀態更進一步的理解與掌握即時資訊。
Resource
如同前一篇介紹的在專案 Apphost 中所設定 Redis、apiService 跟 webFrontend 名字,三個各自型別 (Type) 也有顯示在畫面的欄位上,其中 Redis 是透過 docker 啟動,開啟 Docker Desktop Dashboard 可以看到對應到相同的資料與狀態,分別是 in use 跟 Running,還有應用程式啟動時間與位置資訊
Logs
點擊 Logs 欄位可以顯示應用程式的 log 資訊,以 webfrondend 為例會看到 Log 內容是應用程式相關的 console logs 資訊,還包含 log 的 level 與 message 與 log 紀錄的時間。另外 Watch logs 旁邊還可以進行專案的切換,可以更方便的看到不同 instance 的 console log 重要資訊
Detail
在 .NET Aspire 會自動解析應用程式相關的 endpoint 與環境變數等 config 設定資訊,在將其資訊呈現在 Dashboard 頁面,像是跟第三方串接時串接的 endpoint url、feature toogle 的開關 flag 設定,預設這些內容是隱藏看不到的,需要在點擊右方的 icon 才看到的。這邊 OTEL 開頭的到專案中 config 檔可以發現找不到這些相關的設定參數,OTEL 是與 opentelemetry 相關的設定資訊,這些資訊說明晚點會在後面介紹。

執行應用程式
前面介紹了 Resources 與 Console 之後,下面三個重要功能 Structure、Traces 與 Metrics 是可觀測性常提到的三大支柱,在 .NET Aspire 之所以可以輕鬆地蒐集這些資訊,主要是因為在 .NET 8 底層及組件實做 OpenTelemetry 的標準並提供相關的 API,並在應用程式啟動時設定蒐集相關遙測數據資料,才可以在 .NET Aspire dashboard 中讓開發者看到相關的資訊。因此在介紹 Structure、Traces 與 Metrics 前要先透過執行應用程式 API 與功能,才可以蒐集相關遙測數據在 Dashboard 上觀察到應用程式執行的狀況。
透過 Resources 中 webfrontend 點擊 endpoint 可以看到上面畫面,這是一個簡單的畫面資訊是透過後端載入回傳當天天氣相關的資料。我們可以重新整理畫面試著多呼叫 API,測試中可以發現有設定 cache 功能將資料 cache 10 秒後失效。接著我們回到功能看各數據收集的狀況。

Structured logs
Structured logs 功能可以查看應用程式所記錄的 Log 資訊,像是 log 的來源 (Resources)、Log Level 等級、發生的時間與 log 內容等重要資訊,以下是該功能中自己覺得實用的功能
  • 專案切換 : 進行 resources 的切換,當應用程式變多時這個功能就很需要
  • 搜尋 : 針對 log message 內容進行搜尋的動作,方便快速過濾不必要的資訊
  • 篩選 : 提供 log level 的篩選
  • Trace : 切換畫面到 Trace,找到此 request 相關的請求 log
  • Log entry detail : log 詳細內容,將所有 log 相關的 attribute 資訊全部呈現在畫面上
Traces
Traces 可以看到該請求所相依的服務或是依賴 service,當服務越切越細時當異常發生我們要找到其中哪個服務異常就會是一個重要的議題,舉例來說我們今天系統中有個登入 login 服務,系統背後可能經過自己系統的服務、第三方 SSO 像是 github、微軟 AAD 等第三方系統整合,最後再將資訊整合到既有系統的 Auth 服務,在將整體登入的結果回到前端的 Client Device,即使各應用程式有紀錄 log 但彼此這些 log 是沒有關聯,在盤查問題實就會花費很多時間,Trace 就是透過 TraceID 來將各自的 log 資訊串聯再一起,讓開發者可以透過類似上列畫面更快的定位可能發生異常的服務。
上圖是 sample 專案中 /weather 請求作為範例進行簡單介紹
  • 請求資訊 : 該請求資訊所發生的時間、花費多久 (Duration)、經過多少服務 (Resource) 及經過多少個 Span
  • /weather API 的請求路徑,可以看到在此請求路徑(Span)中的 endpoint 及 httpmethod 方法
  • 呈現這個請求中每個 Span 的細節,各自占比以及所花費的時間,並以 Dashboard 方式呈現讓大家更容易理解可能的瓶頸點
  • Detail : 分別記錄 Span、Application 與 Event 三個資訊的 log 資訊,Span 是紀錄請求類的相關資訊像是 spanID、http method、使用的 Url 及 port。Application 類則是該 service 相關資訊,像是服務名稱、instance ID (多個服務時方便識別)、收集遙測數據實使用的 sdk Name / language 及版本,如下圖所示。
Metrics
Metrics 可以做為監控應用程式即時的狀況,在 .NET Aspire 中會依據應用程式列出需要的 metrics 指標內容
以上圖為例是查看 http.client.request.durtion 的資訊,詳細資訊可以參考 MSDN ASP.NET Core metrics 了解更多資訊。

Dashboard 實作原理
.NET Aspire 提供可視化的 dashboard,讓開發人員在開發雲端應用程式時變得更簡單,那麼在背後是如何做到的呢 ? 我們可以看到 AspireSampleApp.ApiService 與 AspireSampleApp.Web 兩個專案中的啟動呼叫 builder.AddServiceDefaults(); 方法,在程式一開始透過 ConfigureOpenTelemetry 來蒐集應用程式相關的遙測數據資料,其內容如下
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()
                   .AddProcessInstrumentation()
                   .AddRuntimeInstrumentation();
        })
        .WithTracing(tracing =>
        {
            if (builder.Environment.IsDevelopment())
            {
                // We want to view all traces in development
                tracing.SetSampler(new AlwaysOnSampler());
            }

            tracing.AddAspNetCoreInstrumentation()
                   .AddGrpcClientInstrumentation()
                   .AddHttpClientInstrumentation();
        });

    builder.AddOpenTelemetryExporters();

    return builder;
}  
  

OpenTelemetry 是什麼
由於在 Dashboard 分為三塊分別是 Logging、Traces 與 Metrics 三者,因此在上述程式碼的說明我分為三部分來說明,首先先來看一下程式方法中提到的 OpenTelemetry。如果提到可觀測性 Observability 一定會討論到 OpenTelemetry 這套收集遙測數據的標準,OpenTelemetry 是什麼呢
OpenTelemetry 提供單一的開放原始碼標準和技術組合,可從雲端原生應用程式和基礎架構中擷取及匯出指標、追蹤記錄和記錄檔。
目的是為了解決雲端原生應用程式在分散式系統中收集應用程式的遙測據據資料問題,像是指標和追蹤在過去有不同種蒐集方式,透過 OpenTelemetry 統一的標準,可以簡化及更有效地蒐集需要的資料內容,彙整到相關的技術廠商或是 open Source 專案,各程式語言可以透過 OpenTelemetry 所定義的標準將其其程式語言的方法實做出來,提供給各自的開發者使用,如果想要了解更多關於 OpenTelemetry 介紹,可以參考我之前在 Will 保哥粉絲團直播分享的 初探 OpenTelemetry 工具組:蒐集遙測數據的新標準,這裡就不在多加介紹


Logging
程式碼中 3~7 行,設定 logging 相關資訊主要執行程式碼如下
builder.Logging.AddOpenTelemetry(logging =>
{
    logging.IncludeFormattedMessage = true;
    logging.IncludeScopes = true;
});  
在 logging 中主要透過程式碼中的 Logging.AddOpenTelemetry 往下查看是使用底層 AddOpenTelemetryInternal 方法,透過 Visual Studio IDE 查看定義 sourece code 來探討背後做了哪些事情
private static ILoggingBuilder AddOpenTelemetryInternal(
	ILoggingBuilder builder,
	Action? configureBuilder,
	Action? configureOptions)
{
	Guard.ThrowIfNull(builder);

	builder.AddConfiguration();

	var services = builder.Services;

	// Note: This will bind logger options element (eg "Logging:OpenTelemetry") to OpenTelemetryLoggerOptions
	RegisterLoggerProviderOptions(services);

	services.AddOpenTelemetrySharedProviderBuilderServices();

	if (configureOptions != null)
	{
		// Note: Order is important here so that user-supplied delegate
		// fires AFTER the options are bound to Logging:OpenTelemetry
		// configuration.
		services.Configure(configureOptions);
	}

	var loggingBuilder = new LoggerProviderBuilderBase(services).ConfigureBuilder(
		(sp, logging) =>
		{
			var options = sp.GetRequiredService>().CurrentValue;

			if (options.ResourceBuilder != null)
			{
				logging.SetResourceBuilder(options.ResourceBuilder);

				options.ResourceBuilder = null;
			}

			foreach (var processorFactory in options.ProcessorFactories)
			{
				logging.AddProcessor(processorFactory);
			}

			options.ProcessorFactories.Clear();
		});

	configureBuilder?.Invoke(loggingBuilder);

	services.TryAddEnumerable(
		ServiceDescriptor.Singleton(
			sp => new OpenTelemetryLoggerProvider(
				sp.GetRequiredService(),
				sp.GetRequiredService>().CurrentValue,
				disposeProvider: false)));

	return builder;

#if NET6_0_OR_GREATER
	[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "OpenTelemetryLoggerOptions contains only primitive properties.")]
	[UnconditionalSuppressMessage("AOT", "IL3050", Justification = "OpenTelemetryLoggerOptions contains only primitive properties.")]
#endif
	static void RegisterLoggerProviderOptions(IServiceCollection services)
	{
		LoggerProviderOptions.RegisterProviderOptions(services);
	}
}    
上述程式碼重點是在 .NET 程式中與 OpenTelemetry 進行配置日誌紀錄,通過 services.AddOpenTelemetrySharedProviderBuilderServices() 向 DI 加上 OpenTelemetry 相關服務,並在 RegisterLoggerProviderOptions(services) 綁定 OpenTelemetryLoggerOptions,使用 Singleton 方式新增到 service 中 (IServiceCollection)。

Metrics
程式碼 9~16 行,是用來設定 Metrics 相關資訊主要執行程式碼如下
builder.Services.AddOpenTelemetry()
	.WithMetrics(metrics =>
	{
		metrics.AddAspNetCoreInstrumentation()
			   .AddHttpClientInstrumentation()
			   .AddProcessInstrumentation()
			   .AddRuntimeInstrumentation();
	})
AddOpenTelemetry() 是 IServiceCollection 的擴充方法,在這擴充方法中允許在 IServiceCollection 的 instance 直接使用,並在 service 中新增 TelemetryHostedService 的服務 (DI Lifecycle 同樣為 Singleton),最後方法回傳 OpenTelemetryBuilder 實例。並調用其 WithMetrics 方法,我們來看看該方法做了哪些事情
/// 
/// Adds metric services into the builder.
/// 
/// 
/// Notes:
/// 
/// This is safe to be called multiple times and by library authors.
/// Only a single  will be created for a given
/// .
/// This method automatically registers an  named 'OpenTelemetry' into the .
/// 
/// 
/// The supplied  for chaining
/// calls.
public OpenTelemetryBuilder WithMetrics()
    => this.WithMetrics(b => { });

/// 
/// Adds metric services into the builder.
/// 
/// 
/// 
/// configuration callback.
/// The supplied  for chaining
/// calls.
public OpenTelemetryBuilder WithMetrics(Action configure)
{
    OpenTelemetryMetricsBuilderExtensions.RegisterMetricsListener(
        this.Services,
        configure);

    return this;
}
透過上述代碼我們可以得知下列資訊
  • WithMetrics 是 OpenTelemetryBuilder 的擴充方法,用於在 .NET 應用程式中加上 Metrics 中的標準服務 (Builder)
  • 方法一的 WithMetrics() 沒有參數,第二個 WithMetrics(Action configure) 接收一個參數 configure,用於將 Metrics 度量標準服務添加到建構器中
  • 內部調用 RegisterMetricsListener 來執行實際的註冊跟配置

接者在回到 sample 專案程式碼中可以看到 WithMetrics 有四個設定值分別是 AddAspNetCoreInstrumentationAddHttpClientInstrumentationAddProcessInstrumentationAddRuntimeInstrumentation,透過上述程式碼可以蒐集應用程式的內建指標包括 Process、Memory、GC、HttpClient 與伺服器相關指標等資訊。提供開發者可以使用簡單幾行程式簡化啟用所有內建重要指標的過程,因此可以呼應上面在介紹 .NET Aspire Dashboard 時為何可以蒐集到這麼多與應用程式相關的 metrics 資訊,就是在這段程式碼所設定的才可以在 dashboard 看到。 如果對於 ASP.NTE Core Metrics 有興趣探索可以參考 built in metrics aspnetcore

Traces
程式碼 17~28 行,是用來設定 Traces 相關資訊主要執行程式碼如下
.WithTracing(tracing =>
{
    if (builder.Environment.IsDevelopment())
    {
        // We want to view all traces in development
        tracing.SetSampler(new AlwaysOnSampler());
    }

    tracing.AddAspNetCoreInstrumentation()
           .AddGrpcClientInstrumentation()
           .AddHttpClientInstrumentation();
});
並調用其 WithTracing 方法,我們來看看該方法做了哪些事情
/// 
/// Adds tracing services into the builder.
/// 
/// 
/// Note: This is safe to be called multiple times and by library authors.
/// Only a single  will be created for a given
/// .
/// 
/// The supplied  for chaining
/// calls.
public OpenTelemetryBuilder WithTracing()
    => this.WithTracing(b => { });

/// 
/// Adds tracing services into the builder.
/// 
/// 
/// 
/// configuration callback.
/// The supplied  for chaining
/// calls.
public OpenTelemetryBuilder WithTracing(Action configure)
{
    Guard.ThrowIfNull(configure);

    var builder = new TracerProviderBuilderBase(this.Services);

    configure(builder);

    return this;
}
  
與 withMetrics 相似,兩者都是 OpenTelemetryBuilder 的公開擴充方法。並且都是一個方法沒參數,第二個方法提供 config 設定參數,在 withTraces 中會給特定的 IServiceCollection 建立 TracerProvider。

在回到前面程式碼,預設會希望在開發環境自動將 trace 資料蒐集起來,方便開發者在測試環境時可以捕捉相關資訊,因此有類似判斷當符合條件時將採集器設定為 AlwaysOnSampler
  • AddAspNetCoreInstrumentation : 加上與 ASP.NET Core 相關的 tracing 資訊,這有助於開發者追蹤 Web 應用程式的請求和回應。
  • AddGrpcClientInstrumentation : 加上 gRPC 使 gRPC 呼叫可以被追蹤。
  • AddHttpClientInstrumentation : 加上 HttpClient 相關的 tracing 資訊,它有助於追蹤 HTTP 客戶端請求。

Exporters
上面提到很多蒐集遙測數據資料的設定與方法,最後這些資料會蒐集到何處呢 ? 在程式後面第 30 行透過 builder.AddOpenTelemetryExporters() 方法設定 OpenTelemetry 加上一種或是多種的導出器(Exporter),設定完後將蒐集到的各項資訊透過 config 設定發送到各個後端系統,例如 Azure Monitor、 Prometheus、Jaeger、Zipkin、Elasticsearch、Grafana 等。從 source code 可以看到蒐集位置是透過 config 的 OTEL_EXPORTER_OTLP_ENDPOINT,我們可以在透過 dashboard 中的 resource 查看此 config 設定位置為何。
補充 : 如果有需要調整可以過 config 中的設定來修改。

小結
以上快速介紹了關於 .NET Aspire 提供好用的框架 dashboard,除了功能介紹之外也一起探索了其背後關於可觀測性三支柱 logging、Tracing、Metrics 在 .NET Aspire 的實現背後原理與程式碼說明。相信透過今天的文章各位夥伴對於 .NET Aspire dashboard 有更進一步的理解,身為開發者的我們在了解後相信也可以在自己所開發的應用程式中加上相關好用的功能,但 .NET Aspire 與 .NET 8 對於 Cloud Native 所提供的強大功能不僅於如此,在下篇文章我們將再繼續介紹其他好玩的功能與講解背後程式碼原理,happy Coding !

參考
.NET Aspire documentation (Preview)

2024年3月4日 星期一

[NET] .NET Aspire From 0 To 1 : 快速入門

前言
.NET Conf 一直是微軟對開發者展示火力的重要來源之一,在今年 .NET Conf 2023 上 .NET 平台團隊的 PM Glenn condrin 與 David Flower 介紹新一代雲原生框架 .NET Asipre。在議程中 David Fowler Demo 使用 .NET Aspire 專案,讓開發者可以輕鬆開始使用 .NET Aspire 框架提升開發者的體驗,並增進生產力,並帶來所有雲原生可以帶來的遙測數據、可觀察性、可擴展性和靈活性。另外讓我覺得興奮的是 .NET 8 與 OpenTelemetry 的深度整合,對於開發者在實踐可觀測性上更是方便許多。
自己在研究 Aspire 接著會預計整理成相關系列文章,這篇是研究 .NET Aspire 的系列文第一篇,這系列主要會分為幾篇分別是
  • .NET Aspire 快速入門 : 介紹 .NET Aspire 的基礎知識,包括其設計理念、主要功能
  • .NET Aspire 中的可觀測性 : 解釋 .NET Aspire 如何提供應用程式的可觀測性,包括如何蒐集遙測數據、監控和日誌記錄
  • .NET Aspire 整合 : 說明 .NET Aspire 如何與其他微軟雲服務(如 Azure)整合
希望可以透過各種官網的文件與說明,加上自己的理解針對新一代 .NET Aspire 雲原生框架進行說明,讓有興趣的開發夥伴們可以更進一步了解對開發者帶來的好處與效益,與若對於上述內容有問題或是不清楚的地方,歡迎提出來一起討論。


背景
近幾年互聯網發展的速度可以說是有增無減,新的科技名詞層出不窮,當中 ABCDE 可以代表新興科技發展的五個重要方向,分別是指 AI(人工智慧)、Big data(大數據)、Cloud(雲端)、Device(裝置)、Ecosystem(生態)。隨著雲端技術的成熟,將應用程式上雲一直是這幾年熱度不減的議題之一,身為開發者的我們,除了要了解或熟悉雲端上的服務 Service、將應用程式服務容器化,在 Cloud Native 雲原生概念出現後,開發者要將傳統的應用程式搬遷到雲端上也是一個不容忽視的挑戰,在 CNCF 雲原生基金會所提供各式各樣的 Gituhb 專案中,開發者可以找到許多用於建購或部署雲原生服務的工具或組件。這些組件或工具包含了架構、容器化、持續集成或是持續部署(CI/CD),在到上線後的監控與日誌等方面,身為開發者的我們,還須了解它們各自的優勢與限制將其組合起來,應用在開發專案中,有沒有一個框架可以協助開發者快速進度入雲原生應用 ? 讓開發者可以更專注的在開發應用程式的服務呢 ?

.NET Aspire 簡介
在官方 github 介紹如下
.NET Aspire is an opinionated, cloud ready stack for building observable, production ready, distributed applications. .NET Aspire is delivered through a collection of NuGet packages that handle specific cloud-native concerns. Cloud-native apps often consist of small, interconnected pieces or microservices rather than a single, monolithic code base. Cloud-native apps generally consume a large number of services, such as databases, messaging, and caching.
簡單翻譯可以理解為
  • .NET Aspire 是微軟為雲原生應用開發推出的一個新框架,它在 .NET 8 中提供了許多重要功能,專注於提升開發效率和簡化雲原生應用的開發過程。它包含了與雲端服務更緊密的整合,以及支持容器化、微服務架構和分散式系統管理的先進工具和實踐。
.NET Aspire 的目標是簡化複雜的雲原生應用開發,同時提供強大的功能,如改進的遙測數據、可觀察性、可擴展性和靈活性。這對於追蹤和管理分散式系統至關重要,尤其是在面對快速發展的雲端環境時。

準備條件
了解完 .NET Asipre 想要解決的問題與目的後,接著我們來看如果想要使用 .NET Aspire 的前置作業是甚麼,在使用前需安装以下軟體:
  • .NET 8.0
  • .NET Aspire workload
  • Use the Visual Studio installer
  • Use the dotnet workload install aspire command
  • Docker Desktop
  • Integrated Developer Environment (IDE) or code editor, such as:
  • Visual Studio 2022 Preview version 17.9 or higher (Optional)
  • Visual Studio Code (Optional)
透過 cli 安裝 .NET Aspire workload 示意圖
Use the Visual Studio installer,記得確認右方要有 .NET Aspire SDK (Preview)
建立專案
在本機安裝完上述的軟體後,接著我們透過 Visual Studio IDE 來建立 Aspire 應用程式模板方案,在 Visual Studio 2022 17.9 版本有提供 .NET Aspire 專案模板,可以提供開發者設定出初始組態設定作業。

Step 1 : 開啟 Visual Studio 並建立新專案,右方對話框中搜尋 Aspire 後選擇或者是在 project type 直接選擇 .NET Aspire,點選 .NET Aspire Starter Application,點選下一步
Step 2 : 專案名稱輸入 AspireSampleApp,其餘設定保持預設值,點選下一步
Step 3 : Framework 選擇 .NET 8(Long term support),接著按下建立按鈕
創建完畢之後,我們來看一下方案中預設有哪些專案跟內容
└───📂 AspireSample
     ├───📂 AspireSample.ApiService
     │    ├───📂 Properties
     │    │    └─── launchSettings.json
     │    ├─── appsettings.Development.json
     │    ├─── appsettings.json
     │    ├─── AspireSample.ApiService.csproj
     │    └─── Program.cs
     ├───📂 AspireSample.AppHost
     │    ├───📂 Properties
     │    │    └─── launchSettings.json
     │    ├─── appsettings.Development.json
     │    ├─── appsettings.json
     │    ├─── AspireSample.AppHost.csproj
     │    └─── Program.cs
     ├───📂 AspireSample.ServiceDefaults
     │    ├─── AspireSample.ServiceDefaults.csproj
     │    └─── Extensions.cs
     ├───📂 AspireSample.Web
     │    ├───📂 Components
     │    │    ├───📂 Layout
     │    │    │    ├─── MainLayout.razor
     │    │    │    ├─── MainLayout.razor.css
     │    │    │    ├─── NavMenu.razor
     │    │    │    └─── NavMenu.razor.css
     │    │    ├───📂 Pages
     │    │    │    ├─── Counter.razor
     │    │    │    ├─── Error.razor
     │    │    │    ├─── Home.razor
     │    │    │    └─── Weather.razor
     │    │    ├─── _Imports.razor
     │    │    ├─── App.razor
     │    │    └─── Routes.razor
     │    ├───📂 Properties
     │    │    └─── launchSettings.json
     │    ├───📂 wwwroot
     │    │    ├───📂 bootstrap
     │    │    │    ├─── bootstrap.min.css
     │    │    │    └─── bootstrap.min.css.map
     │    │    ├─── app.css
     │    │    └─── favicon.png
     │    ├─── appsettings.Development.json
     │    ├─── appsettings.json
     │    ├─── AspireSample.Web.csproj
     │    ├─── Program.cs
     │    └─── WeatherApiClient.cs
     └─── AspireSample.sln
稍早建立的方案 AspireSampleApp 其中有內建四個專案,分別是
  • AspireSampleApp.ApiService
  • AspireSampleApp.AppHost
  • AspireSampleApp.ServiceDefaults
  • AspireSampleApp.Web
接著我們來針對這些重要的專案分別作重點介紹

AppHost
做為跨專案的協調器 (orchestrator) 項目,用於連結和配置應用程式不同項目和服務。此專案會設定為方案的啟動項目,並且依賴於 AspireSample.ApiService 和 AspireSample.Web 專案。專案命名以 *.AppHost 做結尾,且開啟專案屬性的設定為 true
前面提到 AppHost 專案作為協調器,那麼在這專案要如何設定呢 ? 我們可以開啟 AspireSampleApp.AppHost 中的 Program.cs 程式碼來一探究竟
var builder = DistributedApplication.CreateBuilder(args);

var cache = builder.AddRedis("cache");

var apiService = builder.AddProject("apiservice");

builder.AddProject("webfrontend")
    .WithReference(cache)
    .WithReference(apiService);

builder.Build().Run();
  • 以上代碼開始透過 CreateBuilder 方法建立 IDistributedApplicationBuilder 的 instance,透過 IDistributedApplicationBuilder 的擴充方法來設定其相依資源 (resource)
  • 程式碼中描述定義包含三個資源 (resource),要設定 Redis 快取就是透過 .AddRedis 擴充方法並設定其容器 (Container) 名稱是 Cache
  • 接著加入兩個專案 (Project) 分別是 apiservice 跟 webfrontend,因此使用 AddProjects 方法來將 project 加入到 AppHost 專案中
  • WithReference 方法是動態注入 service discovery 的一部分,將其資訊 endpoint 注入到應用程式的配置或資源中,使得在運行時可以根據配置或環境進行適當的連結,查看 source code 可以發現其 format 是 "services__{sourceResourceName}__{endpointIndex}={endpointNameQualifiedUriString}。
  • 使用 Build.Run() 方法來啟動應用程式與所有相依性
MSDN 有針對各自用途做詳細介紹並整理成可視化的圖形如下
另外也提供多種資源類型,像是 ProjectResource、ContainerResource 和 ExecutableResource,詳細可以參考 app-host-overview 介紹

ServiceDefaults
ServiceDefaults 專案目的是放置共用的項目。

過去在開發雲原生應用程式時,遇到好用的 package 或是套件時可以說是每個專案都會同步加上,例如像是 log 可能會使用 serilog 搭配合適的 skin,如果想要加上 retry 機制可能會加上 polly 等廣為人知的好用套件,但如果應用程式的架構或是服務拆分越來越細時,這些套件會在各個專案都重複看見與被使用,假設其套件的組態設定需要調整時,也需要在各個所使用的專案上進行調整,才有機會將其設定統一設定好不遺漏。這可以說是開發者不方便的點之一,在 .NET Aspire 中為了解決這問題,提供開發者可以將其設定移置 serviceDefault 專案中,來提供開發者更好的開發體驗,也可以在其專案中加上工具、OpenTelemetry 狀態檢查獲是環境變數等管理。
以 AspireSampleApp 專案為例建立完後會有 AspireSampleApp.Web 與 AspireSampleApp.ApiService 等應用程式專案,兩者其共用的組態設定與配置是定義在 AspireSampleApp.ServiceDefaults 的 AddServiceDefaults 擴充方法中。程式碼如下
public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBuilder builder)
{
    builder.ConfigureOpenTelemetry();

    builder.AddDefaultHealthChecks();

    builder.Services.AddServiceDiscovery();

    builder.Services.ConfigureHttpClientDefaults(http =>
    {
        // Turn on resilience by default
        http.AddStandardResilienceHandler();

        // Turn on service discovery by default
        http.UseServiceDiscovery();
    });

    return builder;
}
  • ConfigureOpenTelemetry 方法 : 用來設定 openTelemetry 收集遙測數據的設定
  • AddDefaultHealthChecks 方法 : 定義應用程式 healthcheck (AddCheck 方法) 服務正常時需要回傳的值
  • AddServiceDiscovery 方法 : 新增 service discovery 功能
  • ConfigureHttpClientDefaults : 新增在使用 httpClient 的預設值
ApiService 與 Web
  • ApiService : 隨機回傳氣溫結果
  • Web : 作為 Client 負責呼叫 apiService 並將請求呈現在網頁上
目的是演示 Demo 用的氣溫應用程式,若有興趣可以查看專案內容,在此就不在多介紹說明

測試專案
在上面的描述與說明中我們可以初步了解 .NET Aspire sample project 方案中各自專案的用途,設定 AspireSampleApp.Web 為起始專案後,在 Visual Studio 按下 F5 來執行應用程式,可以看到天氣的頁面
專案啟動時同步也會啟動 .NET Aspire 儀表板示意圖如下
今天快速介紹了關於微軟推出的新框架 .NET Aspire,以及透過 Visual Studio IDE 建立 .NET Aspire 新專案的過程,也探索了其方案建立後專案的組成與各自的設計含意與目的,相信透過今天的文章對於 .NET Aspire 有了初步的認識,.NET 8 整合 openTelemetry 後將其遙測數據收集到 .NET Aspire 所提供的 Dashboard 上,讓開發者對於應用程式的掌握度更高,下一篇我們再來探索 .NET Aspire 儀錶板其奧妙之處 !


參考
.NET Aspire documentation (Preview)

Copyright © m@rcus 學習筆記 | Powered by Blogger

Design by Anders Noren | Blogger Theme by NewBloggerThemes.com