Partilhar via


Melhorar o desempenho e a fiabilidade do Azure Functions

Este artigo fornece orientação para melhorar o desempenho e a confiabilidade de seus aplicativos de função sem servidor . Para obter um conjunto mais geral de práticas recomendadas do Azure Functions, consulte Práticas recomendadas do Azure Functions.

A seguir estão as práticas recomendadas sobre como você cria e arquiteta suas soluções sem servidor usando o Azure Functions.

Evite funções de longa duração

Funções extensas e de longa duração podem causar problemas inesperados de timeout. Para saber mais sobre os timeouts num plano de hospedagem específico, consulte Tempo de limite de execução da aplicação de função.

Uma função pode se tornar grande devido a muitas dependências Node.js. A importação de dependências também pode causar maiores tempos de carregamento que resultam em tempos limite inesperados. As dependências são carregadas explícita e implicitamente. Um único módulo carregado pelo seu código pode carregar seus próprios módulos adicionais.

Sempre que possível, refatore funções grandes em conjuntos de funções menores que trabalham juntos e retornam respostas rapidamente. Por exemplo, uma função webhook ou de gatilho HTTP pode exigir uma resposta de confirmação dentro de um determinado limite de tempo; é comum que os webhooks exijam uma resposta imediata. Você pode passar a carga útil do gatilho HTTP para uma fila a ser processada por uma função de gatilho de fila. Essa abordagem permite adiar o trabalho real e retornar uma resposta imediata.

Certifique-se de que as tarefas em segundo plano foram concluídas

Quando sua função inicia quaisquer tarefas, retornos de chamada, threads, processos, eles devem ser concluídos antes que seu código de função retorne. Como o Functions não rastreia esses threads em segundo plano, o desligamento do site pode ocorrer independentemente do status do thread em segundo plano, o que pode causar um comportamento não intencional em suas funções.

Por exemplo, se uma função iniciar uma tarefa em segundo plano e retornar uma resposta bem-sucedida antes que a tarefa seja concluída, o tempo de execução do Functions considerará a execução como concluída com êxito, independentemente do resultado da tarefa em segundo plano. Se essa tarefa em segundo plano estiver executando um trabalho essencial, ela poderá ser interrompida devido ao desligamento do site, deixando esse trabalho em um estado desconhecido.

Comunicação entre funções

As Funções Duráveis e as Aplicações Lógicas do Azure foram criadas para gerir transições de estado e comunicação entre várias funções.

Se não estiver usando funções duráveis ou aplicativos lógicos para integrar com várias funções, é melhor usar filas de armazenamento para comunicação entre funções. A principal razão é que as filas de armazenamento são mais baratas e muito mais fáceis de provisionar do que outras opções de armazenamento.

As mensagens individuais em uma fila de armazenamento são limitadas em tamanho a 64 KB. Caso necessite passar mensagens maiores entre funções, pode usar uma fila do Azure Service Bus para suportar tamanhos de mensagens até 256 KB na camada standard e até 100 MB na camada premium.

Os tópicos do Service Bus são úteis se necessitar de filtragem de mensagens antes do processamento.

Os hubs de eventos são úteis para suportar comunicações de alto volume.

Escrever funções para serem sem monitoração de estado

As funções devem ser sem estado e idempotentes, se possível. Associe todas as informações de estado necessárias aos seus dados. Por exemplo, um pedido que está a ser processado provavelmente terá um membro associado state. Uma função pode processar uma ordem com base nesse estado, enquanto a própria função permanece sem estado.

Funções idempotentes são especialmente recomendadas associadas a gatilhos de temporizador. Por exemplo, se você tem algo que absolutamente deve ser executado uma vez por dia, escreva-o para que possa ser executado a qualquer momento durante o dia com os mesmos resultados. A função pode terminar quando não houver trabalho para um determinado dia. Além disso, se uma execução anterior não foi concluída, a próxima execução deve continuar de onde parou. Isso é particularmente importante para ligações baseadas em mensagens que tentam novamente em caso de falha. Para obter mais informações, consulte Projetando o Azure Functions para entrada idêntica.

Escrever funções defensivas

Suponha que sua função pode encontrar uma exceção a qualquer momento. Projete suas funções com a capacidade de continuar a partir de um ponto de falha anterior durante a próxima execução. Considere um cenário que exija as seguintes ações:

  1. Consulta de 10.000 linhas em um banco de dados.
  2. Crie uma mensagem de espera para cada uma dessas linhas para processamento posterior.

Dependendo da complexidade do seu sistema, você pode ter: serviços a jusante envolvidos se comportando mal, interrupções de rede ou limites de cota atingidos, etc. Tudo isto pode afetar a sua função a qualquer momento. Você precisa projetar suas funções para estar preparado para isso.

