ASP.NET Core in Azure Service Fabric Reliable Services

ASP.NET Core è un framework open source e multipiattaforma. Questo framework è progettato per la creazione di applicazioni connesse a Internet basate sul cloud, ad esempio app Web, app IoT e back-end per dispositivi mobili.

Questo articolo è una guida approfondita all'hosting di servizi ASP.NET Core in Service Fabric Reliable Services tramite il set di pacchetti NuGet Microsoft.ServiceFabric.AspNetCore.

Per un'esercitazione introduttiva su ASP.NET Core in Service Fabric e istruzioni su come configurare l'ambiente di sviluppo, vedere Esercitazione: Creare e distribuire un'applicazione con un servizio front-end api Web ASP.NET Core e un servizio back-end con stato.

Il resto di questo articolo presuppone che si abbia già familiarità con ASP.NET Core. In caso contrario, leggere i concetti fondamentali ASP.NET Core.

ASP.NET Core nell'ambiente Service Fabric

Sia ASP.NET Core che le app di Service Fabric possono essere eseguite in .NET Core o in .NET Framework completo. È possibile usare ASP.NET Core in due modi diversi in Service Fabric:

  • Ospitato come eseguibile guest. Questo modo viene usato principalmente per eseguire applicazioni ASP.NET Core esistenti in Service Fabric senza modifiche al codice.
  • Eseguire all'interno di un servizio Reliable Services. In questo modo è possibile migliorare l'integrazione con il runtime di Service Fabric e consentire servizi di ASP.NET Core con stato.

Il resto di questo articolo illustra come usare ASP.NET Core all'interno di un servizio Reliable Services, tramite i componenti di integrazione ASP.NET Core forniti con Service Fabric SDK.

Hosting di servizi di Service Fabric

In Service Fabric una o più istanze e/o repliche del servizio vengono eseguite in un processo host del servizio: un file eseguibile che esegue il codice del servizio. L'utente, autore del servizio, è proprietario del processo host del servizio e Service Fabric lo attiva e lo monitora automaticamente.

La versione tradizionale di ASP.NET, fino a MVC 5, è strettamente associata a IIS tramite System.Web.dll. ASP.NET Core fornisce una separazione tra il server Web e l'applicazione Web. Questa separazione consente alle applicazioni Web di essere portabili tra server Web diversi. Consente anche ai server Web di essere self-hosted. Ciò significa che è possibile avviare un server Web nel proprio processo, anziché un processo di proprietà di software server Web dedicato, ad esempio IIS.

Per combinare un servizio di Service Fabric e ASP.NET, come eseguibile guest o in un servizio Reliable Service, è necessario essere in grado di avviare ASP.NET all'interno del processo host del servizio. Il self-hosting di ASP.NET Core consente di eseguire questa operazione.

Hosting di ASP.NET Core in un servizio Reliable Services

Le applicazioni ASP.NET Core con self-hosting creano in genere un WebHost in un punto di ingresso dell'applicazione, ad esempio il metodo static void Main() in Program.cs. In questo caso, il ciclo di vita di WebHost è associato al ciclo di vita del processo.

Hosting di ASP.NET Core in un processo

Ma il punto di ingresso dell'applicazione non è il posto giusto per creare un WebHost in un servizio Reliable Service. Questo perché il punto di ingresso dell'applicazione viene usato solo per registrare un tipo di servizio con il runtime di Service Fabric, in modo che possa creare istanze di tale tipo di servizio. WebHost deve essere creato in un servizio Reliable Service. All'interno del processo host del servizio, le istanze del servizio e/o le repliche possono passare attraverso più cicli di vita.

Un'istanza di Reliable Services è rappresentata dalla classe del servizio derivante da StatelessService o StatefulService. Lo stack di comunicazione di un servizio è contenuto in un'implementazione ICommunicationListener nella classe del servizio. I Microsoft.ServiceFabric.AspNetCore.* pacchetti NuGet contengono implementazioni di ICommunicationListener che avviano e gestiscono il ASP.NET Core WebHost per Kestrel o HTTP.sys in un servizio Reliable Service.

