Diretrizes de desempenho e escala para Hubs de Eventos e Azure Functions

Hubs de eventos do Azure
Azure Functions

Este artigo fornece orientação para otimizar a escalabilidade e o desempenho quando você usa os Hubs de Eventos do Azure e o Azure Functions juntos em seus aplicativos.

Função de agrupamento

Normalmente, uma função encapsula uma unidade de trabalho em um fluxo de processamento de eventos. Por exemplo, uma função pode transformar um evento em uma nova estrutura de dados ou enriquecer dados para aplicativos downstream.

No Functions, um aplicativo de funções fornece o contexto de execução para funções. Os comportamentos do aplicativo de funções se aplicam a todas as funções que o aplicativo de função hospeda. As funções em um aplicativo de funções são implantadas e escaladas juntas. Todas as funções de um aplicativo de funções precisam ser da mesma linguagem.

A forma como você agrupa funções em aplicativos de função pode afetar o desempenho e os recursos de dimensionamento de seus aplicativos de função. Você pode agrupar de acordo com os direitos de acesso, a implantação e os padrões de uso que invocam seu código.

Para obter orientação sobre as práticas recomendadas do Functions para agrupamento e outros aspectos, consulte Práticas recomendadas para uso confiável do Azure Functions e Melhore o desempenho e a confiabilidade do Azure Functions.

A lista a seguir é uma orientação para agrupar funções. A orientação considera aspectos de armazenamento e grupo de consumidores:

  • Hospede uma única função em um aplicativo de funções: Caso os Event Hubs acionem uma função, você pode, de modo a reduzir a contenção com outras funções, isolar a função em seu próprio aplicativo de funções. O isolamento é especialmente importante se as outras funções forem intensivas em CPU ou memória. Essa técnica ajuda porque cada função tem seu próprio volume de memória e padrões de uso que podem afetar diretamente o dimensionamento do aplicativo de função que a hospeda.

  • Dê a cada aplicativo de função sua própria conta de armazenamento: evite compartilhar contas de armazenamento entre aplicativos de função. Além disso, se um aplicativo de função usar uma conta de armazenamento, não use essa conta para outras operações ou necessidades de armazenamento. Pode ser especialmente importante evitar o compartilhamento de contas de armazenamento para funções acionadas pelos Hubs de Eventos, pois essas funções podem ter um alto volume de transações de armazenamento devido ao ponto de verificação.

  • Crie um grupo de consumidores dedicado para cada aplicativo de função: um grupo de consumidores é uma perspectiva de um hub de eventos. Diferentes grupos de consumidores têm visões diferentes, o que significa que os estados, posições e compensações podem variar. Os grupos de consumidores possibilitam que vários aplicativos consumidores tenham suas próprias visualizações do fluxo de eventos e leiam o fluxo de forma independente em seu próprio ritmo e com seus próprios offsets. Para obter mais informações sobre grupos de consumidores, consulte Recursos e terminologia nos Hubs de Eventos do Azure .

    Um grupo de consumidores tem um ou mais aplicativos de consumidor associados a ele, e um aplicativo de consumidor pode usar um ou mais grupos de consumidores. Em uma solução de processamento de fluxo, cada aplicativo consumidor equivale a um grupo de consumidores. Um aplicativo de funções é um excelente exemplo de um aplicativo destinado ao consumidor. O diagrama a seguir fornece um exemplo de dois aplicativos de função que leem de um hub de eventos, onde cada aplicativo tem seu próprio grupo de consumidores dedicado:

    Grupos de clientes dedicados para cada aplicativo de função

    Não compartilhe grupos de consumidores entre aplicativos de função e outros aplicativos de consumidor. Cada aplicativo de funções deve ser uma aplicação distinta com o seu próprio grupo de consumidores de eventos atribuído, a fim de garantir a integridade do offset de cada consumidor e simplificar as dependências em uma arquitetura de streaming de eventos. Além de fornecer a cada função acionada pelo hub de eventos seu próprio aplicativo de função e conta de armazenamento, essa configuração também ajuda a definir a base para o desempenho e o dimensionamento ideais.

Planos de hospedagem de funções

Existem várias opções de hospedagem para aplicativos de funções e é importante revisar seus recursos. Para obter informações sobre essas opções de hosting, consulte Opções de hosting do Azure Functions. Observe como as opções aumentam.

O plano de consumo Flex é o plano de hospedagem sem servidor recomendado para Azure Functions, incluindo cargas de trabalho controladas por eventos disparadas pelos Hubs de Eventos. Ele dimensiona cada função disparada por Hubs de Eventos em instâncias próprias, com base em regras de dimensionamento orientadas ao destino para Hubs de Eventos.

Os planos Premium e Dedicado são frequentemente usados para hospedar aplicativos e funções de várias funções que consomem mais CPU e memória. Com o plano Dedicado, você executa suas funções em um plano de Serviço de Aplicativos do Azure com taxas regulares de Serviço de Aplicativos. Todos os aplicativos de funções nesses planos compartilham os recursos alocados para o plano. Se as funções tiverem perfis de carga diferentes ou requisitos exclusivos, é melhor hospedá-las em planos diferentes, especialmente em aplicativos de processamento de fluxo.

Os Aplicativos de Contêiner do Azure fornecem suporte integrado para desenvolver, implantar e gerenciar aplicativos de funções em contêineres no Azure Functions. Isso permite que você execute suas funções controladas por eventos em um ambiente totalmente gerenciado baseado em Kubernetes com suporte interno para monitoramento de software livre, mTLS, Dapr e KEDA.

Dimensionamento de Hubs de Eventos

Quando você implanta um namespace de Hubs de Eventos, há várias configurações importantes que devem ser definidas corretamente para garantir o desempenho e o dimensionamento máximos. Esta seção se concentra na camada Standard dos Hubs de Eventos e nos recursos exclusivos dessa camada que afetam o dimensionamento quando você também usa o Functions. Para obter mais informações sobre camadas de Hubs de Eventos, consulte Camada Básica vs. Padrão vs. Premium vs. Dedicada.

Um namespace de Hubs de Eventos corresponde a um cluster Kafka. Para obter informações sobre como os Hubs de Eventos e o Kafka se relacionam, consulte O que é o Hubs de Eventos do Azure para Apache Kafka.

Noções básicas sobre unidades de produtividade (TUs)

Na camada Standard dos Hubs de Eventos, a taxa de transferência é classificada como a quantidade de dados que entra e é lida do namespace por unidade de tempo. As TUs são unidades pré-adquiridas de capacidade de produtividade.

As TUs são cobradas por hora.

Todos os hubs de eventos em um namespace compartilham as TUs. Para calcular corretamente as necessidades de capacidade, você deve considerar todos os aplicativos e serviços, tanto editores quanto consumidores. As funções afetam o número de bytes e eventos que são publicados e lidos em um hub de eventos.

A ênfase para determinar o número de TUs está no ponto de ingresso. No entanto, o agregado para os aplicativos de consumo, incluindo a taxa na qual esses eventos são processados, também deve ser incluído no cálculo.

Para obter mais informações sobre unidades de taxa de transferência dos Hubs de Eventos, consulte Unidades de produtividade.

Escale verticalmente com a ampliação automática

A expansão automática pode ser habilitada em um namespace do Event Hubs para acomodar situações em que a carga excede o número configurado de TUs. O uso da ampliação automática evita a limitação do aplicativo e ajuda a garantir que o processamento, incluindo a ingestão de eventos, continue sem interrupções. Como a configuração de TU afeta os custos, o uso da Auto-inflação ajuda a mitigar inquietações sobre o superprovisionamento.

O recurso de auto-inflate dos Hubs de Eventos é frequentemente confundido com o dimensionamento automático, especialmente no contexto de soluções sem servidor. No entanto, a ampliação automática, ao contrário do dimensionamento automático, não reduz verticalmente quando a capacidade adicional não é mais necessária.

Se o aplicativo precisar de capacidade que exceda o número máximo permitido de TUs, considere usar as camadas Premium ou Dedicada dos Hubs de Eventos.

Partições e funções simultâneas

Quando um hub de eventos é criado, o número de partições deve ser especificado. A contagem de partições permanece fixa e não pode ser alterada, exceto nas camadas Premium e Dedicada. Quando os Hubs de Eventos acionam aplicativos de funções, é possível que o número de instâncias simultâneas seja igual ao número de partições.