Como seu código reage se ocorrer uma falha após inserir 5.000 desses itens em uma fila para processamento? Rastreie itens em um conjunto que você concluiu. Caso contrário, poderá inseri-los novamente da próxima vez. Esta dupla inserção pode ter um sério impacto no seu fluxo de trabalho, por isso torne as suas funções idempotentes.

Se um item de fila já tiver sido processado, permita que sua função seja um no-op.

Aproveite as medidas defensivas já fornecidas para os componentes que você usa na plataforma Azure Functions. Por exemplo, consulte Manipulando mensagens de fila suspeita na documentação de gatilhos e associações da fila de armazenamento do Azure.

Para funções baseadas em HTTP, considere estratégias de controle de versão de API com o Gerenciamento de API do Azure. Por exemplo, se você precisar atualizar seu aplicativo de função baseado em HTTP, implante a nova atualização em um aplicativo de função separado e use revisões ou versões do Gerenciamento de API para direcionar os clientes para a nova versão ou revisão. Quando todos os clientes estiverem usando a versão ou revisão e não sobrarem mais execuções no aplicativo de função anterior, você poderá desprovisionar o aplicativo de função anterior.

Práticas recomendadas de organização de funções

Como parte de sua solução, você pode desenvolver e publicar várias funções. Essas funções geralmente são combinadas em um único aplicativo de função, mas também podem ser executadas em aplicativos de função separados. Nos planos de hospedagem Premium e dedicado (Serviço de Aplicativo), vários aplicativos funcionais também podem compartilhar os mesmos recursos executando no mesmo plano. A forma como você agrupa suas funções e aplicativos de função pode afetar o desempenho, o dimensionamento, a configuração, a implantação e a segurança de sua solução geral. Não há regras que se apliquem a todos os cenários, portanto, considere as informações nesta seção ao planejar e desenvolver suas funções.

Organizar funções para desempenho e dimensionamento

Cada função que cria tem uma pegada de memória. Embora essa pegada seja geralmente pequena, ter muitas funções dentro de um aplicativo de função pode levar a uma inicialização mais lenta do seu aplicativo em novas instâncias. Isso também significa que o uso geral de memória do seu aplicativo de função pode ser maior. É difícil dizer quantas funções devem estar em um único aplicativo, o que depende da sua carga de trabalho específica. No entanto, se sua função armazena muitos dados na memória, considere ter menos funções em um único aplicativo.

Se você executar vários aplicativos funcionais em um único plano Premium ou plano dedicado (Serviço de Aplicativo), todos esses aplicativos compartilharão os mesmos recursos alocados para o plano. Se você tiver um aplicativo de função que tenha um requisito de memória muito maior do que os outros, ele usará uma quantidade desproporcional de recursos de memória em cada instância na qual o aplicativo é implantado. Como isso pode deixar menos memória disponível para os outros aplicativos em cada instância, convém executar um aplicativo de função de alta memória como este em seu próprio plano de hospedagem separado.

Observação

Ao usar o plano de consumo, recomendamos que você sempre coloque cada aplicativo em seu próprio plano, já que os aplicativos são dimensionados de forma independente. Para obter mais informações, consulte Vários aplicativos no mesmo plano.

Considere se deseja agrupar funções com perfis de carga diferentes. Por exemplo, se você tiver uma função que processa milhares de mensagens de fila e outra que é chamada apenas ocasionalmente, mas tem altos requisitos de memória, convém implantá-las em aplicativos de função separados para que eles obtenham seus próprios conjuntos de recursos e sejam dimensionados independentemente uns dos outros.

Organizar funções para configuração e implantação

Os aplicativos de função têm um host.json arquivo, que é usado para configurar o comportamento avançado de gatilhos de função e o tempo de execução do Azure Functions. As alterações no host.json arquivo se aplicam a todas as funções dentro do aplicativo. Se você tiver algumas funções que precisam de configurações personalizadas, considere movê-las para seu próprio aplicativo de funções.

Todas as funções em seu projeto local são implantadas juntas como um conjunto de arquivos para seu aplicativo de função no Azure. Talvez seja necessário implantar funções individuais separadamente ou usar recursos como slots de implantação para algumas funções e não para outras. Nesses casos, você deve implantar essas funções (em projetos de código separados) em aplicativos de função diferentes.

Organizar funções por privilégio

As cadeias de conexão e outras credenciais armazenadas nas configurações do aplicativo dão a todas as funções no aplicativo de função o mesmo conjunto de permissões no recurso associado. Considere minimizar o número de funções com acesso a credenciais específicas movendo funções que não usam essas credenciais para um aplicativo de função separado. Você sempre pode usar técnicas como encadeamento de funções para passar dados entre funções em diferentes aplicativos de função.

Práticas recomendadas de escalabilidade

Há vários fatores que afetam a escala das instâncias do seu aplicativo de função. Os detalhes são fornecidos na documentação para dimensionamento de funções. A seguir estão algumas práticas recomendadas para garantir a escalabilidade ideal de um aplicativo funcional.

Partilhe e faça a gestão das ligações

Reutilize conexões com recursos externos sempre que possível. Veja como gerenciar conexões no Azure Functions.

Evite partilhar contas de armazenamento

Ao criar um aplicativo funcional, você deve associá-lo a uma conta de armazenamento. A conexão da conta de armazenamento é mantida na configuração do aplicativo AzureWebJobsStorage.

Para maximizar o desempenho, use uma conta de armazenamento separada para cada aplicativo de função. Essa abordagem é particularmente importante quando você tem funções duráveis ou Hubs de eventos acionados, que geram um alto volume de transações de armazenamento. Quando a lógica do aplicativo interage com o Armazenamento do Azure, diretamente (usando o SDK de Armazenamento) ou por meio de uma das associações de armazenamento, você deve usar uma conta de armazenamento dedicada. Por exemplo, se você tiver uma função acionada pelo hub de eventos gravando alguns dados no armazenamento de blobs, use duas contas de armazenamento: uma para o aplicativo de função e outra para os blobs que a função armazena.

Não misture código de teste e de produção na mesma aplicação de funções

As funções dentro de um aplicativo de função compartilham recursos. Por exemplo, a memória é compartilhada. Se você estiver usando um aplicativo de função em produção, não adicione funções e recursos relacionados ao teste a ele. Ele pode causar sobrecarga inesperada durante a execução do código de produção.

Tenha cuidado com o que você carrega em seus aplicativos de função de produção. A média da memória é calculada para cada função no contexto do aplicativo.

Se você tiver um assembly compartilhado referenciado em várias funções .NET, coloque-o em uma pasta compartilhada comum. Caso contrário, você pode implantar acidentalmente várias versões do mesmo binário que se comportam de forma diferente entre funções.

Não use registro detalhado no código de produção, que tem um impacto negativo no desempenho.

Utilize código assíncrono, mas evite bloquear chamadas

A programação assíncrona é uma prática recomendada, especialmente quando estão envolvidas operações de entrada/saída de bloqueio.

Em C#, evite sempre referenciar a propriedade Result ou chamar o método Wait numa instância de Task. Essa abordagem pode levar à exaustão do fio.

Sugestão

Se você planeja usar as ligações HTTP ou WebHook, planeje evitar o esgotamento da porta que pode ser causado pela instanciação incorreta do HttpClient. Para obter mais informações, consulte Como gerenciar conexões no Azure Functions.

Usar vários processos de trabalho

Por padrão, qualquer instância de host para Functions usa um único processo de trabalho. Para melhorar o desempenho, especialmente com runtimes de thread única como o Python, use o FUNCTIONS_WORKER_PROCESS_COUNT para aumentar o número de processos de trabalho por host (até 10). Em seguida, o Azure Functions tenta distribuir uniformemente invocações de função simultâneas entre esses trabalhadores.

FUNCTIONS_WORKER_PROCESS_COUNT aplica-se a cada anfitrião que as Funções criam quando aumentam horizontalmente a sua aplicação para satisfazer a procura.

Receba mensagens em lotes sempre que possível

Alguns gatilhos como o Hub de Eventos permitem receber um lote de mensagens em uma única invocação. O envio de mensagens em lote tem um desempenho muito melhor. Você pode configurar o tamanho máximo do lote no arquivo conforme detalhado em documentação de referênciahost.json

Para funções C#, você pode alterar o tipo para uma matriz fortemente tipada. Por exemplo, em vez de EventData sensorEvent, a assinatura de método poderia ser EventData[] sensorEvent. Para outros idiomas, terá de definir explicitamente a propriedade cardinality no seu function.json para many de forma a habilitar o processamento em lote conforme mostrado aqui.

Configurar comportamentos do anfitrião para lidar melhor com a simultaneidade

O host.json ficheiro na aplicação de função permite a configuração do tempo de execução do host e dos comportamentos de gatilho. Além dos comportamentos de envio em lote, você pode gerenciar a simultaneidade para vários gatilhos. Muitas vezes, ajustar os valores nessas opções pode ajudar cada instância a ser dimensionada adequadamente para as demandas das funções invocadas.

As configurações no arquivo host.json se aplicam a todas as funções dentro do aplicativo, dentro de uma única instância da função. Por exemplo, se você tivesse um aplicativo de função com duas funções HTTP e maxConcurrentRequests solicitações definidas como 25, uma solicitação para qualquer gatilho HTTP contaria para as 25 solicitações simultâneas compartilhadas. Quando esse aplicativo de função é dimensionado para 10 instâncias, as dez funções efetivamente permitem 250 solicitações simultâneas (10 instâncias * 25 solicitações simultâneas por instância).

Outras opções de configuração de host são encontradas no artigo de configuração dohost.json.

Próximos passos

Para obter mais informações, consulte os seguintes recursos: