Melhorar o desempenho e a fiabilidade do Funções do Azure

Este artigo fornece orientações para melhorar o desempenho e a fiabilidade das suas aplicações de funções sem servidor . Para obter um conjunto mais geral de melhores práticas Funções do Azure, veja Funções do Azure melhores práticas.

Seguem-se as melhores práticas na forma como cria e arquiteta as suas soluções sem servidor com Funções do Azure.

Evitar funções de execução prolongada

As funções grandes e de execução prolongada podem causar problemas de tempo limite inesperados. Para saber mais sobre os tempos limite de um determinado plano de alojamento, veja Duração do tempo limite da aplicação de funções.

Uma função pode tornar-se grande devido a muitas dependências Node.js. A importação de dependências também pode causar um aumento dos tempos de carga que resultam em tempos limite inesperados. As dependências são carregadas explicitamente e implicitamente. Um único módulo carregado pelo código pode carregar os seus próprios módulos adicionais.

Sempre que possível, refatorize funções grandes em conjuntos de funções mais pequenos que funcionam em conjunto e devolvem respostas rapidamente. Por exemplo, um webhook ou uma função de acionador HTTP pode exigir uma resposta de confirmação dentro de um determinado limite de tempo; É comum os webhooks exigirem uma resposta imediata. Pode passar o payload do acionador HTTP para uma fila para ser processado por uma função de acionador de fila. Esta abordagem permite-lhe adiar o trabalho real e devolver uma resposta imediata.

Certifique-se de que as tarefas em segundo plano estão concluídas

Quando a função inicia tarefas, chamadas de retorno, threads, processos, têm de ser concluídas antes de o código da função ser devolvido. Uma vez que as Funções não monitorizam estes threads em segundo plano, o encerramento do site pode ocorrer independentemente do estado do thread em segundo plano, o que pode causar comportamentos indesejados nas suas funções.

Por exemplo, se uma função iniciar uma tarefa em segundo plano e devolver uma resposta com êxito antes de a tarefa ser concluída, o runtime das Funções considera a execução como tendo sido concluída com êxito, independentemente do resultado da tarefa em segundo plano. Se esta tarefa em segundo plano estiver a realizar um trabalho essencial, poderá ser impedida pelo encerramento do site, deixando esse trabalho num estado desconhecido.

Comunicação entre funções

Durable Functions e o Azure Logic Apps são criados para gerir transições de estado e comunicação entre múltiplas funções.

Se não estiver a utilizar Durable Functions ou o Logic Apps para integrar com múltiplas funções, é melhor utilizar 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 aprovisionar do que outras opções de armazenamento.

As mensagens individuais numa fila de armazenamento têm um tamanho limitado a 64 KB. Se precisar de transmitir mensagens maiores entre funções, poderá ser utilizada uma fila de Azure Service Bus para suportar tamanhos de mensagens até 256 KB no escalão Standard e até 100 MB no escalão Premium.

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

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

Escrever funções para não ter estado

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

As funções Idempotent são especialmente recomendadas com acionadores de temporizador. Por exemplo, se tiver algo que tem de ser executado uma vez por dia, escreva-o para que possa ser executado em qualquer altura do dia com os mesmos resultados. A função pode sair quando não há trabalho para um dia específico. Além disso, se uma execução anterior não tiver sido concluída, a execução seguinte deverá retomar o ponto onde ficou. Isto é particularmente importante para enlaces baseados em mensagens que repitam a falha. Para obter mais informações, veja Estruturar Funções do Azure para entradas idênticas.

Escrever funções defensivas

Suponha que a função pode encontrar uma exceção em qualquer altura. Crie as 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 requer as seguintes ações:

  1. Consultar 10 000 linhas numa base de dados.
  2. Crie uma mensagem de fila para cada uma dessas linhas processar mais abaixo.

Dependendo da complexidade do seu sistema, pode ter envolvido serviços a jusante que se comportam mal, falhas de rede ou limites de quota atingidos, etc. Todas estas funções podem afetar a sua função em qualquer altura. Tem de estruturar as suas funções para estar preparado para o mesmo.

Como é que o código reage se ocorrer uma falha após inserir 5000 desses itens numa fila para processamento? Controlar itens num conjunto que concluiu. Caso contrário, poderá inseri-los novamente da próxima vez. Esta dupla inserção pode ter um impacto grave no fluxo de trabalho, pelo que torna as suas funções idempotentes.

Se um item de fila já tiver sido processado, permita que a função não funcione.

Tire partido das medidas defensivas já fornecidas para os componentes que utiliza na plataforma Funções do Azure. Por exemplo, veja Handling poison queue messages (Processar mensagens de fila de venenos ) na documentação para acionadores e enlaces da Fila de Armazenamento do Azure.

Para funções baseadas em HTTP, considere estratégias de controlo de versões de API com o Azure Gestão de API. Por exemplo, se tiver de atualizar a sua aplicação de funções baseada em HTTP, implemente a nova atualização numa aplicação de funções separada e utilize Gestão de API revisões ou versões para direcionar os clientes para a nova versão ou revisão. Assim que todos os clientes estiverem a utilizar a versão ou revisão e não existirem mais execuções na aplicação de funções anterior, pode desaprovisionar a aplicação de funções anterior.

Melhores práticas da organização de funções

Como parte da sua solução, pode desenvolver e publicar várias funções. Estas funções são frequentemente combinadas numa única aplicação de funções, mas também podem ser executadas em aplicações de funções separadas. Nos planos de alojamento Premium e dedicados (Serviço de Aplicações), várias aplicações de funções também podem partilhar os mesmos recursos ao executar no mesmo plano. A forma como agrupa as suas funções e aplicações de funções pode afetar o desempenho, o dimensionamento, a configuração, a implementação e a segurança da sua solução global. Não existem regras que se apliquem a todos os cenários, por isso, considere as informações nesta secção ao planear e desenvolver as suas funções.

Organizar funções para desempenho e dimensionamento

Cada função que criar tem um espaço de memória. Embora esta quantidade de espaço seja geralmente pequena, ter demasiadas funções numa aplicação de funções pode levar a um arranque mais lento da sua aplicação em novas instâncias. Também significa que a utilização geral da memória da sua aplicação de funções pode ser superior. É difícil dizer quantas funções devem estar numa única aplicação, o que depende da carga de trabalho específica. No entanto, se a sua função armazenar muitos dados na memória, considere ter menos funções numa única aplicação.

Se executar várias aplicações de funções num único plano Premium ou plano dedicado (Serviço de Aplicações), estas aplicações estão todas a partilhar os mesmos recursos alocados ao plano. Se tiver uma aplicação de funções com um requisito de memória muito mais elevado do que as outras, utiliza uma quantidade desproporcional de recursos de memória em cada instância na qual a aplicação é implementada. Uma vez que isto poderia deixar menos memória disponível para as outras aplicações em cada instância, poderá querer executar uma aplicação de funções de utilização de memória elevada como esta no seu próprio plano de alojamento separado.

Nota

Ao utilizar o plano de Consumo, recomendamos que coloque sempre cada aplicação no seu próprio plano, uma vez que as aplicações são dimensionadas de forma independente. Para obter mais informações, veja Várias aplicações no mesmo plano.

Considere se pretende agrupar funções com perfis de carga diferentes. Por exemplo, se tiver uma função que processa muitos milhares de mensagens de fila e outra chamada apenas ocasionalmente, mas que tenha requisitos de memória elevados, poderá querer implementá-las em aplicações de funções separadas para que obtenham os seus próprios conjuntos de recursos e sejam dimensionados de forma independente entre si.

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

As aplicações de funções têm um host.json ficheiro, que é utilizado para configurar o comportamento avançado dos acionadores de função e do Funções do Azure runtime. As alterações ao host.json ficheiro aplicam-se a todas as funções na aplicação. Se tiver algumas funções que precisam de configurações personalizadas, considere movê-las para a sua própria aplicação de funções.

Todas as funções no seu projeto local são implementadas em conjunto como um conjunto de ficheiros para a sua aplicação de funções no Azure. Poderá ter de implementar funções individuais separadamente ou utilizar funcionalidades como blocos de implementação para algumas funções e não para outras. Nestes casos, deve implementar estas funções (em projetos de código separados) em diferentes aplicações de funções.

Organizar funções por privilégio

As cadeias de ligação e outras credenciais armazenadas nas definições da aplicação fornecem a todas as funções na aplicação de funções o mesmo conjunto de permissões no recurso associado. Considere minimizar o número de funções com acesso a credenciais específicas ao mover funções que não utilizam essas credenciais para uma aplicação de funções separada. Pode sempre utilizar técnicas como o encadeamento de funções para transmitir dados entre funções em diferentes aplicações de funções.

Melhores práticas de escalabilidade

Existem vários fatores que afetam a forma como as instâncias da sua aplicação de funções são dimensionadas. Os detalhes são fornecidos na documentação para dimensionamento de funções. Seguem-se algumas das melhores práticas para garantir a escalabilidade ideal de uma aplicação de funções.

Partilhar e gerir ligações

Reutilizar ligações a recursos externos sempre que possível. Veja como gerir ligações no Funções do Azure.

Evitar a partilha de contas de armazenamento

Quando cria uma aplicação de funções, tem de associá-la a uma conta de armazenamento. A ligação da conta de armazenamento é mantida na definição da aplicação AzureWebJobsStorage.

Para maximizar o desempenho, utilize uma conta de armazenamento separada para cada aplicação de funções. Isto é particularmente importante quando tem Durable Functions ou funções acionadas pelo Hub de Eventos, que geram um elevado volume de transações de armazenamento. Quando a lógica da aplicação interage com o Armazenamento do Azure, diretamente (com o SDK de Armazenamento) ou através de um dos enlaces de armazenamento, deve utilizar uma conta de armazenamento dedicada. Por exemplo, se tiver uma função acionada pelo Hub de Eventos a escrever alguns dados no armazenamento de blobs, utilize duas contas de armazenamento : uma para a aplicação de funções e outra para os blobs que estão a ser armazenados pela função.

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

Funções numa aplicação de funções partilham recursos. Por exemplo, a memória é partilhada. Se estiver a utilizar uma aplicação de funções em produção, não adicione funções e recursos relacionados com o teste à mesma. Pode causar uma sobrecarga inesperada durante a execução do código de produção.

Tenha cuidado com o que carrega nas suas aplicações de funções de produção. A memória é média em cada função na aplicação.

Se tiver uma assemblagem partilhada referenciada em várias funções .NET, coloque-a numa pasta partilhada comum. Caso contrário, poderia implementar acidentalmente várias versões do mesmo binário que se comportam de forma diferente entre funções.

Não utilize o registo verboso no código de produção, o que tem um impacto negativo no desempenho.

Utilizar código assíncrono, mas evitar bloquear chamadas

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

Em C#, evite sempre referenciar a Result propriedade ou o método de chamada Wait numa Task instância. Esta abordagem pode levar ao esgotamento dos threads.

Dica

Se planear utilizar os enlaces HTTP ou WebHook, planeie evitar o esgotamento das portas que pode ser causado por instâncias impróprias de HttpClient. Para obter mais informações, veja Como gerir ligações no Funções do Azure.

Utilizar vários processos de trabalho

Por predefinição, qualquer instância de anfitrião para Funções utiliza um único processo de trabalho. Para melhorar o desempenho, especialmente com runtimes de thread único, como o Python, utilize o FUNCTIONS_WORKER_PROCESS_COUNT para aumentar o número de processos de trabalho por anfitrião (até 10). Funções do Azure, em seguida, tenta distribuir uniformemente invocações de funções simultâneas por estes trabalhos.

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.

Receber mensagens em lote sempre que possível

Alguns acionadores, como o Hub de Eventos, permitem receber um lote de mensagens numa única invocação. A criação de batches de mensagens tem um desempenho muito melhor. Pode configurar o tamanho máximo do lote no host.json ficheiro, conforme detalhado na documentação de referência host.json

Para as funções C#, pode alterar o tipo para uma matriz com um tipo de letra forte. Por exemplo, em vez da assinatura do EventData sensorEvent método, pode ser EventData[] sensorEvent. Para outros idiomas, terá de definir explicitamente a propriedade cardinalidade na sua function.json para many para ativar a criação de batches , conforme mostrado aqui.

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

O host.json ficheiro na aplicação de funções permite a configuração do runtime do anfitrião e dos comportamentos do acionador. Além dos comportamentos de criação de batches, pode gerir a simultaneidade de vários acionadores. Muitas vezes, ajustar os valores nestas opções pode ajudar cada instância a dimensionar adequadamente para as exigências das funções invocadas.

As definições no ficheiro host.json aplicam-se a todas as funções na aplicação, numa única instância da função. Por exemplo, se tivesse uma aplicação de funções com duas funções HTTP e maxConcurrentRequests pedidos definidos como 25, um pedido para qualquer acionador HTTP contaria para os 25 pedidos simultâneos partilhados. Quando essa aplicação de funções é dimensionada para 10 instâncias, as dez funções permitem efetivamente 250 pedidos simultâneos (10 instâncias * 25 pedidos simultâneos por instância).

Outras opções de configuração do anfitrião encontram-se no artigo de configuração host.json.

Passos seguintes

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