Este documento descreve as práticas recomendadas para conceber, implementar, testar e implementar funções do Cloud Run.
Exatidão
Esta secção descreve as práticas recomendadas gerais para conceber e implementar funções do Cloud Run.
Escreva funções idempotentes
As suas funções devem produzir o mesmo resultado, mesmo que sejam chamadas várias vezes. Isto permite-lhe repetir uma invocação se a invocação anterior falhar durante a execução do código. Para mais informações, consulte o artigo sobre como tentar novamente funções acionadas por eventos.
Certifique-se de que as funções HTTP enviam uma resposta HTTP
Se a sua função for acionada por HTTP, lembre-se de enviar uma resposta HTTP, como mostrado abaixo. Se não o fizer, a função é executada até atingir o limite de tempo. Se isto ocorrer, é-lhe cobrado o tempo limite total. Os limites de tempo também podem causar um comportamento imprevisível ou inícios a frio em invocações subsequentes, o que resulta num comportamento imprevisível ou latência adicional.
Node.js
Python
Ir
Java
C#
Ruby
PHP
Não iniciar atividades em segundo plano
A atividade em segundo plano é tudo o que acontece depois de a sua função ter terminado.
Uma invocação de função termina quando a função devolve ou sinaliza de outra forma a conclusão, como através da chamada do argumento callback
em funções baseadas em eventos do Node.js. Qualquer código executado após a terminação normal não pode aceder à CPU e não faz qualquer progresso.
Além disso, quando uma invocação subsequente é executada no mesmo ambiente,
a sua atividade em segundo plano é retomada, interferindo com a nova invocação. Isto pode
resultar num comportamento inesperado e em erros difíceis de diagnosticar. Aceder à rede após a terminação de uma função geralmente leva à reposição das ligações (código de erro ECONNRESET
).
A atividade em segundo plano pode ser frequentemente detetada nos registos de invocações individuais, encontrando tudo o que é registado após a linha que indica que a invocação terminou. Por vezes, a atividade em segundo plano pode estar mais oculta no código, especialmente quando estão presentes operações assíncronas, como callbacks ou temporizadores. Reveja o código para se certificar de que todas as operações assíncronas terminam antes de terminar a função.
Elimine sempre ficheiros temporários
O armazenamento em disco local no diretório temporário é um sistema de ficheiros na memória. Os ficheiros que escreve consomem a memória disponível para a sua função e, por vezes, persistem entre invocações. Se não eliminar explicitamente estes ficheiros, pode ocorrer um erro de falta de memória e um reinício a frio subsequente.
Pode ver a memória usada por uma função individual selecionando-a na lista de funções naGoogle Cloud consola e escolhendo o gráfico Utilização de memória.
Se precisar de acesso ao armazenamento a longo prazo, considere usar montagens de volumes do Cloud Run com o Cloud Storage ou volumes NFS.
Pode reduzir os requisitos de memória quando processa ficheiros maiores através do processamento em pipeline. Por exemplo, pode processar um ficheiro no Cloud Storage criando um fluxo de leitura, passando-o por um processo baseado em fluxo e escrevendo o fluxo de saída diretamente no Cloud Storage.
Functions Framework
Para garantir que as mesmas dependências são instaladas de forma consistente em todos os ambientes, recomendamos que inclua a biblioteca Functions Framework no seu gestor de pacotes e fixe a dependência a uma versão específica do Functions Framework.
Para o fazer, inclua a sua versão preferencial no ficheiro de bloqueio relevante (por exemplo, package-lock.json
para Node.js ou requirements.txt
para Python).
Se o Functions Framework não estiver explicitamente listado como uma dependência, é adicionado automaticamente durante o processo de compilação através da versão disponível mais recente.
Ferramentas
Esta secção fornece diretrizes sobre como usar ferramentas para implementar, testar e interagir com funções do Cloud Run.
Desenvolvimento local
A implementação de funções demora algum tempo, pelo que é frequentemente mais rápido testar o código da sua função localmente.
Relatório de erros
Em linguagens que usam o processamento de exceções, não lance exceções não capturadas, porque forçam inícios a frio em invocações futuras.
Não sair manualmente
A saída manual pode causar um comportamento inesperado. Em alternativa, use os seguintes idiomatismos específicos do idioma:
Node.js
Não use process.exit()
. As funções HTTP devem enviar uma resposta com res.status(200).send(message)
, e as funções acionadas por eventos terminam assim que são devolvidas (implícita ou explicitamente).
Python
Não use sys.exit()
. As funções HTTP devem devolver explicitamente uma resposta como uma string e as funções orientadas por eventos terminam assim que devolvem um valor (implícita ou explicitamente).
Ir
Não use os.Exit()
. As funções HTTP devem devolver explicitamente uma resposta como uma string e as funções orientadas por eventos terminam assim que devolvem um valor (implícita ou explicitamente).
Java
Não use System.exit()
. As funções HTTP devem enviar uma resposta com response.getWriter().write(message)
, e as funções acionadas por eventos terminam assim que são devolvidas (implícita ou explicitamente).
C#
Não use System.Environment.Exit()
. As funções HTTP devem enviar uma resposta com context.Response.WriteAsync(message)
, e as funções acionadas por eventos terminam assim que são devolvidas (implícita ou explicitamente).
Ruby
Não use exit()
nem abort()
. As funções HTTP devem devolver explicitamente uma resposta como uma string e as funções orientadas por eventos terminam assim que devolvem um valor (implícita ou explicitamente).
PHP
Não use exit()
nem die()
. As funções HTTP devem devolver explicitamente uma resposta como uma string e as funções orientadas por eventos terminam assim que devolvem um valor (implícita ou explicitamente).
Use o Sendgrid para enviar emails
As funções do Cloud Run não permitem ligações de saída na porta 25, pelo que não pode estabelecer ligações não seguras a um servidor SMTP. A forma recomendada de enviar emails é usar um serviço de terceiros, como o SendGrid. Pode encontrar outras opções para enviar emails no tutorial Enviar emails a partir de uma instância do Google Compute Engine.
Desempenho
Esta secção descreve as práticas recomendadas para otimizar o desempenho.
Evite a baixa concorrência
Como os inícios a frio são dispendiosos, a capacidade de reutilizar instâncias iniciadas recentemente durante um pico é uma excelente otimização para processar a carga. A limitação da simultaneidade limita a forma como as instâncias existentes podem ser usadas, o que gera mais inícios a frio.
Aumentar a simultaneidade ajuda a diferir vários pedidos por instância, o que facilita o processamento de picos de carga.Use as dependências de forma inteligente
Uma vez que as funções não têm estado, o ambiente de execução é frequentemente inicializado de raiz (durante o que é conhecido como um início a frio). Quando ocorre um arranque a frio, o contexto global da função é avaliado.
Se as suas funções importarem módulos, o tempo de carregamento desses módulos pode aumentar a latência de invocação durante um arranque a frio. Pode reduzir esta latência, bem como o tempo necessário para implementar a sua função, carregando as dependências corretamente e não carregando as dependências que a sua função não usa.
Use variáveis globais para reutilizar objetos em invocações futuras
Não existe garantia de que o estado de uma função do Cloud Run seja preservado para invocações futuras. No entanto, as funções do Cloud Run reciclam frequentemente o ambiente de execução de uma invocação anterior. Se declarar uma variável no âmbito global, o respetivo valor pode ser reutilizado em invocações subsequentes sem ter de ser recalculado.
Desta forma, pode colocar em cache objetos que podem ser dispendiosos de recriar em cada invocação de função. Mover esses objetos do corpo da função para o âmbito global pode resultar em melhorias significativas no desempenho. O exemplo seguinte cria um objeto pesado apenas uma vez por instância da função e partilha-o em todas as invocações da função que atingem a instância dada:
Node.js
Python
Ir
Java
C#
Ruby
PHP
É particularmente importante colocar em cache as ligações de rede, as referências de bibliotecas e os objetos de cliente da API no âmbito global. Consulte as práticas recomendadas de rede para ver exemplos.
Reduza os inícios a frio definindo um número mínimo de instâncias
Por predefinição, as funções do Cloud Run escalam o número de instâncias com base no número de pedidos recebidos. Pode alterar este comportamento predefinido definindo um número mínimo de instâncias que as funções do Cloud Run têm de manter prontas para atender pedidos. A definição de um número mínimo de instâncias reduz os inícios a frio da sua aplicação. Recomendamos que defina um número mínimo de instâncias e conclua a inicialização no momento do carregamento se a sua aplicação for sensível à latência.
Para saber como definir um número mínimo de instâncias, consulte o artigo Usar instâncias mínimas.
Notas sobre o início a frio e a inicialização
A inicialização global ocorre no momento do carregamento. Sem ele, o primeiro pedido teria de concluir a inicialização e carregar módulos, o que incorreria numa latência mais elevada.
No entanto, a inicialização global também tem um impacto nos inícios a frio. Para minimizar este impacto, inicialize apenas o que é necessário para o primeiro pedido, de modo a manter a latência do primeiro pedido o mais baixa possível.
Isto é especialmente importante se tiver configurado instâncias mínimas, conforme descrito acima, para uma função sensível à latência. Nesse cenário, a conclusão da inicialização no momento do carregamento e o armazenamento em cache de dados úteis garantem que o primeiro pedido não precisa de o fazer e é apresentado com baixa latência.
Se inicializar variáveis no âmbito global, consoante o idioma, os tempos de inicialização longos podem resultar em dois comportamentos: - Para algumas combinações de idiomas e bibliotecas assíncronas, a estrutura de funções pode ser executada de forma assíncrona e devolvida imediatamente, o que faz com que o código continue a ser executado em segundo plano, o que pode causar problemas, como não conseguir aceder à CPU. Para evitar esta situação, deve bloquear a inicialização do módulo, conforme descrito abaixo. Isto também garante que os pedidos não são publicados até a inicialização estar concluída. - Por outro lado, se a inicialização for síncrona, o longo tempo de inicialização vai causar inícios a frio mais longos, o que pode ser um problema, especialmente com funções de concorrência baixa durante picos de carga.
Exemplo de pré-aquecimento de uma biblioteca assíncrona do Node.js
O Node.js com o Firestore é um exemplo de biblioteca node.js assíncrona. Para tirar partido de min_instances, o código seguinte conclui o carregamento e a inicialização no momento do carregamento, bloqueando o carregamento do módulo.
É usado o TLA, o que significa que o ES6 é necessário, usando uma extensão .mjs
para o código node.js ou adicionando type: module
ao ficheiro package.json.
{ "main": "main.js", "type": "module", "dependencies": { "@google-cloud/firestore": "^7.10.0", "@google-cloud/functions-framework": "^3.4.5" } }
Node.js
import Firestore from '@google-cloud/firestore'; import * as functions from '@google-cloud/functions-framework'; const firestore = new Firestore({preferRest: true}); // Pre-warm firestore connection pool, and preload our global config // document in cache. In order to ensure no other request comes in, // block the module loading with a synchronous global request: const config = await firestore.collection('collection').doc('config').get(); functions.http('fetch', (req, res) => { // Do something with config and firestore client, which are now preloaded // and will execute at lower latency. });
Exemplos de inicialização global
Node.js
Python
Ir
Java
C#
Ruby
PHP
As funções PHP não podem preservar variáveis entre pedidos. O exemplo de âmbitos acima usa o carregamento diferido para colocar em cache os valores das variáveis globais num ficheiro.
Isto é particularmente importante se definir várias funções num único ficheiro e as diferentes funções usarem variáveis diferentes. A menos que use a inicialização preguiçosa, pode desperdiçar recursos em variáveis que são inicializadas, mas nunca usadas.
Recursos adicionais
Saiba mais sobre a otimização do desempenho no vídeo "Google Cloud Performance Atlas" (Google Cloud Performance Atlas) Tempo de arranque a frio das funções do Cloud Run.