ASP.NET Core nos Serviços Confiáveis do Azure Service Fabric

ASP.NET Core é uma estrutura de código aberto e multiplataforma. Essa estrutura foi projetada para criar aplicativos conectados à Internet baseados em nuvem, como aplicativos Web, aplicativos IoT e back-ends móveis.

Este artigo é um guia detalhado para hospedar serviços ASP.NET Core nos Serviços Fiáveis do Serviço Fabric usando o conjunto de pacotes NuGet Microsoft.ServiceFabric.AspNetCore.

Para obter um tutorial introdutório sobre o ASP.NET Core no Service Fabric e instruções sobre como configurar seu ambiente de desenvolvimento, consulte Tutorial: Criar e implantar um aplicativo com um serviço front-end da API Web ASP.NET Core e um serviço back-end com monitoração de estado.

O restante deste artigo pressupõe que você já esteja familiarizado com ASP.NET Core. Se não, leia os fundamentos do ASP.NET Core.

ASP.NET Core no ambiente do Service Fabric

Os aplicativos ASP.NET Core e Service Fabric podem ser executados no .NET Core ou no .NET Framework completo. Você pode usar o ASP.NET Core de duas maneiras diferentes no Service Fabric:

  • Hospedado como um executável convidado. Essa maneira é usada principalmente para executar aplicativos ASP.NET Core existentes no Service Fabric sem alterações de código.
  • Execute dentro de um serviço confiável. Dessa forma, permite uma melhor integração com o tempo de execução do Service Fabric e permite serviços ASP.NET Core com monitoração de estado.

O restante deste artigo explica como usar o ASP.NET Core dentro de um serviço confiável, por meio dos componentes de integração do ASP.NET Core fornecidos com o SDK do Service Fabric.

Hospedagem de serviço do Service Fabric

No Service Fabric, uma ou mais instâncias e/ou réplicas do serviço são executadas em um processo de host de serviço: um arquivo executável que executa o código de serviço. Você, como autor de serviço, é o proprietário do processo de host de serviço e o Service Fabric o ativa e monitora para você.

O ASP.NET tradicional (até MVC 5) está fortemente acoplado ao IIS através do System.Web.dll. ASP.NET Core fornece uma separação entre o servidor Web e seu aplicativo Web. Esta separação permite que as aplicações Web sejam portáteis entre diferentes servidores Web. Ele também permite que os servidores web sejam auto-hospedados. Isso significa que você pode iniciar um servidor Web em seu próprio processo, em oposição a um processo que pertence a um software de servidor Web dedicado, como o IIS.

Para combinar um serviço do Service Fabric e ASP.NET, tanto como executável de convidado quanto como serviço fiável, é necessário ser capaz de iniciar ASP.NET dentro do processo do anfitrião de serviço. ASP.NET auto-hospedagem Core permite que você faça isso.

Hospedagem ASP.NET Core em um serviço confiável

Normalmente, os aplicativos ASP.NET Core auto-hospedados criam um WebHost no ponto de entrada de um aplicativo, como o static void Main() método em Program.cs. Neste caso, o ciclo de vida do WebHost está vinculado ao ciclo de vida do processo.

Hospedagem do ASP.NET Core num processo

Mas o ponto de entrada do aplicativo não é o lugar certo para criar um WebHost em um serviço confiável. Isso ocorre porque o ponto de entrada do aplicativo é usado apenas para registrar um tipo de serviço com o tempo de execução do Service Fabric, para que ele possa criar instâncias desse tipo de serviço. O WebHost deve ser criado em um serviço confiável em si. Dentro do processo de host de serviço, as instâncias de serviço e/ou réplicas podem passar por vários ciclos de vida.

Uma instância de Serviço Confiável é representada pela sua classe de serviço derivada de StatelessService ou StatefulService. A pilha de comunicação para um serviço está contida numa ICommunicationListener implementação na sua classe de serviço. Os Microsoft.ServiceFabric.AspNetCore.* pacotes NuGet contêm implementações que ICommunicationListener iniciam e gerenciam o ASP.NET Core WebHost para Kestrel ou HTTP.sys em um serviço confiável.

Diagrama para hospedar ASP.NET Core em um serviço confiável

ASP.NET Core ICommunicationListeners

As ICommunicationListener implementações para Kestrel e HTTP.sys nos Microsoft.ServiceFabric.AspNetCore.* pacotes do NuGet têm padrões de uso semelhantes. Mas eles executam ações ligeiramente diferentes específicas para cada servidor web.

Ambos os ouvintes de comunicação fornecem um construtor que usa os seguintes argumentos:

  • ServiceContext serviceContext: Este é o ServiceContext objeto que contém informações sobre o serviço em execução.
  • string endpointName: Este é o nome de uma Endpoint configuração no ServiceManifest.xml. É principalmente onde os dois ouvintes de comunicação diferem. HTTP.sys requer uma Endpoint configuração, enquanto o Kestrel não.
  • Func<string, AspNetCoreCommunicationListener, IWebHost> build: Esta é uma função lambda que você próprio implementa, na qual cria e retorna um IWebHost. Ele permite que você configure IWebHost da maneira que normalmente faria em um aplicativo ASP.NET Core. O lambda gera uma URL para si, dependendo das opções de integração do Service Fabric que utiliza e da configuração Endpoint que fornece. Em seguida, você pode modificar ou usar essa URL para iniciar o servidor Web.

Middleware de integração do Service Fabric

O pacote NuGet Microsoft.ServiceFabric.AspNetCore inclui o método de extensão UseServiceFabricIntegration em IWebHostBuilder que adiciona um middleware compatível com o Service Fabric. Esse middleware configura o Kestrel ou HTTP.sys ICommunicationListener para registrar uma URL de serviço exclusiva com o Service Fabric Naming Service. Em seguida, ele valida as solicitações do cliente para garantir que os clientes estejam se conectando ao serviço certo.

Esta etapa é necessária para evitar que os clientes se conectem erroneamente ao serviço errado. Isso porque, em um ambiente de host compartilhado, como o Service Fabric, vários aplicativos Web podem ser executados na mesma máquina física ou virtual, mas não usam nomes de host exclusivos. Esse cenário é descrito com mais detalhes na próxima seção.

Um caso de identidade errada

As réplicas de serviço, independentemente do protocolo, escutam em uma combinação exclusiva de IP:port. Assim que uma réplica de serviço começa a escutar num ponto de extremidade de IP:porta, comunica esse endereço de ponto de extremidade ao Serviço de Nomenclatura do Service Fabric. Lá, clientes ou outros serviços podem descobri-lo. Se os serviços usarem portas de aplicativo atribuídas dinamicamente, uma réplica de serviço pode, coincidentemente, usar o mesmo ponto de extremidade IP:port de outro serviço anteriormente na mesma máquina física ou virtual. Isso pode fazer com que um cliente se conecte por engano ao serviço errado. Esse cenário pode resultar se a seguinte sequência de eventos ocorrer:

  1. O serviço A escuta em 10.0.0.1:30000 via HTTP.
  2. O cliente resolve o Serviço A e obtém o endereço 10.0.0.1:30000.
  3. O serviço A é movido para um nó diferente.
  4. O serviço B é colocado em 10.0.0.1 e, coincidentemente, usa a mesma porta 30000.
  5. O cliente tenta se conectar ao serviço A com o endereço em cache 10.0.0.1:30000.
  6. O cliente agora está conectado com sucesso ao serviço B, sem perceber que está conectado ao serviço errado.

Isso pode causar bugs em momentos aleatórios que podem ser difíceis de diagnosticar.

Usando URLs de serviço exclusivas

Para evitar esses bugs, os serviços podem publicar um endpoint no Serviço de Nomenclatura com um identificador único e, em seguida, validar esse identificador único durante as solicitações do cliente. Esta é uma ação cooperativa entre serviços num ambiente de confiança não hostil ao locatário. Ele não fornece autenticação de serviço segura em um ambiente de locatário hostil.

Em um ambiente confiável, o middleware adicionado pelo UseServiceFabricIntegration método acrescenta automaticamente um identificador exclusivo ao endereço postado no Serviço de Nomenclatura. Ele valida esse identificador em cada solicitação. Se o identificador não corresponder, o middleware retornará imediatamente uma resposta HTTP 410 Desaparecido.

Os serviços que usam uma porta atribuída dinamicamente devem usar esse middleware.

Os serviços que usam uma porta fixa exclusiva não têm esse problema em um ambiente cooperativo. Uma porta fixa exclusiva é normalmente usada para serviços externos que precisam de uma porta bem conhecida para os aplicativos cliente se conectarem. Por exemplo, a maioria dos aplicativos Web voltados para a Internet usará a porta 80 ou 443 para conexões de navegador da Web. Nesse caso, o identificador exclusivo não deve ser habilitado.

O diagrama a seguir mostra o fluxo de solicitação com o middleware habilitado:

Integração do Service Fabric ASP.NET Core

Tanto o Kestrel quanto as implementações HTTP.sys ICommunicationListener usam esse mecanismo exatamente da mesma maneira. Embora HTTP.sys possa diferenciar internamente solicitações com base em caminhos de URL exclusivos usando o recurso de compartilhamento de porta HTTP.sys subjacente, essa funcionalidade não é usada pela implementação HTTP.sys ICommunicationListener . Isso porque resulta em códigos de status de erro HTTP 503 e HTTP 404 no cenário descrito anteriormente. Isso, por sua vez, torna difícil para os clientes determinar a intenção do erro, já que HTTP 503 e HTTP 404 são comumente usados para indicar outros erros.

Assim, tanto o Kestrel quanto as implementações HTTP.sys ICommunicationListener padronizam o middleware fornecido pelo método de UseServiceFabricIntegration extensão. Portanto, os clientes só precisam executar uma ação de re-resolução do ponto de extremidade do serviço em respostas HTTP 410.

HTTP.sys em Serviços de Confiança

Você pode usar HTTP.sys em Serviços Confiáveis importando o pacote NuGet Microsoft.ServiceFabric.AspNetCore.HttpSys . Este pacote contém HttpSysCommunicationListener, uma implementação de ICommunicationListener. HttpSysCommunicationListener permite que você crie um ASP.NET Core WebHost dentro de um serviço confiável usando HTTP.sys como o servidor web.

HTTP.sys é criado na API do Windows HTTP Server. Essa API usa o driver do kernelHTTP.sys para processar solicitações HTTP e roteá-las para processos que executam aplicativos da Web. Isso permite que vários processos na mesma máquina física ou virtual hospedem aplicativos Web na mesma porta, desambiguados por um caminho de URL exclusivo ou nome de host. Esses recursos são úteis no Service Fabric para hospedar vários sites no mesmo cluster.

Observação

A implementação HTTP.sys funciona apenas na plataforma Windows.

O diagrama a seguir ilustra como HTTP.sys usa o driver do kernelHTTP.sys no Windows para compartilhamento de portas:

HTTP.sys diagrama

HTTP.sys em serviço sem estado

Para usar HttpSys em um serviço sem estado, sobrescreva o método CreateServiceInstanceListeners e retorne uma instância 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 em um serviço que guarda estado

HttpSysCommunicationListener não foi projetado atualmente para uso em serviços com estado devido a complicações com a funcionalidade subjacente de compartilhamento de porta HTTP.sys. Para obter mais informações, consulte a seção a seguir sobre alocação dinâmica de portas com HTTP.sys. Para serviços com estado, Kestrel é o servidor web sugerido.

Configuração do ponto final

Uma Endpoint configuração é necessária para servidores Web que usam a API do Windows HTTP Server, incluindo HTTP.sys. Os servidores Web que usam a API do Windows HTTP Server devem primeiro reservar sua URL com HTTP.sys (isso normalmente é feito com a ferramenta netsh ).

Essa ação requer privilégios elevados que seus serviços não têm por padrão. As opções "http" ou "https" para a propriedade Protocol da configuração em ServiceManifest.xml são usadas especificamente para instruir o runtime do Service Fabric a registrar uma URL com HTTP.sys em seu favor. Ele faz isso usando o prefixo URL de coringa forte.

Por exemplo, para reservar um serviço com http://+:80, utilize a seguinte configuração em ServiceManifest.xml:

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

</ServiceManifest>

E o nome do ponto de extremidade deve ser passado para o HttpSysCommunicationListener construtor:

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

Utilizar HTTP.sys com uma porta estática

Para usar uma porta estática com o HTTP.sys, forneça o número da porta na configuração Endpoint.

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

Usar HTTP.sys com uma porta dinâmica

Para usar uma porta atribuída dinamicamente com HTTP.sys, omita a propriedade Port na configuração Endpoint.

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

Uma porta dinâmica alocada por uma Endpoint configuração fornece apenas uma porta por processo de host. O modelo de hospedagem atual do Service Fabric permite que várias instâncias de serviço e/ou réplicas sejam hospedadas no mesmo processo. Isso significa que cada um compartilhará a mesma porta quando alocado através da Endpoint configuração. Várias instâncias HTTP.sys podem compartilhar uma porta usando o recurso de compartilhamento de portaHTTP.sys subjacente. Mas não é suportado por HttpSysCommunicationListener devido às complicações que introduz para os pedidos dos clientes. Para o uso dinâmico da porta, o Kestrel é o servidor web sugerido.

Peneireiro-das-torres em Serviços de Confiança

Você pode usar o Kestrel em Serviços Confiáveis importando o pacote NuGet Microsoft.ServiceFabric.AspNetCore.Kestrel . Este pacote contém KestrelCommunicationListener, uma implementação de ICommunicationListener. KestrelCommunicationListener permite que você crie um ASP.NET Core WebHost dentro de um serviço confiável usando Kestrel como o servidor web.

Kestrel é um servidor web multi-plataforma para ASP.NET Core. Ao contrário HTTP.sys, o Kestrel não usa um gerenciador de pontos finais centralizado. Também ao contrário do HTTP.sys, o Kestrel não suporta o compartilhamento de portas entre vários processos. Cada instância do Kestrel deve utilizar uma porta única. Para mais informações sobre o Kestrel, consulte os Detalhes da implementação.

Diagrama de peneireiro

Peneireiro-das-torres num serviço apátrida

Para usar Kestrel num serviço sem estado, substitua o método CreateServiceInstanceListeners e retorne uma instância 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();
            ))
    };
}

Peneireiro-das-torres num serviço com estado

Para usar Kestrel num serviço stateful, substitua o método CreateServiceReplicaListeners e retorne uma instância 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();
            ))
    };
}

Neste exemplo, uma instância singleton de IReliableStateManager é fornecida ao contentor de injeção de dependência WebHost. Isso não é estritamente necessário, mas permite o uso de IReliableStateManager e Coleções Fiáveis nos métodos de ação do controlador MVC.

Um Endpoint nome de configuração não é fornecido a KestrelCommunicationListener em um serviço com estado. Isso é explicado com mais detalhes na seção a seguir.

Configurar o Kestrel para usar HTTPS

Ao ativar o HTTPS com o Kestrel em seu serviço, você precisará definir várias opções de audição. Atualize ServiceInstanceListener para usar um ponto de extremidade EndpointHttps e escutar numa porta específica (como a porta ServiceInstanceListener 443). Ao configurar o host para usar o servidor web Kestrel, você deve configurar o Kestrel para ouvir endereços IPv6 em todas as interfaces de rede:

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

Para obter um exemplo completo em um tutorial, consulte Configurar o Kestrel para usar HTTPS.

Configuração do ponto final

Uma Endpoint configuração não é necessária para usar o Kestrel.

Kestrel é um simples servidor web autônomo. Ao contrário do HTTP.sys (ou HttpListener), ele não precisa de uma Endpoint configuração no ServiceManifest.xml porque não requer registro de URL antes de iniciar.

Use o peneireiro-das-torres com uma porta estática

Você pode configurar uma porta estática na configuração de Endpoint do ServiceManifest.xml para uso com o Kestrel. Embora isso não seja estritamente necessário, oferece dois benefícios potenciais:

  • Se a porta não estiver no intervalo de portas do aplicativo, ela será aberta através do firewall do sistema operacional pelo Service Fabric.
  • A URL fornecida a si através de KestrelCommunicationListener usará essa porta.
  <Resources>
    <Endpoints>
      <Endpoint Protocol="http" Name="ServiceEndpoint" Port="80" />
    </Endpoints>
  </Resources>

Se um Endpoint estiver configurado, seu nome deve ser passado para o KestrelCommunicationListener construtor:

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

Se ServiceManifest.xml não usar a configuração Endpoint, omita o nome no construtor KestrelCommunicationListener. Nesse caso, ele usará uma porta dinâmica. Consulte a próxima seção para obter mais informações sobre isso.

Utilize o Kestrel com uma porta dinâmica

Kestrel não pode usar a atribuição automática de porta a partir da configuração Endpoint no ServiceManifest.xml. Isso ocorre porque a atribuição automática de porta de uma Endpoint configuração atribui uma porta exclusiva por processo de host, e um único processo de host pode conter várias instâncias Kestrel. Isso não funciona com o Kestrel porque não suporta compartilhamento de portas. Portanto, cada instância do Kestrel deve ser aberta numa porta única.

Para usar a atribuição de porta dinâmica com Kestrel, omita totalmente a Endpoint configuração em ServiceManifest.xml e não passe um nome de ponto de extremidade para o KestrelCommunicationListener construtor, da seguinte maneira:

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

Nessa configuração, KestrelCommunicationListener selecionará automaticamente uma porta não utilizada no intervalo de portas do aplicativo.

Para HTTPS, é necessário que o endpoint esteja configurado com o protocolo HTTPS sem uma porta especificada no ServiceManifest.xml e que o nome do ponto de extremidade seja passado para o construtor KestrelCommunicationListener.

Integração IHost e Minimal Hosting

Além de IWebHost/IWebHostBuilder, KestrelCommunicationListener e HttpSysCommunicationListener oferecem suporte à criação de serviços ASP.NET Core usando IHost/IHostBuilder. Os pacotes Microsoft.ServiceFabric.AspNetCore.Kestrel e Microsoft.ServiceFabric.AspNetCore.HttpSys estão disponíveis a partir da versão v5.2.1363.

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

Observação

Como KestrelCommunicationListener e HttpSysCommunicationListener se destinam a serviços Web, é necessário registrar/configurar um servidor Web (usando o método ConfigureWebHostDefaults ou ConfigureWebHost ) sobre o IHost

ASP.NET 6 introduziu o modelo de Hospedagem Mínima, que é uma maneira mais simplificada e simplificada de criar aplicações web. Modelo de hospedagem mínima também pode ser usado com 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;
            }))
    };
}

Provedor de configuração do Service Fabric

A configuração do aplicativo no ASP.NET Core é baseada em pares chave-valor estabelecidos pelo provedor de configuração. Leia Configuração no ASP.NET Core para entender mais sobre o suporte geral à configuração do ASP.NET Core.

Esta seção descreve como o provedor de configuração do Service Fabric se integra à configuração ASP.NET Core importando o Microsoft.ServiceFabric.AspNetCore.Configuration pacote NuGet.

Extensões de inicialização do AddServiceFabricConfiguration

Depois de importar o Microsoft.ServiceFabric.AspNetCore.Configuration pacote NuGet, é necessário registrar a origem de Configuração do Service Fabric com a API de configuração do ASP.NET Core. Para fazer isso, verifique as extensões AddServiceFabricConfiguration no namespace, comparando-as em relação a 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; }

Agora, o serviço ASP.NET Core pode acessar as definições de configuração do Service Fabric, assim como qualquer outra configuração de aplicativo. Por exemplo, podes usar o padrão de opções para carregar configurações em objetos com tipagem forte.

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

Mapeamento de chave padrão

Por padrão, o provedor de configuração do Service Fabric inclui o nome do pacote, o nome da seção e o nome da propriedade. Juntos, eles formam a chave de configuração ASP.NET Core, da seguinte maneira:

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

Por exemplo, se você tiver um pacote de configuração nomeado MyConfigPackage com o seguinte conteúdo, o valor de configuração estará disponível no ASP.NET Core IConfiguration por meio de 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>

Opções de configuração do Service Fabric

O provedor de configuração do Service Fabric também oferece suporte ServiceFabricConfigurationOptions para alterar o comportamento padrão do mapeamento de chaves.

Configurações criptografadas

O Service Fabric oferece suporte a configurações criptografadas, assim como o provedor de configuração do Service Fabric. As configurações criptografadas não são descriptografadas para o ASP.NET Core IConfiguration por padrão. Em vez disso, os valores criptografados são armazenados lá. Mas se quiser desencriptar o valor a armazenar no ASP.NET Core IConfiguration, pode definir o sinalizador DecryptValue como false na extensão, da AddServiceFabricConfiguration seguinte forma:

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

Vários pacotes de configuração

O Service Fabric suporta vários pacotes de configuração. Por padrão, o nome do pacote é incluído na chave de configuração. Mas você pode definir o sinalizador IncludePackageName como false, da seguinte maneira:

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

Mapeamento de chave personalizado, extração de valor e população de dados

O fornecedor de configuração do Service Fabric também suporta cenários mais avançados, permitindo personalizar o mapeamento de chaves com ExtractKeyFunc e extrair valores de forma personalizada com ExtractValueFunc. Você pode até mesmo alterar todo o processo de configuração de dados da configuração do Service Fabric para a configuração do ASP.NET Core usando ConfigAction.

Os exemplos a seguir ilustram como usar ConfigAction para personalizar o preenchimento de dados:

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

Atualizações de configuração

O provedor de configuração do Service Fabric também oferece suporte a atualizações de configuração. Você pode usar o ASP.NET Core IOptionsMonitor para receber notificações de alteração e, em seguida, usar IOptionsSnapshot para recarregar dados de configuração. Para obter mais informações, consulte ASP.NET Opções principais.

Essas opções são suportadas por padrão. Nenhuma codificação adicional é necessária para habilitar as atualizações de configuração.

Cenários e configurações

Esta seção fornece a combinação de servidor Web, configuração de porta, opções de integração do Service Fabric e configurações diversas que recomendamos para solucionar os seguintes cenários:

  • Serviços sem estado ASP.NET Core expostos externamente
  • Serviços apenas internos sem estado do ASP.NET Core
  • Serviços com estado ASP.NET Core exclusivos para uso interno

Um serviço exposto externamente é aquele que expõe um endpoint que é chamado a partir de fora do cluster, geralmente através de um balanceador de carga.

Um serviço somente interno é aquele cujo ponto de extremidade é chamado apenas de dentro do cluster.

Observação

Os endpoints de serviços com estado geralmente não devem ser expostos à internet. Os clusters por trás de balanceadores de carga que não estão cientes da resolução de serviços do Service Fabric, como o Azure Load Balancer, não poderão expor serviços com estado. Isso ocorre porque o balanceador de carga não poderá localizar e rotear o tráfego para a réplica de serviço stateful apropriada.

Serviços sem estado do ASP.NET Core expostos externamente

Kestrel é o servidor Web sugerido para serviços front-end que expõem pontos de extremidade HTTP externos voltados para a Internet. No Windows, HTTP.sys pode fornecer capacidade de compartilhamento de porta, o que permite hospedar vários serviços Web no mesmo conjunto de nós usando a mesma porta. Nesse cenário, os serviços Web são diferenciados por nome de host ou caminho, sem depender de um proxy front-end ou gateway para fornecer roteamento HTTP.

Quando exposto à internet, um serviço sem estado deve utilizar um endpoint conhecido e estável, acessível através de um balanceador de carga. Você fornecerá essa URL aos usuários do seu aplicativo. Recomendamos a seguinte configuração:

Tipo Recomendação Observações
Servidor Web Peneireiro Kestrel é o servidor web preferido, pois é suportado em Windows e Linux.
Configuração da porta estática Uma porta estática bem conhecida deve ser configurada na Endpoints configuração de ServiceManifest.xml, como 80 para HTTP ou 443 para HTTPS.
ServiceFabricIntegrationOptions Nenhum Use a opção ServiceFabricIntegrationOptions.None ao configurar o middleware de integração do Service Fabric, para que o serviço não tente validar solicitações de entrada para um identificador exclusivo. Os usuários externos do seu aplicativo não saberão as informações de identificação exclusivas que o middleware usa.
Contagem de instâncias -1 Em casos de uso típicos, a configuração de contagem de instâncias deve ser definida como -1. Isso é feito para que uma instância esteja disponível em todos os nós que recebem tráfego de um balanceador de carga.

Se vários serviços expostos externamente compartilharem o mesmo conjunto de nós, poderá usar HTTP.sys com um caminho de URL exclusivo, mas estável. Você pode fazer isso modificando a URL fornecida ao configurar o IWebHost. Observe que isso se aplica apenas a HTTP.sys.

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

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

Serviço ASP.NET Core sem estado de uso exclusivo interno

Os serviços sem estado que são chamados apenas de dentro do cluster devem usar URLs exclusivas e portas atribuídas dinamicamente para garantir a cooperação entre vários serviços. Recomendamos a seguinte configuração:

Tipo Recomendação Observações
Servidor Web Peneireiro Embora possa usar HTTP.sys para serviços internos sem estado, o Kestrel é o melhor servidor para permitir que várias instâncias de serviço partilhem um host.
Configuração da porta atribuído dinamicamente Várias réplicas de um serviço stateful podem compartilhar um processo de hospedagem ou sistema operacional de hospedagem e, portanto, precisarão de portas exclusivas.
ServiceFabricIntegrationOptions UtilizarUrlDeServiçoÚnica Com a atribuição de porta dinâmica, essa configuração evita o problema de identidade equivocado descrito anteriormente.
ContagemDeInstâncias qualquer A configuração de contagem de instâncias pode ser definida como qualquer valor necessário para operar o serviço.

Serviço ASP.NET Core com estado apenas interno

Os serviços com estado que são chamados apenas de dentro do cluster devem usar portas atribuídas dinamicamente para garantir a cooperação entre vários serviços. Recomendamos a seguinte configuração:

Tipo Recomendação Observações
Servidor Web Peneireiro O HttpSysCommunicationListener não foi projetado para ser usado por serviços com estado, em que as réplicas compartilham um processo de anfitrião.
Configuração da porta atribuído dinamicamente Várias réplicas de um serviço stateful podem compartilhar um processo de hospedagem ou sistema operacional de hospedagem e, portanto, precisarão de portas exclusivas.
ServiceFabricIntegrationOptions UtilizarUrlDeServiçoÚnica Com a atribuição de porta dinâmica, essa configuração evita o problema de identidade equivocado descrito anteriormente.

Próximos passos

Depurar a sua aplicação do Service Fabric com o Visual Studio