Diagramma per l'hosting di ASP.NET Core in un servizio Reliable Services

ICommunicationListeners ASP.NET Core

Le ICommunicationListener implementazioni per Kestrel e HTTP.sys nei Microsoft.ServiceFabric.AspNetCore.* pacchetti NuGet hanno modelli di utilizzo simili. Tuttavia, eseguono azioni leggermente diverse specifiche per ogni server Web.

Entrambi i listener di comunicazione forniscono un costruttore che accetta gli argomenti seguenti:

  • ServiceContext serviceContext: oggetto ServiceContext che contiene informazioni sul servizio in esecuzione.
  • string endpointName: nome di una Endpoint configurazione in ServiceManifest.xml. Si tratta principalmente della differenza tra i due listener di comunicazione. HTTP.sys richiede una Endpoint configurazione, mentre Kestrel non lo fa.
  • Func<string, AspNetCoreCommunicationListener, IWebHost> build: si tratta di un'espressione lambda implementata, in cui si crea e restituisce un oggetto IWebHost. Consente di configurare IWebHost il modo in cui normalmente si farebbe in un'applicazione ASP.NET Core. L'espressione lambda fornisce un URL generato automaticamente, a seconda delle opzioni di integrazione di Service Fabric usate e della Endpoint configurazione fornita. È quindi possibile modificare o usare tale URL per avviare il server Web.

Middleware di integrazione di Service Fabric

Il Microsoft.ServiceFabric.AspNetCore pacchetto NuGet include il metodo di estensione in IWebHostBuilder che aggiunge middleware UseServiceFabricIntegration compatibile con Service Fabric. Questo middleware configura Kestrel o HTTP.sys ICommunicationListener per registrare un URL di servizio univoco con il servizio di denominazione di Service Fabric. Convalida quindi le richieste client per assicurarsi che i client si connettano al servizio corretto.

Questo passaggio è necessario per impedire ai client di connettersi erroneamente al servizio errato. Ciò è dovuto al fatto che, in un ambiente host condiviso, ad esempio Service Fabric, più applicazioni Web possono essere eseguite nella stessa macchina virtuale o fisica, ma non usano nomi host univoci. Questo scenario è descritto più dettagliatamente nella sezione successiva.

Un caso di identità errata

Le repliche del servizio, indipendentemente dal protocollo, sono in ascolto in una combinazione IP:porta univoca. Dopo che una replica del servizio ha avviato l'ascolto su un endpoint IP:port, segnala l'indirizzo dell'endpoint al servizio di denominazione di Service Fabric. Lì, i client o altri servizi possono individuarlo. Se i servizi usano porte dell'applicazione assegnate in modo dinamico, una replica del servizio potrebbe usare in modo casuale lo stesso endpoint IP:port di un altro servizio in precedenza nella stessa macchina virtuale o fisica. In questo modo un client può connettersi per errore al servizio sbagliato. Questo scenario può risultare se si verifica la sequenza di eventi seguente:

  1. Il servizio A è in ascolto in 10.0.0.1:30000 su HTTP.
  2. Il client risolve il servizio A e ottiene l'indirizzo 10.0.0.1:30000.
  3. Il servizio A si sposta in un nodo diverso.
  4. Il servizio B si trova in 10.0.0.1 e usa per coincidenza la stessa porta 30000.
  5. Il client prova a connettersi al servizio A con l'indirizzo 10.0.0.1:30000 memorizzato nella cache.
  6. Il client è ora connesso correttamente al servizio B, non rendendosi conto che è connesso al servizio errato.

Ciò può causare bug in momenti casuali che possono essere difficili da diagnosticare.

Uso di URL di servizio univoci

Per evitare questi bug, i servizi possono inviare un endpoint al servizio di denominazione con un identificatore univoco e quindi convalidare tale identificatore univoco durante le richieste client. Si tratta di un'azione cooperativa tra servizi in un ambiente attendibile di tenant non ostili. Non fornisce l'autenticazione sicura del servizio in un ambiente tenant ostile.

