O Cloud Functions é executado em um ambiente sem servidor totalmente gerenciado, em que o Google processa totalmente infraestrutura, sistemas operacionais e ambientes de execução para você. Cada função do Cloud é executada no próprio contexto de execução seguro e isolado, com dimensionamento automático e um ciclo de vida independente de outras funções.
Ambientes de execução
O Cloud Functions é compatível com ambientes de execução de várias linguagens. Você precisará do valor do ID do ambiente de execução se estiver implantando funções pela linha de comando, ou pelo Terraform.
Ambiente de execução | Imagem base | ID do ambiente de execução |
---|---|---|
Node.js 6 (desativado) | Debian 8 | nodejs6 |
Node.js 8 (obsoleto) | Ubuntu 18.04 | nodejs8 |
Node.js 10 | Ubuntu 18.04 | nodejs10 |
Node.js 12 | Ubuntu 18.04 | nodejs12 |
Node.js 14 | Ubuntu 18.04 | nodejs14 |
Python 3.7 | Ubuntu 18.04 | python37 |
Python 3.8 | Ubuntu 18.04 | python38 |
Go 1.11 (Obsoleto) | Ubuntu 18.04 | go111 |
Go 1.13 | Ubuntu 18.04 | go113 |
Java 11 | Ubuntu 18.04 | java11 |
.NET Core 3.1 | Ubuntu 18.04 | dotnet3 |
Ruby 2.6 | Ubuntu 18.04 | ruby26 |
Ruby 2.7 | Ubuntu 18.04 | ruby27 |
As atualizações dos ambientes de execução geralmente são feitas de maneira automática, a menos que seja notificado de outra forma. Todos os ambientes de execução recebem atualizações automáticas da versão da linguagem à medida que são disponibilizados para a comunidade de linguagem. Da mesma forma, o Cloud Functions pode aplicar atualizações a outros aspectos do ambiente de execução, como o sistema operacional ou pacotes incluídos. Essas atualizações ajudam a manter a segurança da função.
Funções sem estado
O Cloud Functions implementa o paradigma sem servidor, onde você executa seu código sem se preocupar com a infraestrutura subjacente, como servidores e máquinas virtuais. Para permitir que o Google faça o gerenciamento e escalonamento automáticos das funções, elas precisam ser sem estado. Uma invocação de função não precisa depender do estado na memória definido por uma invocação anterior. No entanto, o estado existente pode ser reutilizado para otimização de desempenho. Consulte a recomendação em Dicas e sugestões para mais detalhes.
Por exemplo, o valor do contador retornado pela função a seguir não corresponde à contagem de invocação total porque as invocações podem ser manipuladas por instâncias de função diferentes, que não compartilham variáveis globais, memória, sistemas de arquivos ou outro estado:
Node.js
Python
Go
Java
C#
Ruby
Se você precisa compartilhar estados em invocações de função, sua função deve usar um serviço como Datastore, Firestore ou Cloud Storage para manter os dados. Para uma lista completa de opção de armazenamento disponíveis, consulte Como escolher uma opção de armazenamento.
Escalonamento automático e simultaneidade
O Cloud Functions lida com as solicitações recebidas atribuindo-as a instâncias da sua função. Dependendo do volume de solicitações e do número de instâncias de função existentes, o Cloud Functions pode atribuir uma solicitação a uma instância existente ou criar uma nova.
Cada instância de função lida com uma solicitação simultânea por vez. Isso quer dizer que seu código está processando uma solicitação. Não há possibilidade de uma segunda solicitação ser encaminhada para a mesma instância. Portanto, a solicitação original pode usar a quantidade total de recursos (CPU e memória) que você solicitou.
Nos casos em que o volume de solicitações de entrada excede o número de instâncias existentes, o Cloud Functions pode iniciar várias novas instâncias para processar solicitações. Esse comportamento de escalonamento automático permite que o Cloud Functions lide com muitas solicitações em paralelo, cada uma usando uma instância diferente da sua função.
Como são processadas por instâncias de função diferentes, as solicitações simultâneas não compartilham variáveis nem memória local. Isso será abordado em detalhes posteriormente neste documento.
Como controlar o comportamento de escalonamento automático
O Cloud Functions permite definir um limite para o número total de instâncias de função que podem coexistir a qualquer momento. Em alguns casos, o escalonamento ilimitado não é desejável. Por exemplo, sua função pode depender de um recurso (como um banco de dados) que não pode ser escalonado no mesmo grau que o Cloud Functions. Um grande aumento no volume de solicitações pode fazer com que o Cloud Functions crie mais instâncias de função do que seu banco de dados pode tolerar.
Inicializações a frio
Uma nova instância de função é iniciada em dois casos:
Quando você implanta a função.
Quando uma nova instância de função é criada automaticamente para ser escalonada até a carga ou para substituir uma instância existente às vezes.
O início de uma nova instância de função envolve o carregamento do tempo de execução e do código. As solicitações que incluem inicialização de instância de função (inicializações a frio) podem ser mais lentas do que as solicitações que atingem instâncias de função existentes. Porém, se a função receber carga constante, o número de inicializações a frio normalmente será insignificante, a menos que a função falhe sempre e exija a reinicialização do ambiente de função. Consulte Erros para aprender como processar erros corretamente e evitar inicializações a frio.
Vida útil da instância de função
O ambiente que executa uma instância de função costuma ser resiliente e reutilizado por invocações de função subsequentes, a menos que o número de instâncias esteja sendo reduzido (por causa da falta de tráfego em andamento) ou a função falhe. Isso significa que quando uma execução de função termina, outra invocação pode ser manipulada pela mesma instância. Por isso, é recomendável armazenar em cache o estado entre as invocações em escopo global, quando possível. A função ainda deve estar preparada para funcionar sem esse cache disponível, porque não há garantia de que a próxima invocação alcançará a mesma instância de função (consulte Funções sem estado).
Escopo de função x escopo global
Uma única invocação de função resulta na execução apenas do corpo da função declarada como o ponto de entrada. O escopo global no arquivo de função, que conterá a definição de função, é executado em cada inicialização a frio, mas não se a instância já tiver sido inicializada.
Node.js
Python
Go
Java
É possível pressupor que o escopo global tenha sido executado exatamente uma vez antes da invocação do código de função em uma nova instância de função (e em cada criação subsequente de uma nova instância de função). No entanto, você não precisa depender do número total ou do tempo das execuções de escopo global, porque elas dependem do escalonamento automático gerenciado pelo Google.
Linha do tempo da execução da função
Uma função só tem acesso aos recursos solicitados (CPU e memória) durante a execução da função. Não é garantido que o código seja executado fora do período de execução. Ele também pode ser interrompido a qualquer momento. Portanto, sempre sinalize o fim da execução da função corretamente e evite executar qualquer código além dela. Consulte Funções HTTP e Funções em segundo plano para orientações.
Por exemplo, o código executado após o envio da resposta HTTP pode ser interrompido a qualquer momento:
Node.js
É importante considerar o cronograma de execução ao inicializar seu aplicativo. As tarefas em segundo plano não devem ser criadas no escopo global durante a inicialização, já que são executadas fora da duração de uma solicitação.
Garantias de execução
As funções normalmente são invocadas uma vez para cada evento recebido. No entanto, o Cloud Functions não garante uma única invocação em todos os casos por causa de diferenças em cenários de erro.
O número máximo ou mínimo de vezes em que a função será invocada em um único evento depende do tipo da função:
As funções HTTP são invocadas, no máximo, uma vez. Isso acontece por causa da natureza síncrona das chamadas HTTP, e isso significa que qualquer erro no processamento da invocação da função será retornado sem nova tentativa. O autor da chamada de uma função HTTP processará os erros e tentará novamente, se necessário.
As funções em segundo plano são invocadas pelo menos uma vez. Isso ocorre devido à natureza assíncrona de processar eventos, em que não há nenhum autor da chamada aguardando a resposta. O sistema pode, em raras circunstâncias, invocar uma função em segundo plano mais de uma vez para garantir a entrega do evento. Se uma invocação da função em segundo plano falhar com um erro, ela não é invocada de novo, a menos que a opção novas tentativas em caso de falha esteja ativada para essa função.
Para garantir que a função se comporte corretamente em tentativas de execução repetidas, você precisa torná-la idempotente implementando-a de maneira que um evento resulte nos resultados desejados (e efeitos colaterais), mesmo que ela seja entregue várias vezes. No caso de funções HTTP, isso também indicará o retorno do valor desejado mesmo se o autor da chamada tentar novamente chamadas para o endpoint da função HTTP. Consulte Como recuperar funções em segundo plano para mais informações sobre como tornar a função idempotente.
Erros
A maneira recomendada para uma função sinalizar um erro depende do tipo de função:
As funções HTTP retornarão códigos de status HTTP apropriados que denotarem um erro. Consulte Funções HTTP para exemplos.
As funções em segundo plano registrarão e retornarão uma mensagem de erro. Consulte Funções em segundo plano para exemplos.
Se um erro for retornado da maneira recomendada, a instância da função que retornou o erro será identificada como se comportando normalmente e poderá disponibilizar solicitações futuras, se necessário.
Caso seu código ou qualquer outro código que você chamar gerar uma exceção não capturada ou travar o processo atual, a instância da função poderá ser reiniciada antes de processar a próxima invocação. Isso pode levar a mais inicializações a frio, resultando em maior latência e, portanto, essa prática não é recomendada.
Consulte Como gerar relatórios de erros para mais discussões sobre como informar erros no Cloud Functions.
Tempo limite
O ambiente de execução da função é limitado pela duração do tempo limite, que é possível especificar na hora de implantação da função. Por padrão, uma função expira após um minuto, mas é possível estender esse período para até nove minutos.
Quando a execução da função excede o tempo limite, um status de erro é retornado imediatamente ao autor da chamada. Os recursos de CPU usados pela instância de função expirada são limitados e o processamento da solicitação pode ser pausado imediatamente. Trabalhos pausados podem ou não prosseguir com solicitações subsequentes, o que pode causar efeitos colaterais inesperados.
O snippet abaixo inclui o código programado para execução dois minutos após o início da execução da função. Se o tempo limite for definido como um minuto, esse código talvez nunca seja executado:
Node.js
Python
Go
Java
Em algumas circunstâncias, o código acima pode ser executado com êxito, mas de
maneira inesperada. Considere o cenário em que a função expira. A instância que está atendendo à solicitação está pausada (limitando a CPU). O trabalho pendente está pausado. Se uma solicitação subsequente for roteada para a mesma instância, o trabalho será retomado e Function running...
será emitido para os Registros.
Um sintoma comum desse comportamento é a aparência que o trabalho e os registros de uma solicitação estão "vazando" em uma solicitação subsequente. Como não é possível saber se o trabalho pausado será retomado, você não deve confiar nesse comportamento. Em vez disso, sua função deve evitar tempos limite usando uma combinação das seguintes técnicas:
- Defina um tempo limite maior do que o ambiente de execução esperado da função.
- Rastreie o tempo restante durante a execução e realize a limpeza/saída antecipadamente.
Para definir o tempo máximo de execução de uma função usando a ferramenta de linha de comando gcloud
,
use a sinalização --timeout
no momento da implantação:
gcloud functions deploy FUNCTION_NAME --timeout=TIMEOUT FLAGS...
No comando acima, FLAGS...
se refere a outras
opções passadas durante a implantação da função. Para obter uma referência completa para o comando deploy
, consulte gcloud functions deploy
.
Você também pode definir o tempo limite durante a criação da função no Console do Cloud da seguinte maneira:
Acesse a página Visão geral do Cloud Functions no Console do Cloud.
Clique em Criar função.
Preencha os campos obrigatórios da função.
Veja as configurações avançadas clicando em Mais.
Digite um valor no campo Tempo limite.
Memória
Para definir a memória de uma função usando a ferramenta de linha de comando gcloud
, use a
sinalização --memory
com o
número de megabytes (128
, 256
, 512
, 1024
, 2048
, 4096
). Por exemplo:
gcloud functions deploy FUNCTION_NAME --memory=MEMORY
Por padrão, a memória alocada para cada função é 256 MB.
Sistema de arquivos
O ambiente de execução da função contém um arquivo de função executável, além de arquivos e diretórios incluídos no pacote de funções implantadas, como dependências locais. Esses arquivos estão disponíveis em um diretório somente leitura, que pode ser determinado com base no local do arquivo de função. O diretório da função pode ser diferente do diretório de trabalho atual.
O exemplo a seguir lista arquivos localizados no diretório da função:
Node.js
Python
Go
Java
C#
Ruby
Também é possível carregar código de outros arquivos implantados com a função.
A única parte gravável do sistema de arquivos é o diretório /tmp
, que pode ser usado para armazenar arquivos temporários em uma instância da função. Esse é um ponto de montagem do disco local conhecido como volume "tmpfs", em que os dados gravados no volume são armazenados na memória. Isso consumirá recursos de memória provisionados para a função.
O restante do sistema de arquivos é somente leitura e acessível à função.
Rede
A função pode acessar a Internet pública usando bibliotecas padrão oferecidas pelo ambiente de execução ou por provedores de terceiros. Por exemplo, é possível chamar um ponto de extremidade HTTP conforme mostrado abaixo:
Node.js
Python
Go
Java
C#
Ruby
Tente reutilizar as conexões de rede entre as invocações de função, conforme descrito em Como otimizar a rede. No entanto, uma conexão que permanece sem uso por dois minutos pode ser fechada pelo sistema, e outras tentativas de usar uma conexão fechada resultam em um erro de "redefinição de conexão". O código precisa usar uma biblioteca que processe bem conexões fechadas ou processá-las explicitamente, caso esteja usando construtos de rede de baixo nível.
Várias funções
Cada função implantada permanece isolada de todas as outras funções, mesmo as implantadas pelo mesmo arquivo de origem. Em especial, elas não compartilham memória, variáveis globais, sistemas de arquivos ou outros estados.
Para compartilhar dados em funções implantadas, é possível usar serviços de armazenamento como Datastore, Firestore ou Cloud Storage. Como alternativa, é possível invocar uma função a partir de outra, usando os gatilhos apropriados. Por exemplo, faça uma solicitação HTTP para o endpoint de uma função HTTP ou publique uma mensagem em um tópico Pub/Sub para acionar uma função Pub/Sub.