Condividi tramite


ASP.NET Core in Reliable Services di Azure Service Fabric

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 dettagliata per l'hosting dei servizi ASP.NET Core in Reliable Services di Service Fabric mediante 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.

La parte restante di questo articolo presuppone che si conosca il funzionamento di ASP.NET Core. In caso contrario, leggere i concetti fondamentali di ASP.NET Core.

ASP.NET Core nell'ambiente Service Fabric

Le app ASP.NET Core e Service Fabric possono essere eseguite in .NET Core o .NET Framework completa. È possibile usare ASP.NET Core in due modi diversi in Service Fabric:

  • Ospitato come eseguibile guest. Questa modalità viene usata principalmente per eseguire applicazioni ASP.NET Core esistenti in Service Fabric senza apportare modifiche al codice.
  • Eseguire in un servizio affidabile. Questa modalità offre una migliore integrazione con il runtime di Service Fabric e consente servizi ASP.NET Core con stato.

Il resto di questo articolo illustra come usare ASP.NET Core in un servizio affidabile usando 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'autore del servizio è il proprietario del processo host del servizio, che verrà attivato e monitorato automaticamente da Service Fabric.

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 affidabile, è necessario poter 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 affidabile

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 ASP.NET Core in a process

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. Il WebHost deve essere creato in un servizio affidabile. Nel processo host del servizio, le istanze e/o le repliche del servizio possono attraversare 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 pacchetti NuGet Microsoft.ServiceFabric.AspNetCore.* contengono implementazioni di ICommunicationListener che avviano e gestiscono WebHost ASP.NET Core per Kestrel o HTTP.sys in un servizio affidabile.

Diagram for hosting ASP.NET Core in a reliable service

ICommunicationListeners ASP.NET Core

Le implementazioni ICommunicationListener per Kestrel e HTTP.sys nei pacchetti NuGet Microsoft.ServiceFabric.AspNetCore.* hanno modelli di utilizzo simili. Ma 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 configurazione Endpoint in ServiceManifest.xml. È la differenza principale tra i due listener di comunicazione. HTTP.sys richiede una configurazione Endpoint, mentre Kestrel no.
  • Func<string, AspNetCoreCommunicationListener, IWebHost> build: espressione lambda implementata in cui viene creato e restituito un elemento IWebHost. È così possibile configurare IWebHost come si farebbe normalmente in un'applicazione ASP.NET Core. L'espressione lambda fornisce un URL che viene generato automaticamente a seconda delle opzioni di integrazione di Service Fabric usate e della configurazione Endpoint specificata. È quindi possibile modificare o usare tale URL per avviare il server Web.

Middleware di integrazione di Service Fabric

Il pacchetto NuGet Microsoft.ServiceFabric.AspNetCore include il metodo di estensione UseServiceFabricIntegration in IWebHostBuilder che aggiunge middleware compatibile con Service Fabric. Questo middleware configura Kestrel o HTTP.sys ICommunicationListener per registrare un URL univoco del servizio 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. Questo perché, in un ambiente host condiviso, ad esempio Service Fabric, più applicazioni Web possono essere eseguite nella stessa macchina fisica o virtuale, 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 è stata avviata in 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 delle applicazioni assegnate dinamicamente, una replica del servizio potrebbe usare per coincidenza lo stesso endpoint IP:porta di un altro servizio che si trovava precedentemente nello stesso computer fisico o nella stessa macchina virtuale. 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 al servizio B e non rileva di essersi connesso al servizio sbagliato.

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 Naming 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 offre l'autenticazione sicura dei servizi in un ambiente di tenant ostili.

In un ambiente attendibile, il middleware aggiunto dal metodo UseServiceFabricIntegration 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 - Non più disponibile.

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

I servizi che usano una porta fissa univoca non hanno questo problema in un ambiente collaborativo. Una porta fissa univoca viene in genere usata per i servizi esterni che richiedono una porta nota per la connessione delle applicazioni client. La maggior parte delle applicazioni Web per Internet, ad esempio, usa la porta 80 o 443 per connessioni tramite Web browser. In questo caso, l'identificatore univoco non deve essere abilitato.

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

Service Fabric ASP.NET Core integration

Le implementazioni ICommunicationListener di Kestrel e HTTP.sys usano questo meccanismo nello stesso identico modo. Anche se HTTP.sys può distinguere internamente le richieste in base a percorsi URL univoci usando la funzionalità di condivisione delle porte HTTP.sys sottostante, tale funzionalità non viene usata dall'implementazione ICommunicationListener di HTTP.sys. 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é i codici HTTP 503 e HTTP 404 sono comunemente usati per indicare altri errori.

Di conseguenza, entrambe le implementazioni ICommunicationListener di Kestrel e HTTP.sys standardizzano sul middleware fornito dal metodo di estensione UseServiceFabricIntegration. 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. HttpSysCommunicationListener consente di creare un ASP.NET Core WebHost all'interno di un servizio Reliable Services usando HTTP.sys come server Web.

HTTP.sys si basa sull'API server HTTP di Windows. Questa API usa il driver kernel HTTP.sys per elaborare le richieste HTTP e indirizzarle ai processi che eseguono applicazioni Web. Ciò consente a più processi nello stesso computer fisico o nella stessa macchina virtuale di ospitare applicazioni Web sulla stessa porta, identificate senza ambiguità dal nome host o dal percorso URL univoco. Queste funzionalità sono utili in Service Fabric per ospitare più siti Web nello stesso cluster.

Nota

L'implementazione di HTTP.sys funziona solo sulla piattaforma Windows.

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

HTTP.sys diagram

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 non è attualmente progettato per l'uso in servizi con stato, a causa di problemi con la funzionalità di condivisione delle porte HTTP.sys sottostante. Per altre informazioni, vedere la sezione seguente sull'allocazione dinamica delle porte con HTTP.sys. Per i servizi con stato, Kestrel è il server Web consigliato.

Configurazione endpoint

È necessaria una configurazione Endpoint per i server Web che usano l'API server HTTP di Windows, tra cui HTTP.sys. I server Web che usano l'API server HTTP di Windows devono prima riservare i rispettivi URL con HTTP.sys. Questa operazione viene in genere 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 proprietà Protocol della configurazione Endpoint in ServiceManifest.xml vengono usate specificamente per indicare al runtime di Service Fabric di registrare un URL con http.sys per conto dell'utente con il prefisso URL con caratteri jolly complessi. A tale scopo, usare il prefisso URL caratteri jolly sicuri.

Ad esempio, per riservare http://+:80 per 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 conHTTP.sys, specificare il numero della porta nella configurazione Endpoint:

  <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 proprietà Port nella configurazione Endpoint:

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

Una porta dinamica allocata da una configurazione Endpoint 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 di essi condividerà la stessa porta quando viene allocata tramite la configurazione Endpoint. Diverse istanze di HTTP.sys possono condividere una porta usando la funzionalità di condivisione delle porte HTTP.sys sottostante. Ma non è supportato da HttpSysCommunicationListener a causa delle complicazioni introdotte per le richieste client. Per l'uso delle porte dinamiche, Kestrel è il server Web consigliato.

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. KestrelCommunicationListener consente di creare un ASP.NET Core WebHost all'interno di un servizio Reliable Services 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. Inoltre, a differenza di HTTP.sys, Kestrel non supporta la condivisione delle porte tra più processi. Ogni istanza di Kestrel deve usare una porta univoca. Per altre informazioni su Kestrel, vedere Dettagli implementazione.

Kestrel diagram

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. Questa operazione non è strettamente necessaria, 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 ServiceInstanceListener perché usi un endpoint EndpointHttps e sia in ascolto su una porta specifica (ad esempio la porta 443). Quando si configura l'host Web per l'uso del server Web Kestrel, è necessario configurare Kestrel per l'ascolto di indirizzi IPv6 su 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'esercitazione in cui venga usato un esempio completo, vedere Configurare Kestrel per l'uso di HTTPS.

