Como são processados os pedidos

ID da região

O REGION_ID é um código abreviado que a Google atribui com base na região que seleciona quando cria a sua app. O código não corresponde a um país ou uma província, embora alguns IDs de regiões possam parecer semelhantes aos códigos de países e províncias usados frequentemente. Para apps criadas após fevereiro de 2020, REGION_ID.r está incluído nos URLs do App Engine. Para apps existentes criadas antes desta data, o ID da região é opcional no URL.

Saiba mais acerca dos IDs de regiões.

Este documento descreve como a sua aplicação do App Engine recebe pedidos e envia respostas.

Para mais detalhes, consulte a referência de cabeçalhos e respostas de pedidos.

Se a sua aplicação usar serviços, pode enviar pedidos para um serviço específico ou uma versão específica desse serviço. Para mais informações sobre a capacidade de resposta do serviço, consulte o artigo Como os pedidos são encaminhados.

Processar pedidos

A sua aplicação é responsável por iniciar um servidor Web e processar pedidos. Pode usar qualquer framework Web disponível para a sua linguagem de desenvolvimento.

O App Engine executa várias instâncias da sua aplicação e cada instância tem o seu próprio servidor Web para processar pedidos. Qualquer pedido pode ser encaminhado para qualquer instância, pelo que os pedidos consecutivos do mesmo utilizador não são necessariamente enviados para a mesma instância. Uma instância pode processar vários pedidos em simultâneo. O número de instâncias pode ser ajustado automaticamente à medida que o tráfego muda. Também pode alterar o número de pedidos simultâneos que uma instância pode processar definindo o elemento max_concurrent_requests no ficheiro app.yaml.

Quando o App Engine recebe um pedido Web para a sua aplicação, chama o script do controlador que corresponde ao URL, conforme descrito no ficheiro de configuração app.yaml da aplicação. O tempo de execução do Python 2.7 suporta a norma WSGI e a norma CGI para compatibilidade com versões anteriores. O WSGI é preferível e algumas funcionalidades do Python 2.7 não funcionam sem ele. A configuração dos controladores de scripts da sua aplicação determina se um pedido é processado através de WSGI ou CGI.

O seguinte script Python responde a um pedido com um cabeçalho HTTP e a mensagem Hello, World!.

import webapp2


class MainPage(webapp2.RequestHandler):
    def get(self):
        self.response.headers["Content-Type"] = "text/plain"
        self.response.write("Hello, World!")


app = webapp2.WSGIApplication(
    [
        ("/", MainPage),
    ],
    debug=True,
)

Para enviar vários pedidos a cada servidor Web em paralelo, marque a sua aplicação como segura para threads adicionando um threadsafe: true ao seu ficheiro app.yaml. Os pedidos simultâneos não estão disponíveis se algum controlador de scripts usar CGI.

Quotas e limites

O App Engine atribui automaticamente recursos à sua aplicação à medida que o tráfego aumenta. No entanto, isto está sujeito às seguintes restrições:

  • O App Engine reserva capacidade de escalabilidade automática para aplicações com baixa latência, em que a aplicação responde a pedidos em menos de um segundo.

  • As aplicações que estão fortemente limitadas pela CPU também podem incorrer em alguma latência adicional para partilhar recursos de forma eficiente com outras aplicações nos mesmos servidores. Os pedidos de ficheiros estáticos estão isentos destes limites de latência.

Cada pedido recebido para a aplicação conta para o limite de pedidos. Os dados enviados em resposta a um pedido contam para o limite de largura de banda de saída (faturável).

Os pedidos HTTP e HTTPS (seguros) são contabilizados para os limites de pedidos, largura de banda de entrada (faturável) e largura de banda de saída (faturável). A Google Cloud página de detalhes da quota da consola também comunica pedidos seguros, largura de banda de entrada segura e largura de banda de saída segura como valores separados para fins informativos. Apenas os pedidos HTTPS contam para estes valores. Para mais informações, consulte a página Quotas.

Os seguintes limites aplicam-se especificamente à utilização de controladores de pedidos:

Limite Montante
Tamanho do pedido 32 megabytes
Tamanho da resposta 32 megabytes
Tempo limite de pedido Depende do tipo de dimensionamento que a sua app usa
Número total máximo de ficheiros (ficheiros de apps e ficheiros estáticos) 10 000 no total
1000 por diretório
Tamanho máximo de um ficheiro de aplicação 32 megabytes
Tamanho máximo de um ficheiro estático 32 megabytes
Tamanho total máximo de todos os ficheiros estáticos e de aplicações O primeiro gigabyte é gratuito
0,026$ por gigabyte por mês após o primeiro gigabyte
Tempo limite de pedido pendente 10 segundos
Tamanho máximo de um único campo de cabeçalho de pedido 8 kilobytes para tempos de execução de segunda geração no ambiente padrão. Os pedidos a estes tempos de execução com campos de cabeçalho que excedam 8 kilobytes devolvem erros HTTP 400.

Limites aos pedidos

Todos os pedidos HTTP/2 são traduzidos em pedidos HTTP/1.1 quando são encaminhados para o servidor de aplicações.

Limites de respostas

  • As respostas dinâmicas estão limitadas a 32 MB. Se um controlador de scripts gerar uma resposta superior a este limite, o servidor envia uma resposta vazia com um código de estado 500 Internal Server Error. Esta limitação não se aplica a respostas que fornecem dados do Blobstore antigo ou do Cloud Storage.

  • O limite do cabeçalho de resposta é de 8 KB para os tempos de execução de segunda geração. Os cabeçalhos de resposta que excedam este limite devolvem erros HTTP 502, com registos a apresentar upstream sent too big header while reading response header from upstream.

Cabeçalhos do pedido

Um pedido HTTP de entrada inclui os cabeçalhos HTTP enviados pelo cliente. Por motivos de segurança, alguns cabeçalhos são limpos ou alterados por proxies intermédios antes de chegarem à aplicação.

Para mais informações, consulte a referência de cabeçalhos de pedidos.

Processar limites de tempo de pedidos

O App Engine está otimizado para aplicações com pedidos de curta duração, normalmente, aqueles que demoram algumas centenas de milissegundos. Uma app eficiente responde rapidamente à maioria dos pedidos. Uma app que não o faça não é bem dimensionada com a infraestrutura do App Engine. Para garantir este nível de desempenho, existe um limite de tempo de pedido imposto pelo sistema ao qual todas as apps têm de responder.

Se a sua app exceder este prazo, o App Engine interrompe o controlador de pedidos. O ambiente de tempo de execução do Python consegue isto ao gerar uma exceção DeadlineExceededError de google.appengine.runtime. Se o controlador de pedidos não detetar esta exceção, tal como acontece com todas as exceções não detetadas, o ambiente de tempo de execução devolve um erro de servidor HTTP 500 ao cliente.

O controlador de pedidos pode captar este erro para personalizar a resposta. O ambiente de tempo de execução dá ao controlador de pedidos um pouco mais de tempo (menos de um segundo) após gerar a exceção para preparar uma resposta personalizada.

class TimerHandler(webapp2.RequestHandler):
    def get(self):
        from google.appengine.runtime import DeadlineExceededError

        try:
            time.sleep(70)
            self.response.write("Completed.")
        except DeadlineExceededError:
            self.response.clear()
            self.response.set_status(500)
            self.response.out.write("The request did not complete in time.")

Se o controlador não tiver devolvido uma resposta ou gerado uma exceção até ao prazo de 2 segundos, o controlador é parado e é devolvida uma resposta de erro predefinida.

Responses

O App Engine chama o script do controlador com um Request e aguarda que o script seja devolvido. Todos os dados escritos no fluxo de saída padrão são enviados como a resposta HTTP.

Existem limites de tamanho que se aplicam à resposta que gera, e a resposta pode ser modificada antes de ser devolvida ao cliente.