In un ambiente attendibile, il middleware aggiunto dal UseServiceFabricIntegration metodo aggiunge automaticamente un identificatore univoco all'indirizzo inviato al servizio di denominazione. Convalida l'identificatore in ogni richiesta. Se l'identificatore non corrisponde, il middleware restituisce immediatamente una risposta HTTP 410 Gone.

I servizi che usano una porta assegnata dinamicamente devono usare questo middleware.

I servizi che usano una porta univoca fissa non presentano questo problema in un ambiente cooperativo. Una porta fissa univoca viene in genere usata per i servizi esterni che richiedono una porta nota per la connessione delle applicazioni client. Ad esempio, la maggior parte delle applicazioni Web con connessione Internet userà la porta 80 o 443 per le connessioni web browser. In questo caso, l'identificatore univoco non deve essere abilitato.

Il diagramma seguente illustra il flusso della richiesta con il middleware abilitato:

Integrazione ASP.NET Core di Service Fabric

Le implementazioni di Kestrel e HTTP.sys ICommunicationListener usano questo meccanismo esattamente allo stesso modo. Anche se HTTP.sys può distinguere internamente le richieste in base a percorsi URL univoci usando la funzionalità di condivisione delle porteHTTP.sys sottostante, tale funzionalità non viene usata dall'implementazione HTTP.sys ICommunicationListener . Questo perché genera codici di stato di errore HTTP 503 e HTTP 404 nello scenario descritto in precedenza. Ciò rende a sua volta difficile per i client determinare la finalità dell'errore, perché HTTP 503 e HTTP 404 vengono comunemente usati per indicare altri errori.

Pertanto, sia Kestrel che HTTP.sys ICommunicationListener implementazioni standardizzano nel middleware fornito dal UseServiceFabricIntegration metodo di estensione. Pertanto, i client devono eseguire solo un'azione di risoluzione dell'endpoint di servizio nelle risposte HTTP 410.

HTTP.sys in Reliable Services

È possibile usare HTTP.sys in Reliable Services importando il pacchetto NuGet Microsoft.ServiceFabric.AspNetCore.HttpSys . Questo pacchetto contiene HttpSysCommunicationListener, un'implementazione di ICommunicationListener. HttpSysCommunicationListenerconsente di creare un ASP.NET Core WebHost all'interno di un servizio Reliable Services usando HTTP.sys come server Web.

HTTP.sys è basato sull'API server HTTP di Windows. Questa API usa il driver del kernelHTTP.sys per elaborare le richieste HTTP e instradarle ai processi che eseguono applicazioni Web. Ciò consente a più processi nella stessa macchina virtuale o fisica di ospitare applicazioni Web sulla stessa porta, disambiguate da un percorso URL univoco o da un nome host. Queste funzionalità sono utili in Service Fabric per ospitare più siti Web nello stesso cluster.

Nota

HTTP.sys'implementazione funziona solo sulla piattaforma Windows.

Il diagramma seguente illustra come HTTP.sys usa il driver kernel HTTP.sys in Windows per la condivisione delle porte:

 diagrammaHTTP.sys

HTTP.sys in un servizio senza stato

Per usare HttpSys in un servizio senza stato, eseguire l'override del metodo CreateServiceInstanceListeners e restituire un'istanza 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 in un servizio con stato

HttpSysCommunicationListener attualmente non è progettato per l'uso nei servizi con stato a causa di complicazioni con la funzionalità di condivisione delle porte sottostante HTTP.sys . Per altre informazioni, vedere la sezione seguente sull'allocazione dinamica delle porte con HTTP.sys. Per i servizi con stato, Kestrel è il server Web suggerito.

Configurazione dell'endpoint

È necessaria una Endpoint configurazione per i server Web che usano l'API Server HTTP di Windows, inclusa la HTTP.sys. I server Web che usano l'API Di Windows HTTP Server devono prima riservare l'URL con HTTP.sys (questa operazione viene normalmente eseguita con lo strumento netsh ).

Questa azione richiede privilegi elevati che i servizi non hanno per impostazione predefinita. Le opzioni "http" o "https" per la ProtocolEndpoint proprietà della configurazione in ServiceManifest.xml vengono usate in modo specifico per indicare al runtime di Service Fabric di registrare un URL con HTTP.sys per conto dell'utente. Questa operazione viene eseguita usando il prefisso URL con caratteri jolly forti .

Ad esempio, per riservare http://+:80 un servizio, usare la configurazione seguente in ServiceManifest.xml:

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

</ServiceManifest>

Il nome dell'endpoint deve essere passato al costruttore HttpSysCommunicationListener:

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

Usare HTTP.sys con una porta statica

Per usare una porta statica con HTTP.sys, specificare il numero di Endpoint porta nella configurazione:

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

Usare HTTP.sys con una porta dinamica

Per usare una porta assegnata dinamicamente con HTTP.sys, omettere la Port proprietà nella Endpoint configurazione:

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

Una porta dinamica allocata da una Endpoint configurazione fornisce una sola porta per ogni processo host. Il modello di hosting di Service Fabric corrente consente di ospitare più istanze del servizio e/o repliche nello stesso processo. Ciò significa che ognuno condividerà la stessa porta quando viene allocata tramite la Endpoint configurazione. Più istanze diHTTP.sys possono condividere una porta usando la funzionalità di condivisione delle porte sottostante HTTP.sys . Ma non è supportato da HttpSysCommunicationListener causa delle complicazioni introdotte per le richieste client. Per l'utilizzo di porta dinamica, Kestrel è il server Web suggerito.

Kestrel in Reliable Services

È possibile usare Kestrel in Reliable Services importando il pacchetto NuGet Microsoft.ServiceFabric.AspNetCore.Kestrel . Questo pacchetto contiene KestrelCommunicationListener, un'implementazione di ICommunicationListener. KestrelCommunicationListenerconsente di creare un ASP.NET Core WebHost all'interno di un servizio affidabile usando Kestrel come server Web.

Kestrel è un server Web per ASP.NET Core multipiattaforma. A differenza di HTTP.sys, Kestrel non usa un endpoint manager centralizzato. Diversamente da HTTP.sys, Kestrel non supporta anche la condivisione delle porte tra più processi. Ogni istanza di Kestrel deve usare una porta univoca. Per altre informazioni su Kestrel, vedere i dettagli dell'implementazione.

Diagramma Kestrel

Kestrel in un servizio senza stato

Per usare Kestrel in un servizio senza stato, eseguire l'override del metodo CreateServiceInstanceListeners e restituire un'istanza 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 in un servizio con stato

Per usare Kestrel in un servizio con stato, eseguire l'override del metodo CreateServiceReplicaListeners e restituire un'istanza 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();
            ))
    };
}

In questo esempio viene fornita un'istanza singleton di IReliableStateManager al contenitore di inserimento delle dipendenze WebHost. Questo non è strettamente necessario, ma consente di usare IReliableStateManager e Reliable Collections nei metodi di azione del controller MVC.

Non viene fornito un nome di configurazione Endpoint a KestrelCommunicationListener in un servizio con stato, come spiegato più dettagliatamente nella sezione seguente.

Configurare Kestrel per l'uso di HTTPS

Quando si abilita HTTPS con Kestrel nel servizio, è necessario impostare diverse opzioni di ascolto. Aggiornare l'oggetto ServiceInstanceListener per usare un endpoint EndpointHttps e ascoltare una porta specifica (ad esempio la porta 443). Quando si configura l'host Web per usare il server Web Kestrel, è necessario configurare Kestrel per ascoltare gli indirizzi IPv6 in tutte le interfacce di rete:

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();
        }))

Per un esempio completo in un'esercitazione, vedere Configurare Kestrel per l'uso di HTTPS.

Configurazione dell'endpoint

Non è necessaria una Endpoint configurazione per l'uso di Kestrel.

Kestrel è un semplice server Web autonomo. A differenza di HTTP.sys (o HttpListener), non è necessaria una Endpoint configurazione in ServiceManifest.xml perché non richiede la registrazione URL prima dell'avvio.

Usare Kestrel con una porta statica

È possibile configurare una porta statica nella Endpoint configurazione di ServiceManifest.xml da usare con Kestrel. Anche se questo non è strettamente necessario, offre due potenziali vantaggi:

  • Se la porta non rientra nell'intervallo di porte dell'applicazione, viene aperta tramite il firewall del sistema operativo da Service Fabric.
  • L'URL fornito all'utente tramite KestrelCommunicationListener userà questa porta.
  <Resources>
    <Endpoints>
      <Endpoint Protocol="http" Name="ServiceEndpoint" Port="80" />
    </Endpoints>
  </Resources>

Se è configurato un Endpoint, il nome deve essere passato al costruttore KestrelCommunicationListener:

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

Se ServiceManifest.xml non usa una Endpoint configurazione, omettere il nome nel KestrelCommunicationListener costruttore. In questo caso, userà una porta dinamica. Per altre informazioni, vedere la sezione successiva.

Usare Kestrel con una porta dinamica

Kestrel non può usare l'assegnazione automatica della Endpoint porta dalla configurazione in ServiceManifest.xml. Questo perché l'assegnazione automatica delle porte da una Endpoint configurazione assegna una porta univoca per ogni processo host e un singolo processo host può contenere più istanze di Kestrel. Questo non funziona con Kestrel perché non supporta la condivisione delle porte. Pertanto, ogni istanza di Kestrel deve essere aperta su una porta univoca.

Per usare l'assegnazione dinamica della porta con Kestrel, omettere la Endpoint configurazione in ServiceManifest.xml completamente e non passare un nome endpoint al KestrelCommunicationListener costruttore, come indicato di seguito:

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

In questa configurazione, KestrelCommunicationListener selezionerà automaticamente una porta non usata dall'intervallo di porte dell'applicazione.

Per HTTPS, deve avere l'endpoint configurato con il protocollo HTTPS senza una porta specificata in ServiceManifest.xml e passare il nome dell'endpoint al costruttore KestrelCommunicationListener.

Integrazione di IHost e Minimal Hosting

Oltre a IWebHost/IWebHostBuilder e KestrelCommunicationListenerHttpSysCommunicationListener supportare la compilazione di servizi di ASP.NET Core tramite IHost/IHostBuilder. Questa opzione è disponibile a partire da v5.2.1363 di Microsoft.ServiceFabric.AspNetCore.Kestrel e Microsoft.ServiceFabric.AspNetCore.HttpSys pacchetti.

// 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();
            }))
    };
}

Nota

Poiché KestrelCommunicationListener e HttpSysCommunicationListener sono destinati ai servizi Web, è necessario registrare/configurare un server Web (usando ConfigureWebHostDefaults o ConfigureWebHost ) tramite IHost

ASP.NET 6 ha introdotto il modello di hosting minimo che è un modo più semplificato e semplificato per creare applicazioni Web. È anche possibile usare un modello di hosting minimo con KestrelCommunicationListener e 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;
            }))
    };
}

Provider di configurazione di Service Fabric

La configurazione dell'app in ASP.NET Core si basa su coppie chiave-valore stabilite dal provider di configurazione. Leggere Configurazione in ASP.NET Core per altre informazioni sul supporto generale ASP.NET Core configurazione.

Questa sezione descrive come il provider di configurazione di Service Fabric si integra con ASP.NET Core configurazione importando il Microsoft.ServiceFabric.AspNetCore.Configuration pacchetto NuGet.

Estensioni di avvio di AddServiceFabricConfiguration

Dopo aver importato il pacchetto NuGet, è necessario registrare l'origine Microsoft.ServiceFabric.AspNetCore.Configuration configurazione di Service Fabric con l'API di configurazione ASP.NET Core. A tale scopo, controllare le estensioni AddServiceFabricConfiguration nello Microsoft.ServiceFabric.AspNetCore.Configuration spazio dei nomi rispetto IConfigurationBuildera .

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; }

Ora il servizio ASP.NET Core può accedere alle impostazioni di configurazione di Service Fabric, esattamente come qualsiasi altra impostazione dell'applicazione. Ad esempio, è possibile usare il modello di opzioni per caricare le impostazioni in oggetti fortemente tipizzati.

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

Mapping delle chiavi predefinito

Per impostazione predefinita, il provider di configurazione di Service Fabric include il nome del pacchetto, il nome della sezione e il nome della proprietà. Insieme, questi formano la chiave di configurazione ASP.NET Core, come indicato di seguito:

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

Ad esempio, se si dispone di un pacchetto di configurazione denominato MyConfigPackage con il contenuto seguente, il valore di configurazione sarà disponibile in ASP.NET Core IConfiguration tramite MyConfigPackage:MyConfigSection:MyParameter.

<?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>

Opzioni di configurazione di Service Fabric

Il provider di configurazione di Service Fabric supporta ServiceFabricConfigurationOptions anche la modifica del comportamento predefinito del mapping delle chiavi.

Impostazioni crittografate

Service Fabric supporta le impostazioni crittografate, come fa il provider di configurazione di Service Fabric. Le impostazioni crittografate non vengono decrittografate per ASP.NET Core IConfiguration per impostazione predefinita. I valori crittografati vengono archiviati invece. Tuttavia, se si vuole decrittografare il valore da archiviare in ASP.NET Core IConfiguration, è possibile impostare il flag DecryptValue su false nell'estensioneAddServiceFabricConfiguration, come indicato di seguito:

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();
}

Più pacchetti di configurazione

Service Fabric supporta più pacchetti di configurazione. Per impostazione predefinita, il nome del pacchetto è incluso nella chiave di configurazione. È tuttavia possibile impostare il IncludePackageName flag su false, come indicato di seguito:

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

Mapping delle chiavi personalizzato, estrazione di valori e popolamento dei dati

Il provider di configurazione di Service Fabric supporta anche scenari più avanzati per personalizzare il mapping delle chiavi con ExtractKeyFunc ed estrarre i valori con ExtractValueFunc. È anche possibile modificare l'intero processo di popolamento dei dati dalla configurazione di Service Fabric a ASP.NET Core configurazione usando ConfigAction.

Gli esempi seguenti illustrano come usare ConfigAction per personalizzare il popolamento dei dati:

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();
}

Aggiornamenti della configurazione

Il provider di configurazione di Service Fabric supporta anche gli aggiornamenti della configurazione. È possibile usare ASP.NET Core IOptionsMonitor per ricevere notifiche di modifica e quindi usare IOptionsSnapshot per ricaricare i dati di configurazione. Per altre informazioni, vedere ASP.NET Core opzioni.

Queste opzioni sono supportate per impostazione predefinita. Non è necessaria ulteriore codifica per abilitare gli aggiornamenti della configurazione.

Scenari e configurazioni

In questa sezione viene fornita la combinazione di server Web, configurazione delle porte, opzioni di integrazione di Service Fabric e impostazioni varie che è consigliabile risolvere i problemi seguenti:

  • Servizi ASP.NET Core senza stato esposti all'esterno
  • Servizi senza stato solo interni ASP.NET Core
  • Servizi con stato solo ASP.NET Core interni

Un servizio esposto esternamente è uno che espone un endpoint chiamato dall'esterno del cluster, in genere tramite un servizio di bilanciamento del carico.

Un servizio solo interno è uno il cui endpoint viene chiamato solo dall'interno del cluster.

Nota

Gli endpoint di servizio con stato in genere non devono essere esposti a Internet. I cluster protetti da servizi di bilanciamento del carico non a conoscenza della risoluzione del servizio di Service Fabric, ad esempio Azure Load Balancer, non potranno esporre i servizi con stato. Questo perché il servizio di bilanciamento del carico non sarà in grado di individuare e instradare il traffico alla replica del servizio con stato appropriata.

Servizi ASP.NET Core senza stato esposti all'esterno

Kestrel è il server Web consigliato per i servizi front-end che espongono endpoint HTTP esterni con connessione Internet. In Windows, HTTP.sys può fornire funzionalità di condivisione delle porte, che consente di ospitare più servizi Web nello stesso set di nodi usando la stessa porta. In questo scenario, i servizi Web vengono differenziati in base al nome host o al percorso, senza basarsi su un proxy front-end o un gateway per fornire il routing HTTP.

Se esposto a Internet, un servizio senza stato deve usare un endpoint noto e stabile raggiungibile tramite un servizio di bilanciamento del carico. Questo URL verrà fornito agli utenti dell'applicazione. È consigliabile la configurazione seguente:

Tipo Recommendation Note
Server Web Kestrel Kestrel è il server Web preferito, perché è supportato in Windows e Linux.
Configurazione delle porte static È necessario configurare una porta statica nota nella configurazione Endpoints di ServiceManifest.xml, ad esempio 80 per HTTP o 443 per HTTPS.
ServiceFabricIntegrationOptions Nessuno Usare l'opzione quando si configura il ServiceFabricIntegrationOptions.None middleware di integrazione di Service Fabric, in modo che il servizio non tenti di convalidare le richieste in ingresso per un identificatore univoco. Gli utenti esterni dell'applicazione non conoscono le informazioni di identificazione univoche usate dal middleware.
Conteggio istanze -1 Nei casi d'uso tipici, l'impostazione del conteggio delle istanze deve essere impostata su -1. Questa operazione viene eseguita in modo che un'istanza sia disponibile in tutti i nodi che ricevono il traffico da un servizio di bilanciamento del carico.

Se più servizi esposti esternamente condividono lo stesso set di nodi, è possibile usare HTTP.sys con un percorso URL univoco ma stabile. A tale scopo, è possibile modificare l'URL fornito durante la configurazione di IWebHost. Si noti che questo vale solo per HTTP.sys.

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

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

Servizio ASP.NET Core senza stato solo interno

I servizi senza stato che vengono chiamati solo dall'interno del cluster devono usare URL univoci e porte assegnate dinamicamente per assicurare la cooperazione tra più servizi. È consigliabile la configurazione seguente:

Tipo Recommendation Note
Server Web Kestrel Sebbene sia possibile usare HTTP.sys per i servizi interni senza stato, Kestrel è il server migliore per consentire a più istanze del servizio di condividere un host.
Configurazione delle porte assegnate in modo dinamico Più repliche di un servizio con stato potrebbero condividere un processo host o un sistema operativo host e quindi richiederanno porte univoche.
ServiceFabricIntegrationOptions UseUniqueServiceUrl Con l'assegnazione dinamica delle porte, questa impostazione evita il problema di errata identificazione descritto in precedenza.
InstanceCount any Il numero di istanze può essere impostato su qualsiasi valore necessario per il funzionamento del servizio.

Servizio ASP.NET Core con stato solo interno

I servizi con stato che vengono chiamati solo dall'interno del cluster devono usare porte assegnate dinamicamente per assicurare la cooperazione tra più servizi. È consigliabile la configurazione seguente:

Tipo Recommendation Note
Server Web Kestrel non HttpSysCommunicationListener è progettato per l'uso da parte di servizi con stato in cui le repliche condividono un processo host.
Configurazione delle porte assegnate in modo dinamico Più repliche di un servizio con stato potrebbero condividere un processo host o un sistema operativo host e quindi richiederanno porte univoche.
ServiceFabricIntegrationOptions UseUniqueServiceUrl Con l'assegnazione dinamica delle porte, questa impostazione evita il problema di errata identificazione descritto in precedenza.

Passaggi successivi

Debug dell'applicazione di Service Fabric mediante Visual Studio