Configurazione endpoint

Non è necessaria una configurazione Endpoint per usare Kestrel.

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

Usare Kestrel con una porta statica

È possibile definire una porta statica nella configurazione Endpoint di ServiceManifest.xml per l'uso con Kestrel. Anche se questo non è strettamente necessario, offre due potenziali vantaggi:

  • Se la porta non rientra nell'intervallo di porte dell'applicazione, verrà aperta da Service Fabric nel firewall del sistema operativo.
  • 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 configurazione di Endpoint, omettere il nome nel costruttore KestrelCommunicationListener. In questo caso, userà una porta dinamica. Per altre informazioni su questo argomento, vedere la sezione successiva.

Usare Kestrel con una porta dinamica

Kestrel non può usare l'assegnazione automatica della porta dalla configurazione Endpoint in ServiceManifest.xml. Questo perché l'assegnazione automatica delle porte di una configurazione Endpoint 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 delle porte con Kestrel omettere del tutto la configurazione Endpoint in ServiceManifest.xml e non passare un nome endpoint al costruttore KestrelCommunicationListener, come segue:

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

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

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

Integrazione di hosting minimo e IHost

Oltre a IWebHost/IWebHostBuilder KestrelCommunicationListener e HttpSysCommunicationListener supportano la compilazione di servizi di base ASP.NET tramite IHost/IHostBuilder. È disponibile a partire dalla versione 5.2.1363 di pacchetti Microsoft.ServiceFabric.AspNetCore.Kestrel e Microsoft.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();
            }))
    };
}

Nota

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

ASP.NET 6 ha introdotto il modello di hosting minimo, che è un modo più semplificato e semplificato di 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. Per ulteriori informazioni sul supporto generale della configurazione di ASP.NET Core, vedere Configurazione in ASP.NET Core.

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

Estensioni di avvio addServiceFabricConfiguration

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

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 anche ServiceFabricConfigurationOptions per modificare il comportamento predefinito del mapping delle chiavi.

Impostazioni crittografate

Service Fabric supporta le impostazioni crittografate, come 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 invece archiviati in questa posizione. Tuttavia, se si vuole decrittografare il valore da archiviare in ASP.NET Core IConfiguration, è possibile impostare il flag DecryptValue su false nell'estensione AddServiceFabricConfiguration, 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 flag IncludePackageName 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 di 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 configurazione core 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 Opzioni su ASP.NET Core.

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 varie impostazioni consigliate per risolvere i problemi relativi agli scenari seguenti:

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

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

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

Nota

Gli endpoint di servizio con stato non devono essere in genere esposti a Internet. I cluster dietro i servizi di bilanciamento del carico che non sono a conoscenza della risoluzione del servizio di Service Fabric, ad esempio Azure Load Balancer, non potranno esporre 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 Internet esterni. 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 sono differenziati in base al nome host o al percorso, senza basarsi su un proxy front-end o un gateway per fornire il routing HTTP.

Quando è 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:

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

Se più servizi esposti all'esterno condividono lo stesso set di nodi, è possibile usare HTTP.sys con un percorso URL univoco ma stabile. È possibile farlo modificando l'URL specificato 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:

Type Elemento consigliato 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 della porta assegnate in modo dinamico Repliche multiple di un servizio con stato potrebbero condividere un processo host o un sistema operativo host e richiedono quindi porte univoche.
ServiceFabricIntegrationOptions UseUniqueServiceUrl Con l'assegnazione dinamica delle porte, questa impostazione evita il problema di errata identificazione descritto in precedenza.
InstanceCount qualsiasi 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:

Type Elemento consigliato Note
Server Web Kestrel HttpSysCommunicationListener non può essere usato dai servizi con stato in cui le repliche condividono un processo host.
Configurazione della porta assegnate in modo dinamico Repliche multiple di un servizio con stato potrebbero condividere un processo host o un sistema operativo host e richiedono quindi 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