Nos planos de hosting Consumo e Premium, as instâncias do aplicativo de função são escaladas horizontalmente de forma dinâmica para atender ao número de partições, se necessário. O plano de hospedagem Dedicado executa funções em um plano do Serviço de Aplicativo e requer que você configure manualmente suas instâncias ou configure um esquema de dimensionamento automático. Para obter mais informações, consulte Planos de hospedagem Dedicado para o Azure Functions.

Em última análise, um relacionamento um-para-um entre o número de partições e instâncias de função, ou consumidores, é o objetivo ideal para a taxa máxima de processamento em uma solução de processamento de streams. Para atingir o paralelismo ideal, tenha vários consumidores em um grupo de consumidores. Para o Functions, esse objetivo se traduz em muitas instâncias de uma função no plano. O resultado é referido como paralelismo em nível de partição ou o grau máximo de paralelismo, conforme mostrado no diagrama a seguir:

Grau máximo de paralelismo

Pode parecer fazer sentido configurar o maior número possível de partições para atingir a taxa de produtividade máxima e levar em conta a possibilidade de um volume maior de eventos. No entanto, há vários fatores importantes a serem considerados ao configurar muitas partições:

  • Mais partições podem levar a mais taxa de produtividade: como o grau de paralelismo é o número de consumidores (instâncias de função), quanto mais partições houver, maior será a taxa de produtividade simultânea. Esse fato é importante quando você compartilha um número designado de TUs para um hub de eventos com outros aplicativos de consumo.
  • Mais funções podem exigir mais memória: à medida que o número de instâncias de função aumenta, o mesmo acontece com o volume de memória dos recursos no plano. Em algum momento, muitas partições podem deteriorar o desempenho dos consumidores.
  • Há um risco de contra-pressão dos serviços downstream: À medida que mais capacidade de processamento é gerada, corre-se o risco de sobrecarregar os serviços downstream ou de receber contra-pressão deles. A distribuição ao consumidor deve ser levada em conta ao considerar as consequências para os recursos circundantes. As possíveis consequências incluíam limitação imposta por outros serviços, saturação da rede e outras formas de disputa por recursos.
  • As partições podem ser preenchidas esparsamente: a combinação de muitas partições e um baixo volume de eventos pode levar a dados que são distribuídos esparsamente entre partições. Em vez disso, um número menor de partições pode fornecer melhor desempenho e uso de recursos.

Disponibilidade e consistência

Quando uma chave de partição ou ID não é especificada, os Hubs de Eventos roteiam um evento de entrada para a próxima partição disponível. Essa prática fornece alta disponibilidade e ajuda a aumentar a taxa de produtividade para os consumidores.

Quando a ordenação de um conjunto de eventos é necessária, o produtor de eventos pode especificar que uma partição específica deve ser usada para todos os eventos do conjunto. O aplicativo consumidor que lê a partir da partição recebe os eventos na ordem correta. Essa compensação fornece consistência, mas compromete a disponibilidade. Não use essa abordagem, a menos que a ordem dos eventos deva ser preservada.

Para o Functions, a ordenação é obtida quando os eventos são publicados em uma partição específica e uma função acionada pelos Hubs de Eventos obtém uma concessão para a mesma partição. Atualmente, a capacidade de configurar uma partição com a associação de saída dos Hubs de Eventos não é suportada. Em vez disso, a melhor abordagem é usar um dos SDKs dos Hubs de Eventos para publicar em uma partição específica.

Para obter mais informações sobre como os Hubs de Eventos oferecem suporte à disponibilidade e consistência, consulte Disponibilidade e consistência nos Hubs de Eventos.

Gatilho dos Hubs de Eventos

Esta seção se concentra nas configurações e considerações para otimizar funções que os Hubs de Eventos disparam. Os fatores incluem processamento em lote, amostragem e recursos relacionados que influenciam o comportamento de uma vinculação de gatilho de hub de eventos.

Envio em lote para funções disparadas

Você pode configurar funções que um hub de eventos dispara para processar um lote de eventos ou um evento de cada vez. O processamento de um lote de eventos pode ser mais eficiente quando reduz parte da sobrecarga de invocações de função. A menos que você precise processar apenas um evento único, sua função deve ser configurada para processar vários eventos quando invocada.

A habilitação do envio em lote para a vinculação de gatilho do Event Hubs varia entre as linguagens:

  • JavaScript, Python e outras linguagens habilitam o envio em lote quando a propriedade cardinalidade é definida como muitos no arquivo function.json da função.
  • No C#, a cardinalidade é configurada automaticamente quando uma matriz é designada para o tipo no atributo EventHubTrigger.

Para obter mais informações sobre como o envio em lote está habilitado, consulte Gatilhos de Hubs de Eventos do Azure para o Azure Functions.

Configurações do gatilho

Várias definições de configuração no arquivo host.json desempenham um papel fundamental nas características de desempenho da vinculação do gatilho do Event Hubs para o Functions:

  • maxEventBatchSize: essa configuração representa o número máximo de eventos que a função pode receber quando é chamada. Se o número de eventos recebidos for menor que esse valor, a função ainda será invocada com quantos eventos estiverem disponíveis. Não é possível definir um tamanho mínimo de lote.
  • prefetchCount: a contagem de pré-busca é uma das configurações mais importantes ao otimizar o desempenho. O canal AMQP subjacente faz referência a esse valor para determinar quantas mensagens buscar e armazenar em cache para o cliente. A contagem de pré-busca deve ser maior ou igual ao valor maxEventBatchSize e geralmente é definida como um múltiplo desse valor. Definir esse valor para um número menor que a configuração maxEventBatchSize pode prejudicar o desempenho.
  • batchCheckpointFrequency: à medida que sua função processa lotes, esse valor determina a taxa na qual os pontos de verificação são criados. O valor padrão é 1, o que significa que há um ponto de verificação sempre que uma função processa um único lote com êxito. Um ponto de verificação é criado no nível da partição para cada leitor no grupo de consumidores. Para obter informações sobre como essa configuração influencia repetições e novas tentativas de eventos, consulte Função do Azure disparada pelo hub de eventos: repetições e novas tentativas (postagem de blog).

Faça vários testes de desempenho para determinar os valores a serem definidos para a associação ao gatilho. Recomendamos que você altere as configurações de forma incremental e meça constantemente para ajustar essas opções. Os valores padrão são um ponto de partida razoável para a maioria das soluções de processamento de eventos.

Definindo o ponto de verificação

Os pontos de verificação marcam ou confirmam as posições do leitor em uma sequência de eventos de partição. É responsabilidade do host do Functions criar um ponto de verificação à medida que os eventos são processados e a configuração da frequência do ponto de verificação em lote é atendida. Para obter mais informações sobre pontos de verificação, consulte Recursos e terminologia nos Hubs de Eventos do Azure .

Os conceitos a seguir podem ajudar você a entender a relação entre o ponto de verificação e a maneira como sua função processa eventos:

  • As exceções ainda contam para o sucesso: se o processo da função não falhar durante o processamento de eventos, a conclusão da função será considerada bem-sucedida, mesmo que tenham ocorrido exceções. Quando a função é concluída, o host do Functions avalia batchCheckpointFrequency. Se chegar a hora de um ponto de verificação, ele criará um, independentemente de haver exceções. O fato de que as exceções não afetam o ponto de verificação não deve afetar o uso adequado da verificação e do tratamento de exceções.
  • A frequência do lote é importante: em soluções de streaming de eventos de alto volume, pode ser vantajoso alterar a configuração batchCheckpointFrequency para um valor maior que 1. Aumentar esse valor pode reduzir a taxa de criação de pontos de verificação e, como consequência, o número de operações de E/S de armazenamento.
  • Podem acontecer repetições: cada vez que uma função é invocada com a associação de gatilho dos Hubs de Eventos, ela usa o ponto de verificação mais recente para determinar onde retomar o processamento. O deslocamento de cada consumidor é armazenado no nível de partição para cada grupo de consumidores. Repetições ocorrem quando um ponto de verificação não ocorre durante a última invocação da função, e a função é chamada novamente. Para obter mais informações sobre duplicatas e técnicas de eliminação de duplicação, consulte Idempotência.

