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 ASP.NET serviços principais nos Serviços Confiáveis do Service 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 Core do ASP.NET 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, como um executável convidado ou em um serviço confiável, você deve ser capaz de iniciar ASP.NET dentro do seu processo de host 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 ASP.NET Core em um 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 em uma ICommunicationListener implementação em 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 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: Este é um lambda que você implementa, no qual você cria e retorna um IWebHostarquivo . Ele permite que você configure IWebHost da maneira que normalmente faria em um aplicativo ASP.NET Core. O lambda fornece uma URL que é gerada para você, dependendo das opções de integração do Service Fabric que você usa e da Endpoint configuração fornecida. Em seguida, você pode modificar ou usar essa URL para iniciar o servidor Web.

Middleware de integração do Service Fabric

O Microsoft.ServiceFabric.AspNetCore pacote NuGet inclui o método de extensão que IWebHostBuilder adiciona middleware compatível com o UseServiceFabricIntegration 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. Depois que uma réplica de serviço começa a escutar em um ponto de extremidade IP:port, ela relata esse endereço de ponto de extremidade para o 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 por 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 postar um ponto de extremidade no Serviço de Nomenclatura com um identificador exclusivo e, em seguida, validar esse identificador exclusivo durante as solicitações do cliente. Esta é uma ação cooperativa entre serviços em um ambiente confiável de locatário não hostil. 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 Gone.

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.sysICommunicationListener. 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 reresolução de ponto de extremidade de serviço em respostas HTTP 410.

HTTP.sys em serviços confiáveis

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 kernel HTTP.sys para processar solicitações HTTP e roteá-las para processos que executam aplicativos 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.

Nota

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

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

HTTP.sys diagrama

HTTP.sys em um serviço sem estado

Para usar HttpSys em um serviço sem monitoração de estado, substitua o CreateServiceInstanceListeners método e retorne uma HttpSysCommunicationListener instância:

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 com monitoração de estado

HttpSysCommunicationListenernão foi projetado atualmente para uso em serviços com monitoração de estado devido a complicações com o recurso de compartilhamento de porta HTTP.sys subjacente. 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 Protocol propriedade da configuração no ServiceManifest.xml são usadas especificamente para instruir o tempo de Endpoint execução do Service Fabric a registrar uma URL com HTTP.sys em seu nome. Ele faz isso usando o prefixo de URL curinga forte.

Por exemplo, para reservar http://+:80 um serviço, use a seguinte configuração no 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();
 })

Usar HTTP.sys com uma porta estática

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

  <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 Endpoint a Port propriedade na configuração:

  <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 porta HTTP.sys subjacente. Mas não é suportado devido HttpSysCommunicationListener à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 do 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 usar uma porta exclusiva. Para mais informações sobre o Kestrel, consulte os Detalhes da implementação.

Diagrama de peneireiro-das-torres

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

Para usar Kestrel em um serviço sem monitoração de estado, substitua o CreateServiceInstanceListeners método e retorne uma KestrelCommunicationListener instância:

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 stateful

Para usar Kestrel em um serviço com monitoração de estado, substitua o CreateServiceReplicaListeners método e retorne uma KestrelCommunicationListener instância:

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 é fornecida ao contêiner de injeção de dependência WebHost IReliableStateManager . Isso não é estritamente necessário, mas permite que você use IReliableStateManager coleções confiáveis em seus métodos de ação do controlador MVC.

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

Configurar o Kestrel para utilizar HTTPS

Ao ativar o HTTPS com o Kestrel em seu serviço, você precisará definir várias opções de audição. Atualize o para usar um ponto de extremidade EndpointHttps e ouvir em uma porta específica (como a ServiceInstanceListener porta 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 do ServiceManifest.xml para uso com o Endpoint 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.
  • O URL fornecido a você usará KestrelCommunicationListener 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 uma Endpoint configuração, omita o nome no KestrelCommunicationListener construtor. Nesse caso, ele usará uma porta dinâmica. Consulte a próxima seção para obter mais informações sobre isso.

Use o peneireiro-das-torres com uma porta dinâmica

O Kestrel não pode usar a atribuição automática de porta da Endpoint configuração 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 em uma porta exclusiva.

Para usar a atribuição de porta dinâmica com o Kestrel, omita totalmente a Endpoint configuração no 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, ele deve ter o Endpoint configurado com o protocolo HTTPS sem uma porta especificada em ServiceManifest.xml e passar o nome do ponto de extremidade para o construtor KestrelCommunicationListener.

Integração IHost e Minimal Hosting

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

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

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

Depois de importar o Microsoft.ServiceFabric.AspNetCore.Configuration pacote NuGet, você precisa registrar a fonte de Configuração do Service Fabric com ASP.NET API de configuração Core. Para fazer isso, verifique as extensões AddServiceFabricConfiguration no namespace em relação a IConfigurationBuilderMicrosoft.ServiceFabric.AspNetCore.Configuration .

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, você pode usar o padrão de opções para carregar configurações em objetos fortemente tipados.

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 provedor de configuração do Service Fabric também oferece suporte a cenários mais avançados para personalizar o mapeamento de chaves e ExtractKeyFunc extrair os valores com ExtractValueFunc. Você pode até mesmo alterar todo o processo de preenchimento de dados da configuração do Service Fabric para a configuração do ASP.NET Core usando ConfigActiono .

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 sem estado ASP.NET Core apenas internos
  • Serviços com monitoração de estado ASP.NET Core somente internos

Um serviço exposto externamente é aquele que expõe um ponto de extremidade que é chamado de fora do cluster, geralmente por meio de um balanceador de carga.

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

Nota

Os pontos de extremidade de serviço 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 do serviço Service Fabric, como o Azure Load Balancer, não poderão expor serviços com monitoração de estado. Isso ocorre porque o balanceador de carga não poderá localizar e rotear o tráfego para a réplica de serviço com monitoração de estado apropriada.

Serviços sem estado 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 recursos 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 usar um ponto de extremidade conhecido e estável que possa ser acessado por meio de um balanceador de carga. Você fornecerá essa URL aos usuários do seu aplicativo. Recomendamos a seguinte configuração:

Type Recomendação Notas
Servidor Web Kestrel Kestrel é o servidor web preferido, pois é suportado em Windows e Linux.
Configuração de portas estático Uma porta estática bem conhecida deve ser configurada na Endpoints configuração do ServiceManifest.xml, como 80 para HTTP ou 443 para HTTPS.
ServiceFabricIntegrationOptions Nenhuma Use a opção ao configurar o ServiceFabricIntegrationOptions.None 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, você poderá usá 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 monitoração de estado apenas 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:

Type Recomendação Notas
Servidor Web Kestrel Embora você possa usar HTTP.sys para serviços internos sem monitoração de estado, o Kestrel é o melhor servidor para permitir que várias instâncias de serviço compartilhem um host.
Configuração de portas atribuído dinamicamente Várias réplicas de um serviço com monitoração de estado podem compartilhar um processo de host ou sistema operacional de host e, portanto, precisarão de portas exclusivas.
ServiceFabricIntegrationOptions UseUniqueServiceUrl Com a atribuição de porta dinâmica, essa configuração evita o problema de identidade equivocado descrito anteriormente.
InstanceCount qualquer A configuração de contagem de instâncias pode ser definida como qualquer valor necessário para operar o serviço.

Serviço principal de ASP.NET com monitoração de 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:

Type Recomendação Notas
Servidor Web Kestrel O HttpSysCommunicationListener não foi projetado para uso por serviços com monitoração de estado nos quais as réplicas compartilham um processo de host.
Configuração de portas atribuído dinamicamente Várias réplicas de um serviço com monitoração de estado podem compartilhar um processo de host ou sistema operacional de host e, portanto, precisarão de portas exclusivas.
ServiceFabricIntegrationOptions UseUniqueServiceUrl 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