Editar

Partilhar via


Padrão moderno de aplicativo Web para .NET

Azure App Service
Azure Front Door
Azure Cache for Redis
.NET

Este artigo mostra como implementar o padrão Modern Web App. O padrão Modern Web App define como você deve modernizar aplicativos Web na nuvem e introduzir uma arquitetura orientada a serviços. O padrão Modern Web App fornece arquitetura, código e orientação de configuração prescritivos que se alinham com os princípios da Estrutura Well-Architected do Azure e se baseiam no padrão Aplicativo Web Confiável.

Por que usar o padrão Modern Web App?

O padrão Modern Web App ajuda a otimizar áreas de alta demanda de um aplicativo Web. Ele oferece orientação detalhada para dissociar essas áreas, permitindo dimensionamento independente para otimização de custos. Essa abordagem permite alocar recursos dedicados a componentes críticos, melhorando o desempenho geral. A dissociação de serviços separáveis pode melhorar a confiabilidade, evitando que lentidão em uma parte do aplicativo afete outras. O desacoplamento também permite o controle de versão de componentes individuais do aplicativo de forma independente.

Como implementar o padrão Modern Web App

Este artigo contém diretrizes de arquitetura, código e configuração para implementar o padrão Modern Web App. Use os links a seguir para navegar até a orientação necessária:

  • Orientação de arquitetura: saiba como modularizar componentes de aplicativos Web e selecionar soluções de plataforma como serviço (PaaS) apropriadas.
  • Orientação de código: implemente quatro padrões de projeto para otimizar os componentes dissociados: Strangler Fig, Nivelamento de carga baseado em fila, Consumidores concorrentes e padrões de monitoramento de ponto final de integridade.
  • Diretrizes de configuração: configure autenticação, autorização, dimensionamento automático e conteinerização para os componentes dissociados.

Gorjeta

Logótipo do GitHub Há uma implementação de referência (aplicativo de exemplo) do padrão Modern Web App. Ele representa o estado final da implementação do Modern Web App. É um aplicativo Web de nível de produção que apresenta todas as atualizações de código, arquitetura e configuração discutidas neste artigo. Implante e use a implementação de referência para orientar sua implementação do padrão Modern Web App.

Orientações para a arquitetura

O padrão Modern Web App baseia-se no padrão Reliable Web App. Ele requer alguns componentes de arquitetura extra para implementar. Você precisa de uma fila de mensagens, plataforma de contêiner, armazenamento de dados de serviço dissociado e um registro de contêiner (consulte a figura 1).

Diagrama mostrando a arquitetura de linha de base do padrão Modern Web App.Figura 1. Elementos arquitetónicos essenciais do padrão Modern Web App.

Para um SLO (objetivo de nível de serviço) mais alto, você pode adicionar uma segunda região à arquitetura do seu aplicativo Web. Uma segunda região requer que você configure seu balanceador de carga para rotear o tráfego para a segunda região para oferecer suporte a uma configuração ativa-ativa ou ativa-passiva. Use uma topologia de rede hub-and-spoke para centralizar e compartilhar recursos, como um firewall de rede. Acesse o repositório de contêineres por meio da rede virtual do hub. Se você tiver máquinas virtuais, adicione um host bastion à rede virtual do hub para gerenciá-las com segurança (consulte a figura 2).

Diagrama mostrando a arquitetura de padrão do Aplicativo Web Moderno com segunda região e topologia de rede hub-and-spoke.Figura 2. A arquitetura de padrão do Aplicativo Web Moderno com topologia de rede hub-and-spoke de segunda região e hub-and-spoke.

Arquitetura de desacoplamento

Para implementar o padrão Modern Web App, você precisa desacoplar a arquitetura existente do aplicativo Web. A dissociação da arquitetura envolve a divisão de um aplicativo monolítico em serviços menores e independentes, cada um responsável por um recurso ou funcionalidade específica. Esse processo envolve avaliar o aplicativo Web atual, modificar a arquitetura e, finalmente, extrair o código do aplicativo Web para uma plataforma de contêiner. O objetivo é identificar e extrair sistematicamente os serviços de aplicativos que mais se beneficiam da dissociação. Para dissociar sua arquitetura, siga estas recomendações:

  • Identifique os limites do serviço. Aplique princípios de design orientados por domínio para identificar contextos limitados em seu aplicativo monolítico. Cada contexto delimitado representa um limite lógico e pode ser um candidato para um serviço separado. Serviços que representam funções empresariais distintas e têm menos dependências são bons candidatos para a dissociação.

  • Avalie os benefícios do serviço. Concentre-se nos serviços que mais se beneficiam do dimensionamento independente. A dissociação desses serviços e a conversão de tarefas de processamento de operações síncronas para assíncronas permite um gerenciamento de recursos mais eficiente, oferece suporte a implantações independentes e reduz o risco de afetar outras partes do aplicativo durante atualizações ou alterações. Por exemplo, você pode separar o checkout do pedido do processamento do pedido.

  • Avaliar a viabilidade técnica. Examine a arquitetura atual para identificar restrições técnicas e dependências que possam afetar o processo de dissociação. Planeje como os dados são gerenciados e compartilhados entre serviços. Os serviços dissociados devem gerenciar seus próprios dados e minimizar o acesso direto ao banco de dados entre os limites do serviço.

  • Implante os serviços do Azure. Selecione e implante os serviços do Azure necessários para dar suporte ao serviço de aplicativo Web que você pretendia extrair. Use o seguinte : Selecione a seção correta de serviços do Azure para obter orientação.

  • Desacoplar serviços de aplicativos Web. Defina interfaces e APIs claras para que os serviços de aplicativo Web recém-extraídos interajam com outras partes do sistema. Projete uma estratégia de gerenciamento de dados que permita que cada serviço gerencie seus próprios dados, garantindo consistência e integridade. Para estratégias de implementação específicas e padrões de design a serem usados durante esse processo de extração, consulte a seção Orientação de código.

  • Use armazenamento independente para serviços dissociados. Cada serviço dissociado deve ter seu próprio armazenamento de dados isolado para facilitar o controle de versão, a implantação, a escalabilidade e a manutenção da integridade dos dados independentes. Por exemplo, a implementação de referência separa o serviço de renderização de tíquetes da API da Web e elimina a necessidade de o serviço acessar o banco de dados da API. Em vez disso, o serviço comunica a URL onde as imagens de tíquete foram geradas de volta para a API da Web por meio de uma mensagem do Barramento de Serviço do Azure e a API persiste o caminho para seu banco de dados.

  • Implemente pipelines de implantação separados para cada serviço dissociado. Pipelines de implantação separados permitem que cada serviço seja atualizado em seu próprio ritmo. Se diferentes equipes ou organizações dentro da sua empresa possuem serviços diferentes, ter pipelines de implantação separados dá a cada equipe controle sobre suas próprias implantações. Use ferramentas de integração contínua e entrega contínua (CI/CD) como Jenkins, GitHub Actions ou Azure Pipelines para configurar esses pipelines.

  • Revise os controles de segurança. Certifique-se de que seus controles de segurança estejam atualizados para levar em conta a nova arquitetura, incluindo regras de firewall e controles de acesso.

Selecione os serviços do Azure certos

Para cada serviço do Azure em sua arquitetura, consulte o guia de serviço do Azure relevante no Well-Architected Framework. Para o padrão Modern Web App, você precisa de um sistema de mensagens que ofereça suporte a mensagens assíncronas, uma plataforma de aplicativo que ofereça suporte à conteinerização e um repositório de imagens de contêiner.

  • Escolha uma fila de mensagens. Uma fila de mensagens é uma parte importante das arquiteturas orientadas a serviços. Ele separa remetentes e recetores de mensagens para permitir mensagens assíncronas. Use a orientação sobre como escolher um serviço de mensagens do Azure para escolher um sistema de mensagens do Azure que ofereça suporte às suas necessidades de design. O Azure tem três serviços de mensagens: Grade de Eventos do Azure, Hubs de Eventos do Azure e Service Bus. Comece com o Service Bus como a opção padrão e use as outras duas opções se o Service Bus não atender às suas necessidades.

    Serviço Caso de utilização
    Service Bus Escolha o Service Bus para uma entrega confiável, ordenada e possivelmente transacional de mensagens de alto valor em aplicativos corporativos.
    Event Grid Escolha Grade de Eventos quando precisar lidar com um grande número de eventos discretos de forma eficiente. A Grade de Eventos é escalável para aplicativos controlados por eventos em que muitos eventos pequenos e independentes (como alterações no estado do recurso) precisam ser roteados para assinantes em um modelo de baixa latência, publicação-assinatura.
    Hubs de Eventos Escolha Hubs de Eventos para ingestão massiva de dados de alta taxa de transferência, como telemetria, logs ou análises em tempo real. Os Hubs de Eventos são otimizados para cenários de streaming em que os dados em massa precisam ser ingeridos e processados continuamente.
  • Implemente um serviço de contêiner. Para as partes do seu aplicativo que você deseja colocar em contêineres, você precisa de uma plataforma de aplicativo que ofereça suporte a contêineres. Use a orientação Escolha um serviço de contêiner do Azure para ajudar a tomar sua decisão. O Azure tem três serviços de contêiner principais: Aplicativos de Contêiner do Azure, Serviço Kubernetes do Azure (AKS) e Serviço de Aplicativo do Azure. Comece com Aplicativos de Contêiner como a opção padrão e use as outras duas opções se Aplicativos de Contêiner não atender às suas necessidades.

    Serviço Caso de utilização
    Aplicativos de contêiner Escolha Aplicativos de contêiner se precisar de uma plataforma sem servidor que dimensione e gerencie automaticamente contêineres em aplicativos controlados por eventos.
    AKS Escolha o AKS se precisar de controle detalhado sobre as configurações do Kubernetes e recursos avançados para escalabilidade, rede e segurança.
    Aplicativos Web para contêiner Escolha Aplicativo Web para Contêineres no Serviço de Aplicativo para obter a experiência PaaS mais simples.
  • Implemente um repositório de contêiner. Ao usar qualquer serviço de computação baseado em contêiner, é necessário ter um repositório para armazenar as imagens de contêiner. Você pode usar um registro de contêiner público como o Docker Hub ou um registro gerenciado como o Azure Container Registry. Use a orientação Introdução aos registros de contêiner no Azure para ajudar a tomar sua decisão.

Orientação de código

Para desacoplar e extrair com êxito um serviço independente, você precisa atualizar o código do aplicativo Web com os seguintes padrões de design: o padrão Strangler Fig, o padrão Queue-Based Load Leveling, o padrão Competing Consumers, o padrão Health Endpoint Monitoring e o padrão Retry.

Diagrama mostrando a função dos padrões de design na arquitetura de padrões do Modern Web App.Figura 3. Papel dos padrões de design.

  1. Padrão Strangler Fig: O padrão Strangler Fig migra incrementalmente a funcionalidade de um aplicativo monolítico para o serviço dissociado. Implemente esse padrão no aplicativo Web principal para migrar gradualmente a funcionalidade para serviços independentes, direcionando o tráfego com base em pontos de extremidade.

  2. Padrão de Nivelamento de Carga Baseado em Fila: O padrão de Nivelamento de Carga Baseado em Fila gerencia o fluxo de mensagens entre o produtor e o consumidor usando uma fila como buffer. Implemente esse padrão na parte do produtor do serviço desacoplado para gerenciar o fluxo de mensagens de forma assíncrona usando uma fila.

  3. Padrão de consumidores concorrentes: o padrão de consumidores concorrentes permite que várias instâncias do serviço dissociado leiam independentemente a mesma fila de mensagens e compitam para processar mensagens. Implemente esse padrão no serviço dissociado para distribuir tarefas em várias instâncias.

  4. Padrão de monitoramento de ponto de extremidade de integridade: o padrão de monitoramento de ponto de extremidade de integridade expõe pontos de extremidade para monitorar o status e a integridade de diferentes partes do aplicativo Web. (4a) Implemente este padrão na aplicação Web principal. (4b) Implementá-lo também no serviço dissociado para rastrear a integridade dos endpoints.

  5. Padrão de repetição: o padrão de repetição lida com falhas transitórias repetindo operações que podem falhar intermitentemente. (5a) Implemente esse padrão em todas as chamadas de saída para outros serviços do Azure no aplicativo Web principal, como chamadas para fila de mensagens e pontos de extremidade privados. (5b) Implementar também este padrão no serviço dissociado para lidar com falhas transitórias em chamadas para os pontos finais privados.

Cada padrão de design fornece benefícios que se alinham com um ou mais pilares do Well-Architected Framework (consulte a tabela a seguir).

Padrão de estruturação Local de implementação Fiabilidade (RE) Segurança (SE) Otimização de Custos (CO) Excelência Operacional (OE) Eficiência de desempenho (PE) Apoiando os princípios da estrutura bem arquitetada
Padrão de figo Strangler Aplicação Web principal RE:08
CO:07
CO:08
OE:06
OE:11
Padrão de Redistribuição de Carga Baseada na Fila Produtor de serviço dissociado RE:06
RE:07
CO:12
PE:05
Padrão de consumidores concorrentes Serviço dissociado RE:05
RE:07
CO:05
CO:07
PE:05
PE:07
Padrão de monitoramento de ponto final de integridade Aplicativo Web principal & serviço dissociado RE:07
RE:10
OE:07
PE:05
Padrão de repetição Aplicativo Web principal & serviço dissociado RE:07

Implementar o padrão Strangler Fig

Use o padrão Strangler Fig para migrar gradualmente a funcionalidade da base de código monolítica para novos serviços independentes. Extraia novos serviços da base de código monolítica existente e modernize lentamente partes críticas do aplicativo Web. Para implementar o padrão Strangler Fig, siga estas recomendações:

  • Configure uma camada de roteamento. Na base de código monolítica do aplicativo Web, implemente uma camada de roteamento que direcione o tráfego com base em pontos de extremidade. Use a lógica de roteamento personalizada conforme necessário para lidar com regras de negócios específicas para direcionar o tráfego. Por exemplo, se você tiver um ponto de /users extremidade em seu aplicativo monolítico e tiver movido essa funcionalidade para o serviço dissociado, a camada de roteamento direcionará todas as solicitações para /users o novo serviço.

  • Gerencie a distribuição de recursos. Use as bibliotecas do .NET Feature Management para implementar sinalizadores de recursos e distribuição em estágios para implantar gradualmente os serviços dissociados. O roteamento de aplicativo monolítico existente deve controlar quantas solicitações os serviços dissociados recebem. Comece com uma pequena percentagem de pedidos e aumente a utilização ao longo do tempo à medida que ganha confiança na sua estabilidade e desempenho. Por exemplo, a implementação de referência extrai a funcionalidade de renderização de tíquetes em um serviço autônomo, que pode ser introduzido gradualmente para lidar com uma parte maior das solicitações de renderização de tíquetes. À medida que o novo serviço prova sua confiabilidade e desempenho, ele pode eventualmente assumir toda a funcionalidade de renderização de tíquetes do monólito, completando a transição.

  • Utilize um serviço de fachada (se necessário). Um serviço de fachada é útil quando uma única solicitação precisa interagir com vários serviços ou quando você deseja ocultar a complexidade do sistema subjacente do cliente. No entanto, se o serviço dissociado não tiver APIs voltadas para o público, um serviço de fachada pode não ser necessário. Na base de código do aplicativo Web monolítico, implemente um serviço de fachada para rotear solicitações para o back-end apropriado (monólito ou microsserviço). No novo serviço dissociado, certifique-se de que o novo serviço pode lidar com solicitações de forma independente quando acessado através da fachada.

Implementar o padrão de nivelamento de carga baseado em fila

Implemente o padrão de Nivelamento de Carga Baseado em Fila na parte do produtor do serviço desacoplado para lidar de forma assíncrona com tarefas que não precisam de respostas imediatas. Esse padrão melhora a capacidade de resposta geral do sistema e a escalabilidade usando uma fila para gerenciar a distribuição da carga de trabalho. Permite que o serviço dissociado processe pedidos a um ritmo consistente. Para implementar esse padrão de forma eficaz, siga estas recomendações:

  • Use o serviço de enfileiramento de mensagens sem bloqueio. Certifique-se de que o processo que envia mensagens para a fila não bloqueie outros processos enquanto aguarda que o serviço desacoplado trate mensagens na fila. Se o processo exigir o resultado da operação de serviço dissociado, tenha uma maneira alternativa de lidar com a situação enquanto aguarda a conclusão da operação em fila. Por exemplo, a implementação de referência usa o Service Bus e a await palavra-chave com messageSender.PublishAsync() para publicar mensagens de forma assíncrona na fila sem bloquear o thread que executa esse código:

    // Asynchronously publish a message without blocking the calling thread
    await messageSender.PublishAsync(new TicketRenderRequestMessage(Guid.NewGuid(), ticket, null, DateTime.Now), CancellationToken.None);
    

    Essa abordagem garante que o aplicativo principal permaneça responsivo e possa lidar com outras tarefas simultaneamente, enquanto o serviço desacoplado processa as solicitações enfileiradas a uma taxa gerenciável.

  • Implemente a repetição e remoção de mensagens. Implemente um mecanismo para tentar processar novamente mensagens em fila que não podem ser processadas com êxito. Se as falhas persistirem, essas mensagens devem ser removidas da fila. Por exemplo, o Service Bus tem recursos internos de repetição e fila de mensagens mortas.

  • Configure o processamento idempotente de mensagens. A lógica que processa mensagens da fila deve ser idempotente para lidar com casos em que uma mensagem pode ser processada mais de uma vez. Por exemplo, a implementação de referência usa ServiceBusClient.CreateProcessor com AutoCompleteMessages = true e ReceiveMode = ServiceBusReceiveMode.PeekLock para garantir que as mensagens sejam processadas apenas uma vez e possam ser reprocessadas em caso de falha (consulte o código a seguir).

    // Create a processor for idempotent message processing
    var processor = serviceBusClient.CreateProcessor(path, new ServiceBusProcessorOptions
    {
        // Allow the messages to be auto-completed
        // if processing finishes without failure.
        AutoCompleteMessages = true,
    
        // PeekLock mode provides reliability in that unsettled messages
        // will be redelivered on failure.
        ReceiveMode = ServiceBusReceiveMode.PeekLock,
    
        // Containerized processors can scale at the container level
        // and need not scale via the processor options.
        MaxConcurrentCalls = 1,
        PrefetchCount = 0
    });
    
  • Gerencie as alterações na experiência. O processamento assíncrono pode levar a que as tarefas não sejam imediatamente concluídas. Os utilizadores devem ser informados quando a sua tarefa ainda está a ser processada para definir expectativas corretas e evitar confusões. Use pistas visuais ou mensagens para indicar que uma tarefa está em andamento. Dê aos usuários a opção de receber notificações quando a tarefa for concluída, como um e-mail ou notificação por push.

Implementar o padrão de consumidores concorrentes

Implemente o padrão Consumidores concorrentes nos serviços dissociados para gerenciar tarefas de entrada da fila de mensagens. Esse padrão envolve a distribuição de tarefas entre várias instâncias de serviços dissociados. Esses serviços processam mensagens da fila, melhorando o balanceamento de carga e aumentando a capacidade do sistema para lidar com solicitações simultâneas. O padrão de Consumidores Concorrentes é eficaz quando:

  • A sequência de processamento de mensagens não é crucial.
  • A fila não é afetada por mensagens malformadas.
  • A operação de processamento é idempotente, o que significa que pode ser aplicada várias vezes sem alterar o resultado além da aplicação inicial.

Para implementar o padrão de Consumidores Concorrentes, siga estas recomendações:

  • Manipule mensagens simultâneas. Ao receber mensagens de uma fila, certifique-se de que seu sistema foi projetado para lidar com várias mensagens simultaneamente. Defina o máximo de chamadas simultâneas como 1 para que um consumidor separado trate de cada mensagem.

  • Desative a pré-busca. Desative a pré-busca de mensagens para que os consumidores busquem mensagens somente quando estiverem prontas.

  • Use modos de processamento de mensagens confiáveis. Use um modo de processamento confiável, como PeekLock (ou seu equivalente), que tenta automaticamente mensagens que falham no processamento. Esse modo aumenta a confiabilidade em relação aos métodos de exclusão inicial. Se um trabalhador não conseguir lidar com uma mensagem, outro deverá ser capaz de processá-la sem erros, mesmo que a mensagem seja processada várias vezes.

  • Implemente o tratamento de erros. Encaminhe mensagens malformadas ou não processáveis para uma fila separada de mensagens mortas. Este design evita o processamento repetitivo. Por exemplo, você pode capturar exceções durante o processamento da mensagem e mover a mensagem problemática para a fila separada.

  • Lidar com mensagens fora de ordem. Projete os consumidores para processar mensagens que chegam fora de sequência. Vários consumidores paralelos significam que eles podem processar mensagens fora de ordem.

  • Dimensione com base no comprimento da fila. Os serviços que consomem mensagens de uma fila devem ser dimensionados automaticamente com base no comprimento da fila. O dimensionamento automático baseado em escala permite o processamento eficiente de picos de mensagens recebidas.

  • Use uma fila de resposta de mensagem. Se o sistema exigir notificações para processamento pós-mensagem, configure uma fila de resposta ou resposta dedicada. Essa configuração divide as mensagens operacionais dos processos de notificação.

  • Use serviços sem monitoração de estado. Considere o uso de serviços sem monitoração de estado para processar solicitações de uma fila. Permite um dimensionamento fácil e uma utilização eficiente dos recursos.

  • Configure o registro em log. Integre o registro em log e o tratamento de exceções específicas no fluxo de trabalho de processamento de mensagens. Concentre-se em capturar erros de serialização e direcionar essas mensagens problemáticas para um mecanismo de letra morta. Esses logs fornecem informações valiosas para a solução de problemas.

Por exemplo, a implementação de referência usa o padrão Consumidores Concorrentes em um serviço sem estado em execução em Aplicativos de Contêiner para processar solicitações de renderização de tíquetes de uma fila do Barramento de Serviço. Ele configura um processador de fila com:

  • AutoCompleteMessages: Conclui automaticamente as mensagens se processadas sem falhas.
  • ReceiveMode: usa o modo PeekLock e reentrega mensagens se elas não forem resolvidas.
  • MaxConcurrentCalls: defina como 1 para lidar com uma mensagem de cada vez.
  • PrefetchCount: defina como 0 para evitar mensagens de pré-busca.

O processador registra detalhes de processamento de mensagens, o que ajuda na solução de problemas e no monitoramento. Ele captura erros de desserialização e roteia mensagens inválidas para uma fila de mensagens mortas, impedindo o processamento repetitivo de mensagens defeituosas. O serviço é dimensionado no nível do contêiner, permitindo o tratamento eficiente de picos de mensagens com base no comprimento da fila.

// Create a processor for the given queue that will process
// incoming messages.
var processor = serviceBusClient.CreateProcessor(path, new ServiceBusProcessorOptions
{
    // Allow the messages to be auto-completed
    // if processing finishes without failure.
    AutoCompleteMessages = true,
    // PeekLock mode provides reliability in that unsettled messages
    // are redelivered on failure.
    ReceiveMode = ServiceBusReceiveMode.PeekLock,
    // Containerized processors can scale at the container level
    // and need not scale via the processor options.
    MaxConcurrentCalls = 1,
    PrefetchCount = 0
});

// Called for each message received by the processor.
processor.ProcessMessageAsync += async args =>
{
    logger.LogInformation("Processing message {MessageId} from {ServiceBusNamespace}/{Path}", args.Message.MessageId, args.FullyQualifiedNamespace, args.EntityPath);
    // Unhandled exceptions in the handler will be caught by
    // the processor and result in abandoning and dead-lettering the message.
    try
    {
        var message = args.Message.Body.ToObjectFromJson<T>();
        await messageHandler(message, args.CancellationToken);
        logger.LogInformation("Successfully processed message {MessageId} from {ServiceBusNamespace}/{Path}",args.Message.MessageId, args.FullyQualifiedNamespace, args.EntityPath);
    }
    catch (JsonException)
    {
        logger.LogError("Invalid message body; could not be deserialized to {Type}", typeof(T));
        await args.DeadLetterMessageAsync(args.Message, $"Invalid message body; could not be deserialized to {typeof(T)}",cancellationToken: args.CancellationToken);
    }
};

Implementar o padrão Health Endpoint Monitoring

Implemente o padrão Health Endpoint Monitoring no código principal do aplicativo e no código de serviço desacoplado para controlar a integridade dos pontos de extremidade do aplicativo. Orquestradores como AKS ou Container Apps podem sondar esses pontos de extremidade para verificar a integridade do serviço e reiniciar instâncias não íntegras. ASP.NET aplicativos principais podem adicionar middleware de verificação de integridade dedicado para atender com eficiência dados de integridade de endpoint e dependências de chave. Para implementar o padrão Health Endpoint Monitoring, siga estas recomendações:

  • Implementar verificações de integridade. Use ASP.NET middleware de verificações de integridade principais para fornecer pontos de extremidade de verificação de integridade.

  • Valide dependências. Certifique-se de que suas verificações de integridade validem a disponibilidade de dependências de chave, como banco de dados, armazenamento e sistema de mensagens. O pacote que não é da Microsoft, AspNetCore.Diagnostics.HealthChecks, pode implementar verificações de dependência de verificação de integridade para muitas dependências comuns de aplicativos.

    Por exemplo, a implementação de referência usa ASP.NET middleware de verificação de integridade principal para expor pontos de extremidade de verificação de integridade, usando o AddHealthChecks() builder.Services método no objeto. O código valida a disponibilidade das dependências de chave, do Armazenamento de Blobs do Azure e da fila do Barramento de Serviço com os AddAzureBlobStorage() métodos e AddAzureServiceBusQueue() , que fazem parte do AspNetCore.Diagnostics.HealthChecks pacote. O Container Apps permite a configuração de testes de integridade que são monitorados para avaliar se os aplicativos estão íntegros ou precisam de reciclagem.

    // Add health checks, including health checks for Azure services
    // that are used by this service.
    // The Blob Storage and Service Bus health checks are provided by
    // AspNetCore.Diagnostics.HealthChecks
    // (a popular open source project) rather than by Microsoft. 
    // https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks
    builder.Services.AddHealthChecks()
    .AddAzureBlobStorage(options =>
    {
        // AddAzureBlobStorage will use the BlobServiceClient registered in DI
        // We just need to specify the container name
        options.ContainerName = builder.Configuration.GetRequiredConfigurationValue("App:StorageAccount:Container");
    })
    .AddAzureServiceBusQueue(
        builder.Configuration.GetRequiredConfigurationValue("App:ServiceBus:Host"),
        builder.Configuration.GetRequiredConfigurationValue("App:ServiceBus:RenderRequestQueueName"),
        azureCredentials);
    
    // Further app configuration omitted for brevity
    app.MapHealthChecks("/health");
    
  • Configure os recursos do Azure. Configure os recursos do Azure para usar as URLs de verificação de integridade do aplicativo para confirmar a vivacidade e a prontidão. Por exemplo, a implementação de referência usa o Bicep para configurar as URLs de verificação de integridade para confirmar a vivacidade e a prontidão do recurso do Azure. Uma sonda de vivacidade para atingir o ponto final a /health cada 10 segundos após um atraso inicial de 2 segundos.

    probes: [
      {
        type: 'liveness'
        httpGet: {
          path: '/health'
          port: 8080
        }
        initialDelaySeconds: 2
        periodSeconds: 10
      }
    ]
    

Implementar o padrão Retry

O padrão Retry permite que os aplicativos se recuperem de falhas transitórias. O padrão Repetir é central para o padrão Aplicativo Web Confiável, portanto, seu aplicativo Web já deve estar usando o padrão Repetir. Aplique o padrão Repetir a solicitações para os sistemas de mensagens e solicitações emitidas pelos serviços dissociados extraídos do aplicativo Web. Para implementar o padrão Repetir, siga estas recomendações:

  • Configure as opções de repetição. Ao integrar com uma fila de mensagens, certifique-se de configurar o cliente responsável pelas interações com a fila com as configurações de repetição apropriadas. Especifique parâmetros como o número máximo de tentativas, o atraso entre as tentativas e o atraso máximo.

  • Use backoff exponencial. Implemente uma estratégia de backoff exponencial para tentativas de repetição. Isso significa aumentar exponencialmente o tempo entre cada nova tentativa, o que ajuda a reduzir a carga no sistema durante períodos de altas taxas de falha.

  • Use a funcionalidade de repetição do SDK. Para serviços com SDKs especializados, como Service Bus ou Armazenamento de Blob, use os mecanismos de repetição internos. Os mecanismos de repetição integrados são otimizados para os casos de uso típicos do serviço e podem lidar com novas tentativas de forma mais eficaz com menos configuração necessária da sua parte. Por exemplo, a implementação de referência usa a funcionalidade interna de repetição do SDK do Service Bus (ServiceBusClient e ServiceBusRetryOptions). O ServiceBusRetryOptions objeto busca as configurações de para definir as configurações de MessageBusOptions repetição como MaxRetries, Delay, MaxDelay e TryTimeout.

    // ServiceBusClient is thread-safe and can be reused for the lifetime
    // of the application.
    services.AddSingleton(sp =>
    {
        var options = sp.GetRequiredService<IOptions<MessageBusOptions>>().Value;
        var clientOptions = new ServiceBusClientOptions
        {
            RetryOptions = new ServiceBusRetryOptions
            {
                Mode = ServiceBusRetryMode.Exponential,
                MaxRetries = options.MaxRetries,
                Delay = TimeSpan.FromSeconds(options.BaseDelaySecondsBetweenRetries),
                MaxDelay = TimeSpan.FromSeconds(options.MaxDelaySeconds),
                TryTimeout = TimeSpan.FromSeconds(options.TryTimeoutSeconds)
            }
        };
        return new ServiceBusClient(options.Host, azureCredential ?? new DefaultAzureCredential(), clientOptions);
    });
    
  • Adote bibliotecas de resiliência padrão para clientes HTTP. Para comunicações HTTP, integre uma biblioteca de resiliência padrão, como Polly ou Microsoft.Extensions.Http.Resilience. Essas bibliotecas oferecem mecanismos abrangentes de repetição que são cruciais para gerenciar comunicações com serviços Web externos.

  • Manipule o bloqueio de mensagens. Para sistemas baseados em mensagens, implemente estratégias de tratamento de mensagens que suportem novas tentativas sem perda de dados, como o uso de modos "peek-lock", quando disponíveis. Certifique-se de que as mensagens com falha sejam repetidas de forma eficaz e movidas para uma fila de mensagens mortas após repetidas falhas.

Implementar rastreamento distribuído

À medida que os aplicativos se tornam mais orientados a serviços e seus componentes são dissociados, monitorar o fluxo de execução entre serviços é crucial. O padrão de Aplicativo Web Moderno usa o Application Insights e o Azure Monitor para visibilidade da integridade e do desempenho do aplicativo por meio de APIs OpenTelemetry que dão suporte ao rastreamento distribuído.

O rastreamento distribuído rastreia uma solicitação do usuário à medida que ela percorre vários serviços. Quando uma solicitação é recebida, ela é marcada com um identificador de rastreamento, que é passado para outros componentes por meio de cabeçalhos HTTP e propriedades do Service Bus durante a chamada de dependências. Os rastreamentos e logs incluem o identificador de rastreamento e um identificador de atividade (ou identificador de extensão), que corresponde ao componente específico e sua atividade pai. Ferramentas de monitoramento como o Application Insights o usam para exibir uma árvore de atividades e logs em diferentes serviços, cruciais para monitorar aplicativos distribuídos.

  • Instale bibliotecas OpenTelemetry . Use bibliotecas de instrumentação para habilitar o rastreamento e as métricas de componentes comuns. Adicione instrumentação personalizada com System.Diagnostics.ActivitySource e System.Diagnostics.Activity se necessário. Use bibliotecas de exportação para ouvir diagnósticos do OpenTelemetry e gravá-los em repositórios persistentes. Utilize exportadores existentes ou crie o seu próprio com System.Diagnostics.ActivityListenero .

  • Configure o OpenTelemetry. Use a distribuição do Azure Monitor do OpenTelemetry (Azure.Monitor.OpenTelemetry.AspNetCore). Certifique-se de exportar diagnósticos para o Application Insights e inclua instrumentação interna para métricas, rastreamentos, logs e exceções comuns do tempo de execução do .NET e do ASP.NET Core. Inclua outros pacotes de instrumentação OpenTelemetry para clientes SQL, Redis e SDK do Azure.

  • Monitorizar e analisar. Após a configuração, certifique-se de que os logs, rastreamentos, métricas e exceções sejam capturados e enviados para o Application Insights. Verifique se os identificadores de rastreamento, atividade e atividade pai estão incluídos, permitindo que o Application Insights forneça visibilidade de rastreamento de ponta a ponta nos limites HTTP e do Service Bus. Use essa configuração para monitorar e analisar as atividades do seu aplicativo em todos os serviços de forma eficaz.

O exemplo de Aplicativo Web Moderno usa a distribuição do Azure Monitor de OpenTelemetry (Azure.Monitor.OpenTelemetry.AspNetCore). Mais pacotes de instrumentação são usados para clientes SQL, Redis e SDK do Azure. O OpenTelemetry é configurado no serviço de renderização de tíquetes de exemplo do Modern Web App da seguinte forma:

builder.Logging.AddOpenTelemetry(o => 
{ 
    o.IncludeFormattedMessage = true; 
    o.IncludeScopes = true; 
}); 

builder.Services.AddOpenTelemetry() 
    .UseAzureMonitor(o => o.ConnectionString = appInsightsConnectionString) 
    .WithMetrics(metrics => 
    { 
        metrics.AddAspNetCoreInstrumentation() 
                .AddHttpClientInstrumentation() 
                .AddRuntimeInstrumentation(); 
    }) 
    .WithTracing(tracing => 
    { 
        tracing.AddAspNetCoreInstrumentation() 
                .AddHttpClientInstrumentation() 
                .AddSource("Azure.*"); 
    }); 

O builder.Logging.AddOpenTelemetry método roteia todo o registro por meio do OpenTelemetry, garantindo rastreamento e registro consistentes em todo o aplicativo. Ao registrar os serviços OpenTelemetry no builder.Services.AddOpenTelemetry, o aplicativo é configurado para coletar e exportar diagnósticos, que são enviados para o Application Insights via UseAzureMonitor. Além disso, a instrumentação do cliente para componentes como Service Bus e clientes HTTP é configurada através WithMetrics de e WithTracing, permitindo métricas automáticas e coleta de rastreamento sem exigir alterações no uso do cliente existente, apenas uma atualização para a configuração.

Diretrizes de configuração

As seções a seguir fornecem orientação sobre como implementar as atualizações de configuração. Cada seção se alinha com um ou mais pilares da estrutura bem arquitetada.

Configuração Fiabilidade (RE) Segurança (SE) Otimização de Custos (CO) Excelência Operacional (OE) Eficiência de desempenho (PE) Apoiando os princípios da estrutura bem arquitetada
Configurar autenticação e autorização SE:05
OE:10
Implementar dimensionamento automático independente RE:06
CO:12
PE:05
Implantação do serviço Containerize CO:13
PE:09
PE:03

Configurar autenticação e autorização

Para configurar a autenticação e a autorização em quaisquer novos serviços do Azure (identidades de carga de trabalho) adicionados ao aplicativo Web, siga estas recomendações:

  • Use identidades gerenciadas para cada novo serviço. Cada serviço independente deve ter sua própria identidade e usar identidades gerenciadas para autenticação de serviço a serviço. As identidades gerenciadas eliminam a necessidade de gerenciar credenciais em seu código e reduzem o risco de vazamento de credenciais. Eles ajudam você a evitar colocar informações confidenciais, como cadeias de conexão, em seu código ou arquivos de configuração.

  • Conceda o menor privilégio a cada novo serviço. Atribua apenas as permissões necessárias a cada nova identidade de serviço. Por exemplo, se uma identidade só precisa enviar por push para um registro de contêiner, não dê a ela permissões pull. Revise essas permissões regularmente e ajuste conforme necessário. Use identidades diferentes para diferentes funções, como a implantação e o aplicativo. Isso limita os danos potenciais se uma identidade for comprometida.

  • Adotar a infraestrutura como código (IaC). Use o Bicep ou ferramentas IaC semelhantes para definir e gerenciar seus recursos de nuvem. O IaC garante a aplicação consistente de configurações de segurança em suas implantações e permite que você controle a versão de sua configuração de infraestrutura.

Para configurar a autenticação e a autorização em usuários (identidades de usuário), siga estas recomendações:

  • Conceda o menor privilégio aos usuários. Assim como nos serviços, certifique-se de que os usuários recebam apenas as permissões necessárias para executar suas tarefas. Analise e ajuste regularmente essas permissões.

  • Realizar auditorias de segurança regulares. Reveja e audite regularmente a sua configuração de segurança. Procure por configurações incorretas ou permissões desnecessárias e retifique-as imediatamente.

A implementação de referência usa o IaC para atribuir identidades gerenciadas a serviços adicionados e funções específicas a cada identidade. Ele define funções e permissões de acesso para implantação (containerRegistryPushRoleId), proprietário do aplicativo (containerRegistryPushRoleId) e aplicativo Container Apps (containerRegistryPullRoleId) (consulte o código a seguir).

roleAssignments: \[
    {
    principalId: deploymentSettings.principalId
    principalType: deploymentSettings.principalType
    roleDefinitionIdOrName: containerRegistryPushRoleId
    }
    {
    principalId: ownerManagedIdentity.outputs.principal_id
    principalType: 'ServicePrincipal'
    roleDefinitionIdOrName: containerRegistryPushRoleId
    }
    {
    principalId: appManagedIdentity.outputs.principal_id
    principalType: 'ServicePrincipal'
    roleDefinitionIdOrName: containerRegistryPullRoleId
    }
\]

A implementação de referência atribui a identidade gerenciada como a nova identidade de Aplicativos de Contêiner na implantação (consulte o código a seguir).

module renderingServiceContainerApp 'br/public:avm/res/app/container-app:0.1.0' = {
  name: 'application-rendering-service-container-app'
  scope: resourceGroup()
  params: {
    // Other parameters omitted for brevity
    managedIdentities: {
      userAssignedResourceIds: [
        managedIdentity.id
      ]
    }
  }
}

Configurar o dimensionamento automático independente

O padrão Modern Web App começa a quebrar a arquitetura monolítica e introduz o desacoplamento de serviços. Ao desacoplar uma arquitetura de aplicativo Web, você pode dimensionar serviços dissociados de forma independente. Dimensionar os serviços do Azure para dar suporte a um serviço de aplicativo Web independente, em vez de um aplicativo Web inteiro, otimiza os custos de dimensionamento e, ao mesmo tempo, atende às demandas. Para dimensionar contêineres automaticamente, siga estas recomendações:

  • Use serviços sem monitoração de estado. Certifique-se de que os seus serviços são apátridas. Se seu aplicativo .NET contiver o estado da sessão em processo, externalize-o para um cache distribuído como o Redis ou um banco de dados como o SQL Server.

  • Configure regras de dimensionamento automático. Use as configurações de dimensionamento automático que fornecem o controle mais econômico sobre seus serviços. Para serviços em contêineres, o dimensionamento baseado em eventos, como o Kubernetes Event-Driven Autoscaler (KEDA), geralmente fornece controle granular, permitindo que você dimensione com base em métricas de eventos. Container Apps e AKS suportam KEDA. Para serviços que não suportam o KEDA, como o Serviço de Aplicativo, use os recursos de dimensionamento automático fornecidos pela própria plataforma. Esses recursos geralmente incluem dimensionamento com base em regras baseadas em métricas ou tráfego HTTP.

  • Configure o mínimo de réplicas. Para evitar um arranque a frio, defina as definições de dimensionamento automático para manter um mínimo de uma réplica. Um início a frio é quando você inicializa um serviço a partir de um estado interrompido, o que geralmente cria uma resposta atrasada. Se minimizar os custos for uma prioridade e você puder tolerar atrasos de inicialização a frio, defina a contagem mínima de réplicas como 0 ao configurar o dimensionamento automático.

  • Configure um período de reflexão. Aplique um período de reflexão apropriado para introduzir um atraso entre os eventos de dimensionamento. O objetivo é evitar atividades de dimensionamento excessivo desencadeadas por picos de carga temporários.

  • Configure o dimensionamento baseado em fila. Se seu aplicativo usa uma fila de mensagens como o Service Bus, configure suas configurações de dimensionamento automático para dimensionar com base no comprimento da fila com mensagens de solicitação. O scaler visa manter uma réplica do serviço para cada N mensagens na fila (arredondadas para cima).

Por exemplo, a implementação de referência usa o escalador KEDA do Service Bus para dimensionar o aplicativo de contêiner com base no comprimento da fila. O service-bus-queue-length-rule dimensiona o serviço com base no comprimento de uma fila especificada do Service Bus. O messageCount parâmetro é definido como 10, portanto, o dimensionador tem uma réplica de serviço para cada 10 mensagens na fila. Os scaleMaxReplicas parâmetros e scaleMinReplicas definem o número máximo e mínimo de réplicas para o serviço. O queue-connection-string segredo, que contém a cadeia de conexão para a fila do Barramento de Serviço, é recuperado do Cofre da Chave do Azure. Esse segredo é usado para autenticar o dimensionador para o Service Bus.

scaleRules: [
  {
    name: 'service-bus-queue-length-rule'
    custom: {
      type: 'azure-servicebus'
      metadata: {
        messageCount: '10'
        namespace: renderRequestServiceBusNamespace
        queueName: renderRequestServiceBusQueueName
      }
      auth: [
        {
          secretRef: 'render-request-queue-connection-string'
          triggerParameter: 'connection'
        }
      ]
    }
  }
]

scaleMaxReplicas: 5
scaleMinReplicas: 0

Implantação do serviço Containerize

A conteinerização significa que todas as dependências para que o aplicativo funcione são encapsuladas em uma imagem leve que pode ser implantada de forma confiável em uma ampla variedade de hosts. Para contentorizar a implementação, siga estas recomendações:

  • Identificar limites de domínio. Comece identificando os limites de domínio em seu aplicativo monolítico. Isso ajuda a determinar quais partes do aplicativo você pode extrair em serviços separados.

  • Crie imagens do Docker. Ao criar imagens do Docker para seus serviços .NET, use imagens de base cinzeladas. Essas imagens contêm apenas o conjunto mínimo de pacotes necessários para a execução do .NET, o que minimiza o tamanho do pacote e a área da superfície de ataque.

  • Use Dockerfiles de vários estágios. Implemente Dockerfiles de vários estágios para separar ativos de tempo de compilação da imagem do contêiner de tempo de execução. Ajuda a manter as suas imagens de produção pequenas e seguras.

  • Execute como um usuário não-root. Execute seus contêineres .NET como um usuário não raiz (via nome de usuário ou UID, $APP_UID) para alinhar com o princípio de menor privilégio. Limita os efeitos potenciais de um recipiente comprometido.

  • Ouça na porta 8080. Ao executar como um usuário não-root, configure seu aplicativo para escutar na porta 8080. É uma convenção comum para usuários não-root.

  • Encapsular dependências. Certifique-se de que todas as dependências para o aplicativo funcionar estejam encapsuladas na imagem do contêiner do Docker. O encapsulamento permite que o aplicativo seja implantado de forma confiável em uma ampla gama de hosts.

  • Escolha as imagens de base certas. A imagem base escolhida depende do seu ambiente de implantação. Se você estiver implantando em Aplicativos de Contêiner, por exemplo, precisará usar imagens do Linux Docker.

Por exemplo, a implementação de referência usa um processo de compilação de vários estágios . Os estágios iniciais compilam e compilam o aplicativo usando uma imagem SDK completa (mcr.microsoft.com/dotnet/sdk:8.0-jammy). A imagem de tempo de execução final é criada a partir da imagem base, o chiseled que exclui o SDK e os artefatos de compilação. O serviço é executado como um usuário não raiz (USER $APP_UID) e expõe a porta 8080. As dependências necessárias para que o aplicativo opere estão incluídas na imagem do Docker, como evidenciado pelos comandos para copiar arquivos de projeto e restaurar pacotes. O uso de imagens baseadas em Linux (mcr.microsoft.com/dotnet/aspnet:8.0-jammy-chiseled) garante a compatibilidade com Container Apps, o que requer contêineres Linux para implantação.

# Build in a separate stage to avoid copying the SDK into the final image
FROM mcr.microsoft.com/dotnet/sdk:8.0-jammy AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src

# Restore packages
COPY ["Relecloud.TicketRenderer/Relecloud.TicketRenderer.csproj", "Relecloud.TicketRenderer/"]
COPY ["Relecloud.Messaging/Relecloud.Messaging.csproj", "Relecloud.Messaging/"]
COPY ["Relecloud.Models/Relecloud.Models.csproj", "Relecloud.Models/"]
RUN dotnet restore "./Relecloud.TicketRenderer/Relecloud.TicketRenderer.csproj"

# Build and publish
COPY . .
WORKDIR "/src/Relecloud.TicketRenderer"
RUN dotnet publish "./Relecloud.TicketRenderer.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false

# Chiseled images contain only the minimal set of packages needed for .NET 8.0
FROM mcr.microsoft.com/dotnet/aspnet:8.0-jammy-chiseled AS final
WORKDIR /app
EXPOSE 8080

# Copy the published app from the build stage
COPY --from=build /app/publish .

# Run as non-root user
USER $APP_UID
ENTRYPOINT ["dotnet", "./Relecloud.TicketRenderer.dll"]

Implantar a implementação de referência

Implante a implementação de referência do Modern Web App Pattern for .NET. Há instruções para desenvolvimento e implantação de produção no repositório. Depois de implantar, você pode simular e observar padrões de projeto.

Diagrama mostrando a arquitetura da implementação de referência.Figura 3. Arquitetura da implementação de referência. Baixe um arquivo Visio desta arquitetura.