Compreender o ponto de verificação torna-se fundamental ao considerar as práticas recomendadas para tratamento de erros e novas tentativas, um tópico que será discutido mais adiante neste artigo.

Amostragem de telemetria

O Functions fornece suporte interno para o Application Insights, uma extensão do Azure Monitor que fornece recursos de monitoramento de desempenho do aplicativo. Com esse recurso, você pode registrar informações sobre atividades de função, desempenho, exceções de tempo de execução e muito mais. Para obter mais informações, consulte visão geral do Application Insights.

Essa funcionalidade oferece as principais opções de configuração que afetam o desempenho. Algumas das configurações e considerações importantes para monitoramento e desempenho são:

  • Habilitar amostragem de telemetria: para cenários de alta taxa de transferência, você deve avaliar a quantidade de telemetria e as informações necessárias. Considere o uso do recurso de amostragem de telemetria no Application Insights para evitar a degradação do desempenho de sua função com telemetria e métricas desnecessárias.
  • Definir configurações de agregação: examine e configure a frequência de agregação e envio de dados para o Application Insights. Essa definição de configuração está no arquivo host.json junto com muitas outras opções relacionadas à amostragem e ao registro em log. Para obter mais informações, consulte Configurar o agregador.
  • Desabilitar AzureWebJobDashboard: para aplicativos destinados à versão 1.x do tempo de execução do Functions, essa configuração armazena a cadeia de conexão para uma conta de armazenamento que o SDK do Azure usa para reter logs para o painel WebJobs. Se o Application Insights for usado em vez do painel WebJobs, essa configuração deverá ser removida. Para obter mais informações, consulte AzureWebJobsDashboard.

Quando o Application Insights é habilitado sem amostragem, toda a telemetria é enviada. O envio de dados sobre todos os eventos pode ter um efeito prejudicial no desempenho da função, principalmente em cenários de streaming de eventos de alta taxa de transferência.

Aproveitar a amostragem e avaliar continuamente a quantidade adequada de telemetria necessária para o monitoramento é essencial para o desempenho ideal. A telemetria deve ser usada para avaliação geral da integridade da plataforma e para solução de problemas ocasionais, não para capturar métricas de negócios principais. Para obter mais informações, confira Configurar amostragem.

Vinculação de saída

Use a associação de saída dos Hubs de Eventos para o Azure Functions para simplificar a publicação em um fluxo de eventos a partir de uma função. Os benefícios de usar essa vinculação incluem:

  • Gerenciamento de recursos: O controle de enlace lida com os ciclos de vida do cliente e da conexão para você, reduzindo potenciais problemas que possam surgir com a exaustão de portas e o gerenciamento do pool de conexões.
  • Menos código: a associação abstrai o SDK subjacente e reduz a quantidade de código necessária para publicar eventos. Ele ajuda você a escrever e manter o código com menos linhas clichês.
  • Processamento em lote: para vários idiomas, o processamento em lote é oferecido para publicar com eficiência em um fluxo de eventos. O envio em lote pode melhorar o desempenho e ajudar a simplificar o código que envia os eventos.

É altamente recomendável que você revise a lista de Linguagens que o Functions oferece suporte e os guias do desenvolvedor para essas linguagens. A seção Vínculos de cada linguagem fornece exemplos detalhados e documentação.

Envio em lote ao publicar eventos

Se sua função publicar apenas um único evento, configurar a associação para retornar um valor é uma abordagem comum que é útil se a execução da função sempre terminar com uma instrução que envia o evento. Essa técnica só deve ser usada para funções síncronas que retornam apenas um evento.

Incentiva-se o envio em lote para melhorar o desempenho quando enviados vários eventos para um fluxo. O envio em lote permite que a vinculação publique eventos da forma mais eficiente possível.

O suporte para usar a associação de saída para enviar vários eventos aos Hubs de Eventos está disponível em C#, Java, Python e JavaScript.