Para mais informações, consulte a referência de respostas de pedidos.

Respostas graduais

O App Engine não suporta respostas de streaming em que os dados são enviados em blocos incrementais para o cliente enquanto um pedido está a ser processado. Todos os dados do seu código são recolhidos conforme descrito acima e enviados como uma única resposta HTTP.

Compressão de respostas

O App Engine faz o seu melhor para fornecer conteúdo comprimido (com gzip) a clientes que o suportam. Para determinar se o conteúdo deve ser comprimido, o App Engine faz o seguinte quando recebe um pedido:

  1. Confirma se o cliente pode receber respostas comprimidas de forma fiável através da visualização dos cabeçalhos Accept-Encoding e User-Agent no pedido. Esta abordagem evita alguns erros conhecidos com conteúdo comprimido com gzip em navegadores populares.

  2. Confirma se a compressão do conteúdo é adequada através da visualização do cabeçalho Content-Type que configurou para o processador de respostas. Em geral, a compressão é adequada para tipos de conteúdo baseados em texto e não para tipos de conteúdo binários.

Tenha em conta o seguinte:

  • Um cliente pode forçar a compressão de tipos de conteúdo baseados em texto definindo os cabeçalhos de pedido Accept-Encoding e User-Agent como gzip.

  • Se um pedido não especificar gzip no cabeçalho Accept-Encoding, o App Engine não comprime os dados de resposta.

  • O front-end da Google armazena em cache as respostas dos controladores de ficheiros estáticos e de diretórios do App Engine. Consoante vários fatores, como o tipo de dados de resposta que é colocado em cache primeiro, os cabeçalhos Vary que especificou na resposta e os cabeçalhos incluídos no pedido, um cliente pode pedir dados comprimidos, mas receber dados não comprimidos e vice-versa. Para mais informações, consulte o artigo Colocação em cache de respostas.

Colocação em cache de respostas

O front-end da Google e, potencialmente, o navegador do utilizador e outros servidores proxy de colocação em cache intermédios, vão colocar em cache as respostas da sua app, conforme indicado nos cabeçalhos de colocação em cache padrão que especificar na resposta. Pode especificar estes cabeçalhos de resposta através da sua framework, diretamente no seu código ou através dos processadores de ficheiros estáticos e diretórios do App Engine.

No frontend da Google, a chave da cache é o URL completo do pedido.

Colocar conteúdo estático em cache

Para garantir que os clientes recebem sempre conteúdo estático atualizado assim que é publicado, recomendamos que publique conteúdo estático a partir de diretórios com controlo de versões, como css/v1/styles.css. O front-end da Google não valida a cache (verifica se existe conteúdo atualizado) até a cache expirar. Mesmo após o cache expirar, o cache não é atualizado até que o conteúdo no URL do pedido seja alterado.

Os seguintes cabeçalhos de resposta que pode definir em app.yaml influenciam como e quando o front-end da Google coloca conteúdo em cache:

  • Cache-Control deve ser definido como public para que o front-end da Google coloque em cache o conteúdo. O conteúdo também pode ser colocado em cache pelo front-end da Google, a menos que especifique uma diretiva Cache-Control private ou no-store. Se não definir este cabeçalho em app.yaml, o App Engine adiciona-o automaticamente a todas as respostas processadas por um ficheiro estático ou um controlador de diretório. Para mais informações, consulte o artigo Cabeçalhos adicionados ou substituídos.

  • Vary: Para permitir que a cache devolva respostas diferentes para um URL com base nos cabeçalhos enviados no pedido, defina um ou mais dos seguintes valores no cabeçalho de resposta Vary: Accept, Accept-Encoding, Origin ou X-Origin

    Devido ao potencial de elevada cardinalidade, os dados não são colocados em cache para outros valores de Vary.

    Por exemplo:

    1. Especifique o seguinte cabeçalho da resposta:

      Vary: Accept-Encoding

    2. A sua app recebe um pedido que contém o cabeçalho Accept-Encoding: gzip. O App Engine devolve uma resposta comprimida e o Google Frontend armazena em cache a versão comprimida com gzip dos dados de resposta. Todos os pedidos subsequentes para este URL que contenham o cabeçalho Accept-Encoding: gzip vão receber os dados comprimidos com gzip da cache até que a cache seja invalidada (devido a alterações no conteúdo após a expiração da cache).

    3. A sua app recebe um pedido que não contém o cabeçalho Accept-Encoding. O App Engine devolve uma resposta não comprimida e o Google Frontend armazena em cache a versão não comprimida dos dados de resposta. Todos os pedidos subsequentes para este URL que não contenham o cabeçalho Accept-Encoding vão receber os dados comprimidos da cache até que a cache seja invalidada.

    Se não especificar um cabeçalho de resposta Vary, o front-end da Google cria uma única entrada de cache para o URL e usa-a para todos os pedidos, independentemente dos cabeçalhos no pedido. Por exemplo:

    1. Não especifica o cabeçalho da resposta Vary: Accept-Encoding.
    2. Um pedido contém o cabeçalho Accept-Encoding: gzip e a versão comprimida com gzip dos dados de resposta é colocada em cache.
    3. Um segundo pedido não contém o cabeçalho Accept-Encoding: gzip. No entanto, uma vez que a cache contém uma versão comprimida com gzip dos dados de resposta, a resposta é comprimida com gzip, mesmo que o cliente tenha pedido dados não comprimidos.

Os cabeçalhos no pedido também influenciam o armazenamento em cache:

  • Se o pedido contiver um cabeçalho Authorization, o conteúdo não é colocado em cache pelo front-end da Google.

Expiração da cache

Por predefinição, os cabeçalhos de colocação em cache que os controladores de ficheiros estáticos e de diretórios do App Engine adicionam às respostas indicam aos clientes e aos proxies Web, como o frontend da Google, que a cache expira após 10 minutos.

Depois de um ficheiro ser transmitido com um determinado tempo de expiração, geralmente, não existe forma de o limpar das caches de proxy Web, mesmo que o utilizador limpe a sua própria cache do navegador. A nova implementação de uma nova versão da app não repõe nenhuma cache. Por conseguinte, se alguma vez planear modificar um ficheiro estático, este deve ter um prazo de validade curto (inferior a uma hora). Na maioria dos casos, o tempo de expiração predefinido de 10 minutos é adequado.

Pode alterar a expiração predefinida de todos os gestores de ficheiros e diretórios estáticos especificando o elemento default_expiration no ficheiro app.yaml. Para definir horas de validade específicas para controladores individuais, especifique o elemento expiration no elemento do controlador no seu ficheiro app.yaml.

O valor especificado no tempo dos elementos de expiração é usado para definir os cabeçalhos de resposta HTTP Cache-Control e Expires.

Colocação em cache de apps

O ambiente de execução do Python armazena em cache os módulos importados entre pedidos num único servidor Web, de forma semelhante ao modo como uma aplicação Python autónoma carrega um módulo apenas uma vez, mesmo que o módulo seja importado por vários ficheiros. Uma vez que os controladores WSGI são módulos, são colocados em cache entre pedidos. Os scripts de processador de CGI só são colocados em cache se fornecerem uma rotina main(); caso contrário, o script de processador de CGI é carregado para cada pedido.

O armazenamento em cache de apps oferece uma vantagem significativa no tempo de resposta. Recomendamos que todos os scripts de processamento de CGI usem uma rotina main(), conforme descrito abaixo.

As importações são colocadas em cache

Por questões de eficiência, o servidor Web mantém os módulos importados na memória e não os volta a carregar nem a reavaliar em pedidos subsequentes à mesma aplicação no mesmo servidor. A maioria dos módulos não inicializa dados globais nem tem outros efeitos secundários quando são importados, pelo que a colocação em cache dos mesmos não altera o comportamento da aplicação.

Se a sua aplicação importar um módulo que dependa do módulo que está a ser avaliado para cada pedido, a aplicação tem de ter em conta este comportamento de colocação em cache.

Colocação em cache de controladores CGI

Pode indicar ao App Engine para colocar em cache o próprio script do controlador CGI, além dos módulos importados. Se o script do controlador definir uma função denominada main(), o script e o respetivo ambiente global são colocados em cache como um módulo importado. O primeiro pedido do script num determinado servidor Web avalia o script normalmente. Para pedidos subsequentes, o App Engine chama a função main() no ambiente em cache.

Para colocar em cache um script de controlador, o App Engine tem de conseguir chamar main() sem argumentos. Se o script do controlador não definir uma função main() ou a função main() exigir argumentos (que não têm predefinições), o App Engine carrega e avalia todo o script para cada pedido.

Manter o código Python analisado na memória poupa tempo e permite respostas mais rápidas. A colocação em cache do ambiente global também tem outras utilizações potenciais:

  • Expressões regulares compiladas. Todas as expressões regulares são analisadas e armazenadas num formato compilado. Pode armazenar expressões regulares compiladas em variáveis globais e, em seguida, usar a colocação em cache da app para reutilizar os objetos compilados entre pedidos.

  • Objetos GqlQuery. A string de consulta GQL é analisada quando o objeto GqlQuery é criado. A reutilização de um objeto GqlQuery com a associação de parâmetros e o método bind() é mais rápida do que a reconstrução do objeto de cada vez. Pode armazenar um objeto GqlQuery com a associação de parâmetros para os valores numa variável global e, em seguida, reutilizá-lo associando novos valores de parâmetros para cada pedido.

  • Ficheiros de configuração e de dados. Se a sua aplicação carregar e analisar dados de configuração a partir de um ficheiro, pode reter os dados analisados na memória para evitar ter de recarregar o ficheiro com cada pedido.

O script do controlador deve chamar main() quando for importado. O App Engine espera que a importação do script chame main(), pelo que o App Engine não o chama quando carrega o controlador de pedidos pela primeira vez num servidor.

O armazenamento em cache de apps com o main() oferece uma melhoria significativa no tempo de resposta do controlador de CGI. Recomendamo-lo para todas as aplicações que usam CGI.

Registo

O servidor Web do App Engine captura tudo o que o script do controlador escreve no fluxo de saída padrão para a resposta ao pedido Web. Também captura tudo o que o script do controlador escreve no fluxo de erros padrão e armazena-o como dados de registo. A cada pedido é atribuído um request_id, um identificador exclusivo a nível global com base na hora de início do pedido. Os dados de registo da sua aplicação podem ser vistos na Google Cloud consola Google Cloud através do Cloud Logging.

O ambiente de execução do Python do App Engine inclui compatibilidade especial com o módulo de registo da biblioteca padrão do Python para compreender conceitos de registo, como níveis de registo ("debug", "info", "warning", "error", "critical").

import logging

import webapp2


class MainPage(webapp2.RequestHandler):
    def get(self):
        logging.debug("This is a debug message")
        logging.info("This is an info message")
        logging.warning("This is a warning message")
        logging.error("This is an error message")
        logging.critical("This is a critical message")

        try:
            raise ValueError("This is a sample value error.")
        except ValueError:
            logging.exception("A example exception log.")

        self.response.out.write("Logging example.")


app = webapp2.WSGIApplication([("/", MainPage)], debug=True)

O ambiente

O ambiente de execução define automaticamente várias variáveis de ambiente. Pode definir mais em app.yaml. Das variáveis definidas automaticamente, algumas são especiais para o App Engine, enquanto outras fazem parte das normas WSGI ou CGI. O código Python pode aceder a estas variáveis através do dicionário os.environ.

As seguintes variáveis de ambiente são específicas do App Engine:

  • CURRENT_VERSION_ID: a versão principal e secundária da aplicação em execução atualmente, no formato "X.Y". O número da versão principal ("X") é especificado no ficheiro app.yaml da app. O número da versão secundária ("Y") é definido automaticamente quando cada versão da app é carregada para o App Engine. No servidor Web de desenvolvimento, a versão secundária é sempre "1".

  • AUTH_DOMAIN: o domínio usado para autenticar utilizadores com a API Users. As apps alojadas em appspot.com têm um AUTH_DOMAIN de gmail.com e aceitam qualquer conta Google. As apps alojadas num domínio personalizado têm um AUTH_DOMAIN igual ao domínio personalizado.

  • INSTANCE_ID: contém o ID da instância de front-end que processa um pedido. O ID é uma string hexadecimal (por exemplo, 00c61b117c7f7fd0ce9e1325a04b8f0df30deaaf). Um administrador com sessão iniciada pode usar o ID num URL: https://INSTANCE_ID-dot-VERSION_ID-dot-SERVICE_ID-dot-PROJECT_ID.REGION_ID.r.appspot.com. O pedido é encaminhado para essa instância de front-end específica. Se a instância não conseguir processar o pedido, devolve um erro 503 imediato.

As seguintes variáveis de ambiente fazem parte das normas WSGI e CGI, com um comportamento especial no App Engine:

  • SERVER_SOFTWARE: no servidor Web de desenvolvimento, este valor é "Development/X.Y", em que "X.Y" é a versão do tempo de execução. Quando executado no App Engine, este valor é "Google App Engine/X.Y.Z".

As variáveis de ambiente adicionais são definidas de acordo com a norma WSGI ou CGI. Para mais informações sobre estas variáveis, consulte a norma WSGI ou a norma CGI, conforme adequado.

Também pode definir variáveis de ambiente no app.yaml ficheiro:

env_variables:
  DJANGO_SETTINGS_MODULE: 'myapp.settings'

O controlador de pedidos webapp2 seguinte apresenta todas as variáveis de ambiente visíveis para a aplicação no navegador:

class PrintEnvironmentHandler(webapp2.RequestHandler):
    def get(self):
        self.response.headers["Content-Type"] = "text/plain"
        for key, value in os.environ.iteritems():
            self.response.out.write("{} = {}\n".format(key, value))

IDs de pedidos

No momento do pedido, pode guardar o ID do pedido, que é exclusivo desse pedido. O ID do pedido pode ser usado mais tarde para procurar os registos desse pedido no Cloud Logging.

O seguinte código de exemplo mostra como obter o ID do pedido no contexto de um pedido:

class RequestIdHandler(webapp2.RequestHandler):
    def get(self):
        self.response.headers["Content-Type"] = "text/plain"
        request_id = os.environ.get("REQUEST_LOG_ID")
        self.response.write("REQUEST_LOG_ID={}".format(request_id))

Forçar ligações HTTPS

Por motivos de segurança, todas as aplicações devem incentivar os clientes a estabelecer ligação através de https. Para instruir o navegador a preferir https em vez de http para uma determinada página ou domínio inteiro, defina o cabeçalho Strict-Transport-Security nas suas respostas. Por exemplo:

Strict-Transport-Security: max-age=31536000; includeSubDomains
Para definir este cabeçalho para qualquer conteúdo estático publicado pela sua app, adicione o cabeçalho aos controladores de ficheiros e diretórios estáticos da app.

Para definir este cabeçalho para respostas geradas a partir do seu código, use a biblioteca flask-talisman.

Processamento de trabalho assíncrono em segundo plano

O trabalho em segundo plano é qualquer trabalho que a sua app realiza para um pedido depois de ter enviado a resposta HTTP. Evite realizar trabalho em segundo plano na sua app e reveja o código para se certificar de que todas as operações assíncronas terminam antes de enviar a resposta.

Para tarefas de longa duração, recomendamos a utilização do Cloud Tasks. Com o Cloud Tasks, os pedidos HTTP são de longa duração e devolvem uma resposta apenas após a conclusão de qualquer trabalho assíncrono.