Azure Service Fabric Reliable Services 中的 ASP.NET Core

ASP.NET Core 是開放原始碼和跨平台架構。 此架構的設計目的是要建立雲端式、網際網路連線的應用程式,例如 Web 應用程式、IoT 應用程式和行動後端。

本文是深度指南,說明使用 Microsoft.ServiceFabric.AspNetCore. NuGet 套件集在 Service Fabric Reliable Services 中裝載 ASP.NET Core 服務。

如需 Service Fabric 中 ASP.NET Core 的簡介教學課程,以及如何設定開發環境的指示,請參閱教學課程:建立和部署含有 ASP.NET Core Web API 前端服務和具狀態後端服務的應用程式

本文的其餘部分假設您十分熟悉 ASP.NET Core。 如果並非如此,請仔細閱讀 ASP.NET Core 基本概念

Service Fabric 環境中的 ASP.NET Core

ASP.NET Core 和 Service Fabric 應用程式可以在 .NET Core 或完整的 .NET Framework 上執行。 您可在 Service Fabric 中以兩種方式使用 ASP.NET Core︰

  • 裝載作為客體可執行檔。 此方式主要用於在 Service Fabric 上執行現有的 ASP.NET Core 應用程式而無須變更程式碼。
  • 在 Reliable Service 內部執行。 此方式可與 Service Fabric 執行階段更緊密整合,並可允許具狀態 ASP.NET Core 服務。

本文的其餘部分將說明如何使用隨附於 Service Fabric SDK 的 ASP.NET Core 整合元件,在 Reliable Service 內部使用 ASP.NET Core。

Service Fabric 服務裝載

在 Service Fabric 中,您服務的一或多個執行個體及/或複本會在服務主機處理序中執行,這是執行您服務程式碼的可執行檔。 您身為服務作者,擁有服務主機處理序,且 Service Fabric 會為您啟動及監視。

傳統 ASP.NET (直到 MVC 5) 已透過 System.Web.dll 與 IIS 緊密結合。 ASP.NET Core 能區別網頁伺服器與 web 應用程式。 這種分隔可讓 Web 應用程式在不同的網頁伺服器之間可移植。 其也可讓網頁伺服器自我裝載。 這表示您可以在自己的程序中啟動網頁伺服器,而不是由專用網頁伺服器軟體 (例如 IIS) 所擁有的程序啟動。

若要將 Service Fabric 服務和 ASP.NET 結合為客體可執行檔或結合至 Reliable Service 之中,您必須能夠在服務主機處理序中啟動 ASP.NET。 ASP.NET Core 自我裝載可讓您執行這項操作。

在 Reliable Service 中裝載 ASP.NET Core

一般而言,自我裝載的 ASP.NET Core 應用程式會在應用程式的進入點建立 WebHost,例如 Program.cs 方法中的 static void Main()。 在此情況下,WebHost 的生命週期會繫結至處理序的生命週期。

Hosting ASP.NET Core in a process

但應用程式進入點並不是在可靠服務中建立 WebHost 的正確位置。 這是因為應用程式進入點僅用來向 Service Fabric 執行時間註冊服務類型,因此可以建立該服務類型的執行個體。 WebHost 應該建立在 Reliable Service 之內。 在服務主機處理序中,服務執行個體及/或複本可以通過多個生命週期。

Reliable Service 執行個體是由您衍生自 StatelessServiceStatefulService 的服務類別代表。 服務的通訊堆疊包含在您服務類別中的 ICommunicationListener 實作。 Microsoft.ServiceFabric.AspNetCore.* NuGet 封裝包含 ICommunicationListener 的實作,可在 Reliable Service 中啟動和管理 Kestrel 或 HTTP.sys 的 ASP.NET Core WebHost。

Diagram for hosting ASP.NET Core in a reliable service

ASP.NET Core ICommunicationListeners

Microsoft.ServiceFabric.AspNetCore.* NuGet 封裝中的 Kestrel 和 HTTP.sys ICommunicationListener 實作有類似使用模式。 但其執行動作與每個網頁伺服器的特定動作稍有不同。

這兩種通訊接聽程式都會提供使用下列引數的建構函式︰

  • ServiceContext serviceContext:這是包含關於執行服務資訊的 ServiceContext 物件。
  • string endpointName:這是在 ServiceManifest.xml 中的 Endpoint 組態名稱。 這是兩個通訊接聽程式主要的不同之處。 HTTP.sys 需要Endpoint 組態,而 Kestrel 不需要。
  • Func<string, AspNetCoreCommunicationListener, IWebHost> build:您所實作,並在其中建立並傳回 IWebHost 的 lambda。 這可讓您以您通常會在 ASP.NET Core 應用程式的方式設定 IWebHost。 Lambda 提供為您根據 Service Fabric 整合選項所產生的 URL 和您提供的 Endpoint 組態。 然後您可以修改或使用該 URL 來啟動網頁伺服器。

Service Fabric 整合中介軟體

Microsoft.ServiceFabric.AspNetCore NuGet 套件包含 IWebHostBuilder 上的 UseServiceFabricIntegration 擴充方法,其會新增 Service Fabric 的中介軟體。 此中介軟體會設定 Kestrel 或 HTTP.sys ICommunicationListener,以 Service Fabric 命名服務註冊唯一的服務 URL。 接著,其會驗證用戶端要求,以確保用戶端連接到正確的服務。

必須執行此步驟,才能防止用戶端錯誤地連接到錯誤的服務。 這是因為在 Service Fabric 的共用主機環境中,可以在相同的實體或虛擬機器上執行多個 Web 應用程式,但不會使用唯一的主機名稱。 此情節會在下一節中詳細說明。

誤用身分識別的案例

不論通訊協定,服務複本會接聽唯一 IP:port 組合。 一旦服務複本開始在 IP:port 端點上接聽,便會向 Service Fabric 命名服務報告該端點位址。 用戶端或其他服務可以在此找到該端點。 如果服務使用動態指派的應用程式連接埠,服務複本可能會針對先前碰巧位於相同實體或虛擬機器上的其他服務,而誤用其 IP:port 端點。 這可能會造成用戶端不小心連線至錯誤的服務。 如果發生下列事件順序,就會產生此案例:

  1. 服務 A 透過 HTTP 接聽 10.0.0.1:30000。
  2. 用戶端解析服務 A 並取得位址 10.0.0.1:30000。
  3. 服務 A 移到另一個節點。
  4. 服務 B 置於 10.0.0.1 且碰巧使用相同的連接埠 30000。
  5. 用戶端使用快取的位址 10.0.0.1:30000 嘗試連線到服務 A。
  6. 用戶端現在已成功連接至 B,不瞭解其連線到錯誤的服務。

這會不定時造成 難以診斷的 Bug。

使用唯一的服務 URL

若要避免這些問題,服務可以使用唯一識別碼將端點張貼至命名服務,並在用戶端要求期間驗證該唯一識別碼。 這在非惡意租用戶受信任環境中的服務之間為合作式動作。 在惡意租用戶環境中不會提供安全服務驗證。

在受信任環境中,由 UseServiceFabricIntegration 法所新增的中介軟體會自動將唯一識別項附加到已張貼至命名服務的地址。 會在每個要求上驗證該識別項。 如果識別項不符合,中介軟體會立即傳回 HTTP 410 不存在的回應。

使用動態指派連接埠的服務應該要使用此中介軟體。

使用固定唯一連接埠的服務在合作環境中沒有此問題。 固定的唯一連接埠通常是用於對外開放的服務,需要用戶端應用程式連線的知名通訊埠。 例如,大部分對網際網路開放的 Web 應用程式會使用 Web 瀏覽器連接的連接埠 80 或 443。 在此情況下,不應該啟用唯一識別碼。

下圖顯示已啟用中介軟體的要求流程︰

Service Fabric ASP.NET Core integration

Kestrel 和 HTTP.sys ICommunicationListener 實作以完全相同的方式使用這項機制。 雖然 HTTP.sys 可以使用基礎 HTTP.sys 連接埠共用功能,以根據唯一的 URL 路徑內部區分要求,但 HTTP.sys ICommunicationListener 實作不會使用該功能。 這是因為其會導致稍早所述案例中的 HTTP 503 和 HTTP 404 錯誤狀態碼。 這會使用戶端難以判斷錯誤的意圖,因為 HTTP 503 和 HTTP 404 已經常用來表示其他錯誤。

因此,Kestrel 和 HTTP.sys ICommunicationListener 的實作為標準化 UseServiceFabricIntegration 擴充方法所提供的中介軟體。 因此,用戶端只需要在 HTTP 410 回應上執行服務端點重新解析動作。

Reliable Services 中的 HTTP.sys

您可在 Reliable Services 中使用 HTTP.sys,方法為匯入 Microsoft.ServiceFabric.AspNetCore.HttpSys NuGet 封裝。 此封裝包含 HttpSysCommunicationListener,即 ICommunicationListener 的實作。 HttpSysCommunicationListener可讓您使用 HTTP.sys 作為網頁伺服器,在可靠的服務內建立 ASP.NET Core WebHost。

HTTP.sys 會建置在 Windows HTTP Server API 上。 此 API 使用 HTTP.sys 核心驅動程式來處理 HTTP 要求,並將要求路由傳送到執行 Web 應用程式的處理程序。 這可讓相同實體或虛擬機器上的多個處理程序在相同連接埠上裝載 Web 應用程式,並由唯一的 URL 路徑或主機名稱區分。 這些功能對於在 Service Fabric 中於相同叢集裝載多個網站很有用。

注意

HTTP.sys 實作只適用於 Windows 平台。

下圖說明 HTTP.sys 如何在 Windows 上使用 HTTP.sys 核心驅動程式進行連接埠共用︰

HTTP.sys diagram

無狀態服務中的 HTTP.sys

若要在無狀態服務中使用 HttpSys,請覆寫 CreateServiceInstanceListeners 方法並傳回 HttpSysCommunicationListener 執行個體︰

protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
    return new ServiceInstanceListener[]
    {
        new ServiceInstanceListener(serviceContext =>
            new HttpSysCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
                new WebHostBuilder()
                    .UseHttpSys()
                    .ConfigureServices(
                        services => services
                            .AddSingleton<StatelessServiceContext>(serviceContext))
                    .UseContentRoot(Directory.GetCurrentDirectory())
                    .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
                    .UseStartup<Startup>()
                    .UseUrls(url)
                    .Build()))
    };
}

具狀態服務中的 HTTP.sys

由於基礎 HTTP.sys 連接埠共用功能很複雜,HttpSysCommunicationListener 目前未設計用於具狀態服務。 如需詳細資訊,請參閱下一節的使用 HTTP.sys 動態連接埠配置。 針對具狀態服務,Kestrel 是建議的網頁伺服器。

端點設定

使用 Windows HTTP Server API,包括 HTTP.sys 的網頁伺服器需要 Endpoint 組態。 使用 Windows HTTP 伺服器 API 的網頁伺服器都必須先保留其 URL 與 HTTP.sys (這通常使用 netsh 工具來完成)。

這個動作需要您服務預設未具有的提高權限。 ServiceManifest.xml 中 Endpoint 設定 Protocol 屬性的 "http" 或 "https" 選項會特別用來指示 Service Fabric 執行階段,以代表您註冊 URL 與 HTTP.sys。 其會使用強式萬用字元 URL 前置詞來執行這項工作。

例如,若要保留服務的 http://+:80,應該在 ServiceManifest.xml 中使用下列組態︰

<ServiceManifest ... >
    ...
    <Resources>
        <Endpoints>
            <Endpoint Name="ServiceEndpoint" Protocol="http" Port="80" />
        </Endpoints>
    </Resources>

</ServiceManifest>

端點名稱必須傳遞給 HttpSysCommunicationListener 建構函式︰

 new HttpSysCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
 {
     return new WebHostBuilder()
         .UseHttpSys()
         .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
         .UseUrls(url)
         .Build();
 })

搭配使用 HTTP.sys 與靜態連接埠

搭配使用靜態連接埠與 HTTP.sys,在 Endpoint 組態中提供通訊埠編號︰

  <Resources>
    <Endpoints>
      <Endpoint Protocol="http" Name="ServiceEndpoint" Port="80" />
    </Endpoints>
  </Resources>

搭配使用 HTTP.sys 與動態連接埠

若要使用動態指派的連接埠與 HTTP.sys,省略 Endpoint 組態中的 Port 屬性︰

  <Resources>
    <Endpoints>
      <Endpoint Protocol="http" Name="ServiceEndpoint" />
    </Endpoints>
  </Resources>

Endpoint 組態配置的動態連接埠只提供每個主機處理序一個連接埠。 目前的 Service Fabric 裝載模型允許在相同程序中裝載多個服務執行個體及/或複本。 這表示在透過 Endpoint 設定配置時,每一個項目都會共用相同的連接埠。 多個 HTTP.sys 執行個體可以使用基礎 HTTP.sys 連接埠共用功能來共用連接埠。 但由於用戶端要求所帶來的複雜性,因此不支援 HttpSysCommunicationListener。 針對動態連接埠使用量,Kestrel 是建議的網頁伺服器。

Reliable Services 中的 Kestrel

您可在 Reliable Services 中使用 Kestrel,方法為匯入 Microsoft.ServiceFabric.AspNetCore.Kestrel NuGet 封裝。 此封裝包含 KestrelCommunicationListener,即 ICommunicationListener 的實作。 KestrelCommunicationListener 可讓您使用 Kestrel 作為網頁伺服器,在可靠的服務內建立 ASP.NET Core WebHost。

Kestrel 是 ASP.NET Core 的跨平台網頁伺服器。 不同於 HTTP.sys,Kestrel 不會使用集中式端點管理員。 且不同於 Http.sys,Kestrel 不支援多個處理序之間的連接埠共用。 Kestrel 的每個執行個體必須使用唯一的連接埠。 如需 Kestrel 的詳細資訊,請參閱實作詳細資料

Kestrel diagram

無狀態服務中的 Kestrel

若要在無狀態服務中使用 Kestrel,請覆寫 CreateServiceInstanceListeners 方法並傳回 KestrelCommunicationListener 執行個體︰

protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
    return new ServiceInstanceListener[]
    {
        new ServiceInstanceListener(serviceContext =>
            new KestrelCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
                new WebHostBuilder()
                    .UseKestrel()
                    .ConfigureServices(
                        services => services
                            .AddSingleton<StatelessServiceContext>(serviceContext))
                    .UseContentRoot(Directory.GetCurrentDirectory())
                    .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.UseUniqueServiceUrl)
                    .UseStartup<Startup>()
                    .UseUrls(url)
                    .Build();
            ))
    };
}

具狀態服務中的 Kestrel

若要在具狀態服務中使用 Kestrel,請覆寫 CreateServiceReplicaListeners 方法並傳回 KestrelCommunicationListener 執行個體︰

protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
{
    return new ServiceReplicaListener[]
    {
        new ServiceReplicaListener(serviceContext =>
            new KestrelCommunicationListener(serviceContext, (url, listener) =>
                new WebHostBuilder()
                    .UseKestrel()
                    .ConfigureServices(
                         services => services
                             .AddSingleton<StatefulServiceContext>(serviceContext)
                             .AddSingleton<IReliableStateManager>(this.StateManager))
                    .UseContentRoot(Directory.GetCurrentDirectory())
                    .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.UseUniqueServiceUrl)
                    .UseStartup<Startup>()
                    .UseUrls(url)
                    .Build();
            ))
    };
}

在此範例中,IReliableStateManager 的單一執行個體會提供給 WebHost 相依性插入容器。 這並非絕對必要,但可讓您在 MVC 控制器動作方法中使用 IReliableStateManager 和可靠的集合。

Endpoint 組態名稱提供給具狀態服務中的 KestrelCommunicationListener。 我們會在下列各節中詳細說明這個部分。

將 Kestrel 設定為使用 HTTPS

在服務中使用 Kestrel 啟用 HTTPS 時,您將必須設定數個接聽選項。 請將 ServiceInstanceListener 更新為使用 EndpointHttps 端點,並在特定連接埠 (例如連接埠 443) 上接聽。 將 Web 主機設定為使用 Kestrel 網頁伺服器時,您必須設定 Kestrel 以接聽所有網路介面上的 IPv6 位址:

new ServiceInstanceListener(
serviceContext =>
    new KestrelCommunicationListener(
        serviceContext,
        "EndpointHttps",
        (url, listener) =>
        {
            ServiceEventSource.Current.ServiceMessage(serviceContext, $"Starting Kestrel on {url}");

            return new WebHostBuilder()
                .UseKestrel(opt =>
                {
                    int port = serviceContext.CodePackageActivationContext.GetEndpoint("EndpointHttps").Port;
                    opt.Listen(IPAddress.IPv6Any, port, listenOptions =>
                    {
                        listenOptions.UseHttps(GetCertificateFromStore());
                        listenOptions.NoDelay = true;
                    });
                })
                .ConfigureAppConfiguration((builderContext, config) =>
                {
                    config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
                })

                .ConfigureServices(
                    services => services
                        .AddSingleton<HttpClient>(new HttpClient())
                        .AddSingleton<FabricClient>(new FabricClient())
                        .AddSingleton<StatelessServiceContext>(serviceContext))
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseStartup<Startup>()
                .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
                .UseUrls(url)
                .Build();
        }))

如需教學課程中的完整範例,請參閱將 Kestrel 設定為使用 HTTPS

端點設定

Endpoint 組態不需要使用 Kestrel。

Kestrel 是簡單的獨立網頁伺服器。 與 HTTP.sys (或 HttpListener) 不同,並不需要 ServiceManifest.xml 中的 Endpoint 設定,因為其不需要在啟動之前註冊 URL。

搭配使用 Kestrel 與靜態連接埠

您可在 ServiceManifest.xml 的 Endpoint 組態中設定靜態連接埠以供與 Kestrel 搭配使用。 雖然這並非絕對必要,但會提供兩個潛在的好處︰

  • 如果連接埠不在應用程式連接埠範圍中,則會由 Service Fabric 透過 OS 防火牆開啟。
  • 透過 KestrelCommunicationListener 提供給您的 URL 會使用此連接埠。
  <Resources>
    <Endpoints>
      <Endpoint Protocol="http" Name="ServiceEndpoint" Port="80" />
    </Endpoints>
  </Resources>

如果設定了 Endpoint,其名稱必須傳遞至 KestrelCommunicationListener 建構函式︰

new KestrelCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) => ...

如果 ServiceManifest.xml 未使用 Endpoint 設定,請省略 KestrelCommunicationListener 建構函式中的名稱。 在此情況下,則會使用動態埠。 如需相關的詳細資訊,請參閱下一節。

搭配使用 Kestrel 與動態連接埠

Kestrel 無法使用 ServiceManifest.xml 中 Endpoint 設定的自動連接埠指派。 這是因為從 Endpoint 設定的自動連接埠指派會為每個主機處理序指派唯一的連接埠,而單一主機處理序可以包含多個 Kestrel 執行個體。 這不適用於 Kestrel,因為其不支援連接埠共用。 因此,每個 Kestrel 執行個體都必須在唯一的連接埠上開啟。

若要使用動態通訊連接埠指派與 Kestrel,請完全省略 ServiceManifest.xml 中的 Endpoint 設定,且不要將端點名稱傳遞至 KestrelCommunicationListener 建構函式,如下所示︰

new KestrelCommunicationListener(serviceContext, (url, listener) => ...

在此組態中,KestrelCommunicationListener 會自動從應用程式連接埠範圍選取未使用的連接埠。

若為 HTTPS,則應該將端點設定為使用 HTTPS 通訊協定,而不要在 ServiceManifest.xml 中指定連接埠,然後將端點名稱傳遞給 KestrelCommunicationListener 的建構函式。

IHost 和最小裝載整合

除了 IWebHost/IWebHostBuilder 外,KestrelCommunicationListenerHttpSysCommunicationListener 也支援使用 IHost/IHostBuilder 來建置 ASP.NET Core 服務。 從 5.2.1363 版的 Microsoft.ServiceFabric.AspNetCore.KestrelMicrosoft.ServiceFabric.AspNetCore.HttpSys 套件開始便有提供此功能。

// Stateless Service
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
    return new ServiceInstanceListener[]
    {
        new ServiceInstanceListener(serviceContext =>
            new KestrelCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
            {
                return Host.CreateDefaultBuilder()
                        .ConfigureWebHostDefaults(webBuilder =>
                        {
                            webBuilder.UseKestrel()
                                .UseStartup<Startup>()
                                .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
                                .UseContentRoot(Directory.GetCurrentDirectory())
                                .UseUrls(url);
                        })
                        .ConfigureServices(services => services.AddSingleton<StatelessServiceContext>(serviceContext))
                        .Build();
            }))
    };
}

// Stateful Service
protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
{
    return new ServiceReplicaListener[]
    {
        new ServiceReplicaListener(serviceContext =>
            new KestrelCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
            {
                return Host.CreateDefaultBuilder()
                        .ConfigureWebHostDefaults(webBuilder =>
                        {
                            webBuilder.UseKestrel()
                                .UseStartup<Startup>()
                                .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.UseUniqueServiceUrl)
                                .UseContentRoot(Directory.GetCurrentDirectory())
                                .UseUrls(url);
                        })
                        .ConfigureServices(services =>
                        {
                            services.AddSingleton<StatefulServiceContext>(serviceContext);
                            services.AddSingleton<IReliableStateManager>(this.StateManager);
                        })
                        .Build();
            }))
    };
}

注意

由於 KestrelCommunicationListener 和 HttpSysCommunicationListener 適用於 Web 服務,因此必須透過 IHost 來註冊/設定 Web 伺服器 (使用 ConfigureWebHostDefaultsConfigureWebHost 方法)

ASP.NET 6 引進了最小裝載模型,這是比較簡化的 Web 應用程式建立方式。 最小裝載模型也可與 KestrelCommunicationListener 和 HttpSysCommunicationListener 搭配使用。

// Stateless Service
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
    return new ServiceInstanceListener[]
    {
        new ServiceInstanceListener(serviceContext =>
            new KestrelCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
            {
                var builder = WebApplication.CreateBuilder();

                builder.Services.AddSingleton<StatelessServiceContext>(serviceContext);
                builder.WebHost
                            .UseKestrel()
                            .UseContentRoot(Directory.GetCurrentDirectory())
                            .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
                            .UseUrls(url);

                builder.Services.AddControllersWithViews();

                var app = builder.Build();

                if (!app.Environment.IsDevelopment())
                {
                    app.UseExceptionHandler("/Home/Error");
                }

                app.UseHttpsRedirection();
                app.UseStaticFiles();
                app.UseRouting();
                app.UseAuthorization();
                app.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");

                return app;
            }))
    };
}
// Stateful Service
protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
{
    return new ServiceReplicaListener[]
    {
        new ServiceReplicaListener(serviceContext =>
            new KestrelCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
            {
                var builder = WebApplication.CreateBuilder();

                builder.Services
                            .AddSingleton<StatefulServiceContext>(serviceContext)
                            .AddSingleton<IReliableStateManager>(this.StateManager);
                builder.WebHost
                            .UseKestrel()
                            .UseContentRoot(Directory.GetCurrentDirectory())
                            .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.UseUniqueServiceUrl)
                            .UseUrls(url);

                builder.Services.AddControllersWithViews();

                var app = builder.Build();

                if (!app.Environment.IsDevelopment())
                {
                    app.UseExceptionHandler("/Home/Error");
                }
                app.UseStaticFiles();
                app.UseRouting();
                app.UseAuthorization();
                app.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");

                return app;
            }))
    };
}

Service Fabric 設定提供者

ASP.NET Core 中的應用程式設定是以設定提供者所建立的索引鍵/值組為基礎。 閱讀 ASP.NET Core 中的設定,以深入瞭解一般 ASP.NET Core 設定支援。

本節說明 Service Fabric 設定提供者如何藉由匯入 Microsoft.ServiceFabric.AspNetCore.Configuration NuGet 套件與 ASP.NET Core 設定整合。

AddServiceFabricConfiguration 啟始延伸模組

匯入 Microsoft.ServiceFabric.AspNetCore.Configuration NuGet 套件之後,您必須使用 ASP.NET Core 設定 API 註冊 Service Fabric 設定來源。 做法是對 IConfigurationBuilder 檢查命名空間 Microsoft.ServiceFabric.AspNetCore.Configuration 中的 AddServiceFabricConfiguration 延伸模組。

using Microsoft.ServiceFabric.AspNetCore.Configuration;

public Startup(IHostingEnvironment env)
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
        .AddServiceFabricConfiguration() // Add Service Fabric configuration settings.
        .AddEnvironmentVariables();
    Configuration = builder.Build();
}

public IConfigurationRoot Configuration { get; }

現在 ASP.NET Core 服務可以存取 Service Fabric 的設定,就像任何其他應用程式設定一樣。 例如,您可以使用選項模式將設定載入強型別物件。

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<MyOptions>(Configuration);  // Strongly typed configuration object.
    services.AddMvc();
}

預設金鑰對應

根據預設,Service Fabric 設定提供者會包含套件名稱、區段名稱和屬性名稱。 這些會一起形成 ASP.NET Core 的設定機碼,如下所示:

$"{this.PackageName}{ConfigurationPath.KeyDelimiter}{section.Name}{ConfigurationPath.KeyDelimiter}{property.Name}"

例如,如果您的設定套件以下列內容命名 MyConfigPackage,則可透過 MyConfigPackage:MyConfigSection:MyParameter在 ASP.NET Core IConfiguration 上取得設定值。

<?xml version="1.0" encoding="utf-8" ?>
<Settings xmlns:xsd="https://www.w3.org/2001/XMLSchema" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/2011/01/fabric">  
  <Section Name="MyConfigSection">
    <Parameter Name="MyParameter" Value="Value1" />
  </Section>  
</Settings>

Service Fabric 設定選項

Service Fabric 設定提供者也支援 ServiceFabricConfigurationOptions 變更金鑰對應的預設行為。

加密設定

Service Fabric 支援加密設定,就像 Service Fabric 設定提供者一樣。 根據預設,加密的設定不會解密成 ASP.NET Core IConfiguration。 加密值會改為儲存在該處。 但是,如果您想要將值解密以儲存在 ASP.NET Core IConfiguration 中,則可以在 AddServiceFabricConfiguration 擴充功能中將 DecryptValue 旗標設定為 false,如下所示:

public Startup()
{
    ICodePackageActivationContext activationContext = FabricRuntime.GetActivationContext();
    var builder = new ConfigurationBuilder()        
        .AddServiceFabricConfiguration(activationContext, (options) => options.DecryptValue = false); // set flag to decrypt the value
    Configuration = builder.Build();
}

多個設定套件

Service Fabric 支援多個設定套件。 依預設,套件名稱會包含在設定機碼中。 但是,您可以將 IncludePackageName 旗標設定為 false,如下所示:

public Startup()
{
    ICodePackageActivationContext activationContext = FabricRuntime.GetActivationContext();
    var builder = new ConfigurationBuilder()        
        // exclude package name from key.
        .AddServiceFabricConfiguration(activationContext, (options) => options.IncludePackageName = false); 
    Configuration = builder.Build();
}

自訂索引鍵對應、值擷取和資料填入

Service Fabric 設定提供者也支援使用 ExtractKeyFunc 自訂索引鍵對應和使用 ExtractValueFunc 自訂擷取值的更先進案例。 您甚至可以使用 ConfigAction 將填入資料的整個程序,從 Service Fabric 設定變更為 ASP.NET Core 設定。

下列範例說明如何使用 ConfigAction 自訂資料填入:

public Startup()
{
    ICodePackageActivationContext activationContext = FabricRuntime.GetActivationContext();
    
    this.valueCount = 0;
    this.sectionCount = 0;
    var builder = new ConfigurationBuilder();
    builder.AddServiceFabricConfiguration(activationContext, (options) =>
        {
            options.ConfigAction = (package, configData) =>
            {
                ILogger logger = new ConsoleLogger("Test", null, false);
                logger.LogInformation($"Config Update for package {package.Path} started");

                foreach (var section in package.Settings.Sections)
                {
                    this.sectionCount++;

                    foreach (var param in section.Parameters)
                    {
                        configData[options.ExtractKeyFunc(section, param)] = options.ExtractValueFunc(section, param);
                        this.valueCount++;
                    }
                }

                logger.LogInformation($"Config Update for package {package.Path} finished");
            };
        });
  Configuration = builder.Build();
}

組態更新

Service Fabric 設定提供者也支援設定更新。 您可以使用 ASP.NET Core IOptionsMonitor 來接收變更通知,然後使用 IOptionsSnapshot 來重新載入設定資料。 如需詳細資訊,請參閱 ASP.NET Core 選項

預設支援這些選項。 不需要進一步的編碼來啟用設定更新。

案例和組態

本節提供網頁伺服器、連接埠設定、Service Fabric 整合選項,以及我們建議您針對下列案例進行疑難排解的其他設定組合:

  • 外部公開 ASP.NET Core 無狀態服務
  • 僅供內部使用的 ASP.NET Core 無狀態服務
  • 僅供內部使用的 ASP.NET Core 具狀態服務

外部公開服務是公開可以從叢集外部呼叫的端點,通常是透過負載平衡器。

僅供內部使用服務的端點僅可從叢集內呼叫。

注意

具狀態服務端點通常應該不會公開至網際網路。 未察覺到 Service Fabric 服務解析 (例如 Azure Load Balancer) 的負載平衡器後方叢集,將無法公開具狀態服務。 這是因為負載平衡器無法找出流量,並將其路由傳送至適當的具狀態服務複本。

外部公開 ASP.NET Core 無狀態服務

Kestrel 建議用於前端服務的網頁伺服器,這些服務會公開於外部、網際網路 HTTP 端點。 在 Windows 上,HTTP.sys 可提供連接埠共用功能,讓您使用相同的連接埠,在同一組節點上裝載多個 Web 服務。 在此案例中,Web 服務會以主機名稱或路徑來區分,而不需依賴前端 Proxy 或閘道來提供 HTTP 路由。

當公開至網際網路時,無狀態服務應使用可透過負載平衡器連線的已知且穩定端點。 您會將此 URL 提供給您的應用程式使用者。 建議使用下列設定:

類型 建議 備註
網頁伺服器 Kestrel Kestrel 是慣用的 Web 伺服器,因為 Windows 和 Linux 均支援 Kestrel。
連接埠組態 static 已知的靜態連接埠應在 ServiceManifest.xml 的 Endpoints 組態中設定,例如 HTTP 為 80 或 443 為 HTTPS。
ServiceFabricIntegrationOptions 設定 Service Fabric 整合中介軟體時,請使用 ServiceFabricIntegrationOptions.None 選項,以便服務不會嘗試驗證唯一識別項的傳入要求。 應用程式外部使用者不會知道中介軟體所使用的唯一識別資訊。
執行個體計數 -1 在一般使用案例中,執行個體計數設定應該設為 -1。 這樣做的目的,是要讓執行個體可在接收來自負載平衡器流量的所有節點上使用。

如果多個外部公開的服務共用相同的節點集,則您可使用 HTTP.sys 搭配唯一但穩定的 URL 路徑。 您可以透過修改設定 IWebHost 時提供的 URL 來完成此操作。 請注意,這只適用於 HTTP.sys。

new HttpSysCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
{
    url += "/MyUniqueServicePath";

    return new WebHostBuilder()
        .UseHttpSys()
        ...
        .UseUrls(url)
        .Build();
})

僅供內部使用的無狀態 ASP.NET Core 服務

只會從叢集內呼叫的無狀態服務應該使用唯一的 URL 並動態指派連接埠,以確保多個服務之間的合作。 建議使用下列設定:

類型 建議 備註
網頁伺服器 Kestrel 雖然您可針對內部的無狀態服務使用 HTTP.sys,但 Kestrel 是最適合的伺服器,可允許多個服務執行個體共用主機。
連接埠組態 動態指派 多個具狀態服務的複本可能會共用主機處理序或主機作業系統,因此需要唯一的連接埠。
ServiceFabricIntegrationOptions UseUniqueServiceUrl 使用動態連接埠指派,此設定可防止稍早所述的誤用識別問題。
InstanceCount 任意 執行個體計數設定可以設定為操作本服務所需的任何值。

僅供內部使用的具狀態 ASP.NET Core 服務

只會從叢集內呼叫的具狀態服務應該使用動態指派連接埠,以確保多個服務之間的合作。 建議使用下列設定:

類型 建議 備註
網頁伺服器 Kestrel HttpSysCommunicationListener 不是設計由複本共用主機處理序的具狀態服務使用。
連接埠組態 動態指派 多個具狀態服務的複本可能會共用主機處理序或主機作業系統,因此需要唯一的連接埠。
ServiceFabricIntegrationOptions UseUniqueServiceUrl 使用動態連接埠指派,此設定可防止稍早所述的誤用識別問題。

下一步

使用 Visual Studio 偵錯 Service Fabric 應用程式