Gerar vários eventos com o modelo In-process (C#)

Use os tipos ICollector e IAsyncCollector ao enviar vários eventos de uma função em C#.

  • O método ICollector<T>.Add() pode ser usado em funções síncronas e assíncronas. Ele executa a operação de adição assim que é chamado.
  • O método IAsyncCollector<T>.AddAsync() prepara os eventos a serem publicados no fluxo de eventos. Se você escrever uma função assíncrona, deverá usar IAsyncCollector para gerenciar melhor os eventos publicados.

Para obter exemplos de como usar C# para publicar eventos únicos e múltiplos, consulte Associação de saída dos Hubs de Eventos do Azure para o Azure Functions.

Gerar vários eventos com o modelo de trabalho isolado (C#)

Dependendo da versão do runtime do Functions, o modelo de trabalho isolado dará suporte a diferentes tipos de parâmetros que são passados para a vinculação de saída. Para vários eventos, uma matriz é usada para encapsular o conjunto. É recomendável examinar os atributos de associação de saída e os detalhes de uso do modelo Isolado e anotar as diferenças entre as versões de extensão.

Limitação e pressão de retorno

As considerações de limitação se aplicam à associação de saída, não apenas para Hubs de Eventos, mas também para serviços do Azure, como o Azure Cosmos DB. É importante se familiarizar com os limites e cotas que se aplicam a esses serviços e planejar de acordo.

Para lidar com erros em fluxo descendente com o modelo Em-Processo, você pode encapsular AddAsync e FlushAsync em um manipulador de exceções para .NET Functions para capturar exceções do IAsyncCollector. Outra opção é usar diretamente os SDKs dos Event Hubs em vez de usar vinculações de saída.

Se você estiver aproveitando o modelo Isolado para funções, o tratamento estruturado de exceções deverá ser usado para capturar exceções de forma responsável ao retornar os valores de saída.

Código de função

Esta seção aborda as principais áreas que devem ser consideradas ao escrever código para processar eventos em uma função acionada pelos Hubs de Eventos.

Programação assíncrona

Recomendamos que você escreva sua função para usar código assíncrono e evitar bloquear chamadas, especialmente quando estão envolvidas chamadas de E/S.

Veja a seguir as diretrizes que você deve seguir ao escrever uma função para processar de forma assíncrona:

  • Todos assíncronos ou todos síncronos: se uma função estiver configurada para ser executada de forma assíncrona, todas as chamadas de E/S deverão ser assíncronas. Na maioria dos casos, o código parcialmente assíncrono é pior do que o código totalmente síncrono. Escolha entre assíncrono ou síncrono e mantenha a escolha até o fim.
  • Evite chamadas de bloqueio: as chamadas de bloqueio retornam ao chamador somente após a conclusão da chamada, em contraste com as chamadas assíncronas que retornam imediatamente. Um exemplo em C# seria chamar Task.Result ou Task.Wait em uma operação assíncrona.

Mais informações sobre chamadas de bloqueio

Usar chamadas de bloqueio para operações assíncronas pode levar ao esgotamento do pool de threads e fazer com que o processo da função falhe. A falha acontece porque uma chamada de bloqueio requer que outro thread seja criado para compensar a chamada original que agora está aguardando. Como resultado, ele requer duas vezes mais threads para concluir a operação.

Evitar essa abordagem de síncrono sobre assíncrono é especialmente importante quando os Hubs de Eventos estão envolvidos, porque uma falha de função não atualiza o ponto de verificação. Na próxima vez que a função for invocada, ela pode acabar nesse ciclo e parecer estar presa ou se mover lentamente à medida que as execuções da função eventualmente expiram.

A solução de problemas desse fenômeno geralmente começa com a revisão das configurações de gatilho e a execução de experimentos que podem envolver o aumento da contagem de partições. As investigações também podem levar à alteração de várias das opções de agrupamento em lote, como o tamanho máximo do lote ou a contagem de pré-busca. A impressão é que é um problema de taxa de transferência ou configuração que precisa ser ajustada adequadamente. No entanto, o problema principal está no próprio código e deve ser resolvido lá.

Colaboradores

Esse artigo é mantido pela Microsoft. Ele foi escrito originalmente pelos colaboradores a seguir.

Autor principal:

Para ver perfis não públicos do LinkedIn, entre no LinkedIn.

Próximas etapas

Antes de continuar, considere revisar estes artigos relacionados: